summaryrefslogtreecommitdiff
path: root/lib/stdlib
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib')
-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
111 files changed, 9503 insertions, 2404 deletions
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"),