aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib')
-rw-r--r--lib/stdlib/doc/src/Makefile11
-rw-r--r--lib/stdlib/doc/src/beam_lib.xml8
-rw-r--r--lib/stdlib/doc/src/binary.xml729
-rw-r--r--lib/stdlib/doc/src/dets.xml8
-rw-r--r--lib/stdlib/doc/src/erl_id_trans.xml5
-rw-r--r--lib/stdlib/doc/src/erl_lint.xml4
-rw-r--r--lib/stdlib/doc/src/erl_parse.xml2
-rw-r--r--lib/stdlib/doc/src/erl_scan.xml19
-rw-r--r--lib/stdlib/doc/src/ets.xml186
-rw-r--r--lib/stdlib/doc/src/filelib.xml35
-rw-r--r--lib/stdlib/doc/src/filename.xml15
-rw-r--r--lib/stdlib/doc/src/gen_event.xml64
-rw-r--r--lib/stdlib/doc/src/gen_fsm.xml64
-rw-r--r--lib/stdlib/doc/src/gen_server.xml61
-rw-r--r--lib/stdlib/doc/src/io_protocol.xml6
-rw-r--r--lib/stdlib/doc/src/lists.xml14
-rw-r--r--lib/stdlib/doc/src/ms_transform.xml8
-rw-r--r--lib/stdlib/doc/src/notes.xml535
-rw-r--r--lib/stdlib/doc/src/re.xml143
-rw-r--r--lib/stdlib/doc/src/ref_man.xml7
-rw-r--r--lib/stdlib/doc/src/sofs.xml2
-rw-r--r--lib/stdlib/doc/src/supervisor.xml2
-rw-r--r--lib/stdlib/doc/src/sys.xml2
-rw-r--r--lib/stdlib/doc/src/timer.xml25
-rw-r--r--lib/stdlib/doc/src/unicode_usage.xml48
-rw-r--r--lib/stdlib/doc/src/zip.xml96
-rw-r--r--lib/stdlib/src/Makefile1
-rw-r--r--lib/stdlib/src/beam_lib.erl42
-rw-r--r--lib/stdlib/src/binary.erl177
-rw-r--r--lib/stdlib/src/c.erl141
-rw-r--r--lib/stdlib/src/dets.erl122
-rw-r--r--lib/stdlib/src/dets.hrl4
-rw-r--r--lib/stdlib/src/dets_sup.erl17
-rw-r--r--lib/stdlib/src/dets_v8.erl2
-rw-r--r--lib/stdlib/src/dets_v9.erl5
-rw-r--r--lib/stdlib/src/digraph.erl12
-rw-r--r--lib/stdlib/src/edlin.erl5
-rw-r--r--lib/stdlib/src/epp.erl143
-rw-r--r--lib/stdlib/src/erl_compile.erl12
-rw-r--r--lib/stdlib/src/erl_expand_records.erl151
-rw-r--r--lib/stdlib/src/erl_internal.erl159
-rw-r--r--lib/stdlib/src/erl_lint.erl512
-rw-r--r--lib/stdlib/src/erl_parse.yrl192
-rw-r--r--lib/stdlib/src/erl_pp.erl52
-rw-r--r--lib/stdlib/src/erl_scan.erl134
-rw-r--r--lib/stdlib/src/escript.erl487
-rw-r--r--lib/stdlib/src/ets.erl164
-rw-r--r--lib/stdlib/src/eval_bits.erl4
-rw-r--r--lib/stdlib/src/file_sorter.erl11
-rw-r--r--lib/stdlib/src/filelib.erl94
-rw-r--r--lib/stdlib/src/filename.erl436
-rw-r--r--lib/stdlib/src/gb_sets.erl2
-rw-r--r--lib/stdlib/src/gen.erl45
-rw-r--r--lib/stdlib/src/gen_event.erl42
-rw-r--r--lib/stdlib/src/gen_fsm.erl58
-rw-r--r--lib/stdlib/src/gen_server.erl62
-rw-r--r--lib/stdlib/src/io.erl11
-rw-r--r--lib/stdlib/src/io_lib.erl2
-rw-r--r--lib/stdlib/src/io_lib_fread.erl12
-rw-r--r--lib/stdlib/src/lists.erl92
-rw-r--r--lib/stdlib/src/ms_transform.erl193
-rw-r--r--lib/stdlib/src/orddict.erl2
-rw-r--r--lib/stdlib/src/ordsets.erl18
-rw-r--r--lib/stdlib/src/otp_internal.erl152
-rw-r--r--lib/stdlib/src/proc_lib.erl12
-rw-r--r--lib/stdlib/src/proplists.erl12
-rw-r--r--lib/stdlib/src/qlc.erl2
-rw-r--r--lib/stdlib/src/stdlib.app.src11
-rw-r--r--lib/stdlib/src/string.erl2
-rw-r--r--lib/stdlib/src/supervisor.erl199
-rw-r--r--lib/stdlib/src/timer.erl82
-rw-r--r--lib/stdlib/src/unicode.erl11
-rw-r--r--lib/stdlib/src/zip.erl90
-rw-r--r--lib/stdlib/test/Makefile2
-rw-r--r--lib/stdlib/test/binary_module_SUITE.erl1323
-rw-r--r--lib/stdlib/test/binref.erl588
-rw-r--r--lib/stdlib/test/dets_SUITE.erl145
-rw-r--r--lib/stdlib/test/dummy1_h.erl15
-rw-r--r--lib/stdlib/test/epp_SUITE.erl176
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl235
-rw-r--r--lib/stdlib/test/erl_pp_SUITE.erl216
-rw-r--r--lib/stdlib/test/erl_scan_SUITE.erl213
-rw-r--r--lib/stdlib/test/escript_SUITE.erl419
-rwxr-xr-xlib/stdlib/test/escript_SUITE_data/arg_overflow5
-rwxr-xr-xlib/stdlib/test/escript_SUITE_data/linebuf_overflow5
-rw-r--r--lib/stdlib/test/ets_SUITE.erl969
-rw-r--r--lib/stdlib/test/filelib_SUITE.erl7
-rw-r--r--lib/stdlib/test/filename_SUITE.erl315
-rw-r--r--lib/stdlib/test/gen_event_SUITE.erl59
-rw-r--r--lib/stdlib/test/gen_fsm_SUITE.erl67
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl115
-rw-r--r--lib/stdlib/test/io_proto_SUITE.erl1
-rw-r--r--lib/stdlib/test/ms_transform_SUITE.erl101
-rw-r--r--lib/stdlib/test/qlc_SUITE.erl15
-rw-r--r--lib/stdlib/test/re_SUITE.erl26
-rw-r--r--lib/stdlib/test/string_SUITE.erl5
-rw-r--r--lib/stdlib/test/timer_simple_SUITE.erl18
-rw-r--r--lib/stdlib/test/zip_SUITE.erl78
-rw-r--r--lib/stdlib/vsn.mk3
99 files changed, 9165 insertions, 2246 deletions
diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile
index 13b9b2ff18..b558697d63 100644
--- a/lib/stdlib/doc/src/Makefile
+++ b/lib/stdlib/doc/src/Makefile
@@ -1,19 +1,19 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
# compliance with the License. You should have received a copy of the
# Erlang Public License along with this software. If not, it can be
# retrieved online at http://www.erlang.org/.
-#
+#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and limitations
# under the License.
-#
+#
# %CopyrightEnd%
#
include $(ERL_TOP)/make/target.mk
@@ -40,6 +40,7 @@ XML_REF3_FILES = \
array.xml \
base64.xml \
beam_lib.xml \
+ binary.xml \
c.xml \
calendar.xml \
dets.xml \
diff --git a/lib/stdlib/doc/src/beam_lib.xml b/lib/stdlib/doc/src/beam_lib.xml
index b9286f1402..adc411e272 100644
--- a/lib/stdlib/doc/src/beam_lib.xml
+++ b/lib/stdlib/doc/src/beam_lib.xml
@@ -341,15 +341,17 @@ chunkref() = chunkname() | chunkid()</code>
<v>Beam1 = Beam2 = beam()</v>
<v>Reason = {modules_different, Module1, Module2}</v>
<v>&nbsp;&nbsp;| {chunks_different, ChunkId}</v>
+ <v>&nbsp;&nbsp;| different_chunks</v>
<v>&nbsp;&nbsp;| Reason1 -- see info/1</v>
<v>&nbsp;Module1 = Module2 = atom()</v>
<v>&nbsp;ChunkId = chunkid()</v>
</type>
<desc>
<p>Compares the contents of two BEAM files. If the module names
- are the same, and the chunks with the identifiers
- <c>"Code"</c>, <c>"ExpT"</c>, <c>"ImpT"</c>, <c>"StrT"</c>,
- and <c>"Atom"</c> have the same contents in both files,
+ are the same, and all chunks except for the <c>"CInf"</c> chunk
+ (the chunk containing the compilation information which is
+ returned by <c>Module:module_info(compile)</c>)
+ have the same contents in both files,
<c>ok</c> is returned. Otherwise an error message is returned.</p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/binary.xml b/lib/stdlib/doc/src/binary.xml
new file mode 100644
index 0000000000..c5eb81a86a
--- /dev/null
+++ b/lib/stdlib/doc/src/binary.xml
@@ -0,0 +1,729 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2009</year>
+ <year>2010</year>
+ <holder>Ericsson AB, All Rights Reserved</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved on line at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>binary</title>
+ <prepared>Patrik Nyblom</prepared>
+ <responsible>Kenneth Lundin</responsible>
+ <docno>1</docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2010-05-05</date>
+ <rev>A</rev>
+ <file>binary.xml</file>
+ </header>
+ <module>binary</module>
+ <modulesummary>Library for handling binary data</modulesummary>
+ <description>
+
+ <p>This module contains functions for manipulating byte-oriented
+ binaries. Although the majority of functions could be implemented
+ using bit-syntax, the functions in this library are highly
+ optimized and are expected to either execute faster or consume
+ less memory (or both) than a counterpart written in pure Erlang.</p>
+
+ <p>The module is implemented according to the EEP (Erlang Enhancement Proposal) 31.</p>
+
+ <note>
+ <p>
+ The library handles byte-oriented data. Bitstrings that are not
+ binaries (does not contain whole octets of bits) will result in a <c>badarg</c>
+ exception being thrown from any of the functions in this
+ module.
+ </p>
+ </note>
+
+
+ </description>
+ <section>
+ <title>DATA TYPES</title>
+ <code type="none">
+ cp()
+ - Opaque data-type representing a compiled search-pattern. Guaranteed to be a tuple()
+ to allow programs to distinguish it from non precompiled search patterns.
+ </code>
+ <code type="none">
+ part() = {Start,Length}
+ Start = int()
+ Length = int()
+ - A representaion of a part (or range) in a binary. Start is a
+ zero-based offset into a binary() and Length is the length of
+ that part. As input to functions in this module, a reverse
+ part specification is allowed, constructed with a negative
+ Length, so that the part of the binary begins at Start +
+ Length and is -Length long. This is useful for referencing the
+ last N bytes of a binary as {size(Binary), -N}. The functions
+ in this module always return part()'s with positive Length.
+ </code>
+ </section>
+ <funcs>
+ <func>
+ <name>at(Subject, Pos) -> int()</name>
+ <fsummary>Returns the byte at a specific position in a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pos = int() >= 0</v>
+ </type>
+ <desc>
+
+ <p>Returns the byte at position <c>Pos</c> (zero-based) in the binary
+ <c>Subject</c> as an integer. If <c>Pos</c> &gt;= <c>byte_size(Subject)</c>,
+ a <c>badarg</c>
+ exception is raised.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>bin_to_list(Subject) -> list()</name>
+ <fsummary>Convert a binary to a list of integers</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ </type>
+ <desc>
+ <p>The same as <c>bin_to_list(Subject,{0,byte_size(Subject)})</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>bin_to_list(Subject, PosLen) -> list()</name>
+ <fsummary>Convert a binary to a list of integers</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>PosLen = part()</v>
+ </type>
+ <desc>
+
+ <p>Converts <c>Subject</c> to a list of <c>int()</c>s, each representing
+ the value of one byte. The <c>part()</c> denotes which part of the
+ <c>binary()</c> to convert. Example:</p>
+
+<code>
+1> binary:bin_to_list(&lt;&lt;"erlang"&gt;&gt;,{1,3}).
+"rla"
+%% or [114,108,97] in list notation.
+</code>
+ <p>If <c>PosLen</c> in any way references outside the binary, a <c>badarg</c> exception is raised.</p>
+ </desc>
+ </func>
+ <func>
+ <name>bin_to_list(Subject, Pos, Len) -> list()</name>
+ <fsummary>Convert a binary to a list of integers</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pos = int()</v>
+ <v>Len = int()</v>
+ </type>
+ <desc>
+ <p>The same as<c> bin_to_list(Subject,{Pos,Len})</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>compile_pattern(Pattern) -> cp()</name>
+ <fsummary>Pre-compiles a binary search pattern</fsummary>
+ <type>
+ <v>Pattern = binary() | [ binary() ]</v>
+ </type>
+ <desc>
+
+ <p>Builds an internal structure representing a compilation of a
+ search-pattern, later to be used in the <seealso marker="#match-3">match/3</seealso>,
+ <seealso marker="#matches-3">matches/3</seealso>,
+ <seealso marker="#split-3">split/3</seealso> or
+ <seealso marker="#replace-4">replace/4</seealso>
+ functions. The <c>cp()</c> returned is guaranteed to be a
+ <c>tuple()</c> to allow programs to distinguish it from non
+ pre-compiled search patterns</p>
+
+ <p>When a list of binaries is given, it denotes a set of
+ alternative binaries to search for. I.e if
+ <c>[&lt;&lt;"functional"&gt;&gt;,&lt;&lt;"programming"&gt;&gt;]</c>
+ is given as <c>Pattern</c>, this
+ means "either <c>&lt;&lt;"functional"&gt;&gt;</c> or
+ <c>&lt;&lt;"programming"&gt;&gt;</c>". The pattern is a set of
+ alternatives; when only a single binary is given, the set has
+ only one element. The order of alternatives in a pattern is not significant.</p>
+
+ <p>The list of binaries used for search alternatives shall be flat and proper.</p>
+
+ <p>If <c>Pattern</c> is not a binary or a flat proper list of binaries with length &gt; 0,
+ a <c>badarg</c> exception will be raised.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>copy(Subject) -> binary()</name>
+ <fsummary>Creates a duplicate of a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ </type>
+ <desc>
+ <p>The same as <c>copy(Subject, 1)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>copy(Subject,N) -> binary()</name>
+ <fsummary>Duplicates a binary N times and creates a new</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>N = int() >= 0</v>
+ </type>
+ <desc>
+ <p>Creates a binary with the content of <c>Subject</c> duplicated <c>N</c> times.</p>
+
+ <p>This function will always create a new binary, even if <c>N =
+ 1</c>. By using <c>copy/1</c> on a binary referencing a larger binary, one
+ might free up the larger binary for garbage collection.</p>
+
+ <note>
+ <p>By deliberately copying a single binary to avoid referencing
+ a larger binary, one might, instead of freeing up the larger
+ binary for later garbage collection, create much more binary
+ data than needed. Sharing binary data is usually good. Only in
+ special cases, when small parts reference large binaries and the
+ large binaries are no longer used in any process, deliberate
+ copying might be a good idea.</p> </note>
+
+ <p>If <c>N</c> &lt; <c>0</c>, a <c>badarg</c> exception is raised.</p>
+ </desc>
+ </func>
+ <func>
+ <name>decode_unsigned(Subject) -> Unsigned</name>
+ <fsummary>Decode a whole binary into an integer of arbitrary size</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Unsigned = int() >= 0</v>
+ </type>
+ <desc>
+ <p>The same as <c>decode_unsigned(Subject,big)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>decode_unsigned(Subject, Endianess) -> Unsigned</name>
+ <fsummary>Decode a whole binary into an integer of arbitrary size</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Endianess = big | little</v>
+ <v>Unsigned = int() >= 0</v>
+ </type>
+ <desc>
+
+ <p>Converts the binary digit representation, in big or little
+ endian, of a positive integer in <c>Subject</c> to an Erlang <c>int()</c>.</p>
+
+ <p>Example:</p>
+
+ <code>
+1> binary:decode_unsigned(&lt;&lt;169,138,199&gt;&gt;,big).
+11111111
+ </code>
+ </desc>
+ </func>
+ <func>
+ <name>encode_unsigned(Unsigned) -> binary()</name>
+ <fsummary>Encodes an unsigned integer into the minimal binary</fsummary>
+ <type>
+ <v>Unsigned = int() >= 0</v>
+ </type>
+ <desc>
+ <p>The same as <c>encode_unsigned(Unsigned,big)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>encode_unsigned(Unsigned,Endianess) -> binary()</name>
+ <fsummary>Encodes an unsigned integer into the minimal binary</fsummary>
+ <type>
+ <v>Unsigned = int() >= 0</v>
+ <v>Endianess = big | little</v>
+ </type>
+ <desc>
+
+ <p>Converts a positive integer to the smallest possible
+ representation in a binary digit representation, either big
+ or little endian.</p>
+
+ <p>Example:</p>
+
+ <code>
+1> binary:encode_unsigned(11111111,big).
+&lt;&lt;169,138,199&gt;&gt;
+ </code>
+ </desc>
+ </func>
+ <func>
+ <name>first(Subject) -> int()</name>
+ <fsummary>Returns the first byte of a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ </type>
+ <desc>
+
+ <p>Returns the first byte of the binary <c>Subject</c> as an integer. If the
+ size of <c>Subject</c> is zero, a <c>badarg</c> exception is raised.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>last(Subject) -> int()</name>
+ <fsummary>Returns the last byte of a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ </type>
+ <desc>
+
+ <p>Returns the last byte of the binary <c>Subject</c> as an integer. If the
+ size of <c>Subject</c> is zero, a <c>badarg</c> exception is raised.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>list_to_bin(ByteList) -> binary()</name>
+ <fsummary>Convert a list of integers and binaries to a binary</fsummary>
+ <type>
+ <v>ByteList = iodata() (see module erlang)</v>
+ </type>
+ <desc>
+ <p>Works exactly as <c>erlang:list_to_binary/1</c>, added for completeness.</p>
+ </desc>
+ </func>
+ <func>
+ <name>longest_common_prefix(Binaries) -> int()</name>
+ <fsummary>Returns length of longest common prefix for a set of binaries</fsummary>
+ <type>
+ <v>Binaries = [ binary() ]</v>
+ </type>
+ <desc>
+
+ <p>Returns the length of the longest common prefix of the
+ binaries in the list <c>Binaries</c>. Example:</p>
+
+<code>
+1> binary:longest_common_prefix([&lt;&lt;"erlang"&gt;&gt;,&lt;&lt;"ergonomy"&gt;&gt;]).
+2
+2> binary:longest_common_prefix([&lt;&lt;"erlang"&gt;&gt;,&lt;&lt;"perl"&gt;&gt;]).
+0
+</code>
+
+ <p>If <c>Binaries</c> is not a flat list of binaries, a <c>badarg</c> exception is raised.</p>
+ </desc>
+ </func>
+ <func>
+ <name>longest_common_suffix(Binaries) -> int()</name>
+ <fsummary>Returns length of longest common suffix for a set of binaries</fsummary>
+ <type>
+ <v>Binaries = [ binary() ]</v>
+ </type>
+ <desc>
+
+ <p>Returns the length of the longest common suffix of the
+ binaries in the list <c>Binaries</c>. Example:</p>
+
+<code>
+1> binary:longest_common_suffix([&lt;&lt;"erlang"&gt;&gt;,&lt;&lt;"fang"&gt;&gt;]).
+3
+2> binary:longest_common_suffix([&lt;&lt;"erlang"&gt;&gt;,&lt;&lt;"perl"&gt;&gt;]).
+0
+</code>
+
+ <p>If <c>Binaries</c> is not a flat list of binaries, a <c>badarg</c> exception is raised.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>match(Subject, Pattern) -> Found | <c>nomatch</c></name>
+ <fsummary>Searches for the first match of a pattern in a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pattern = binary() | [ binary() ] | cp()</v>
+ <v>Found = part()</v>
+ </type>
+ <desc>
+ <p>The same as <c>match(Subject, Pattern, [])</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>match(Subject,Pattern,Options) -> Found | <c>nomatch</c></name>
+ <fsummary>Searches for the first match of a pattern in a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pattern = binary() | [ binary() ] | cp()</v>
+ <v>Found = part()</v>
+ <v>Options = [ Option ]</v>
+ <v>Option = {scope, part()}</v>
+ </type>
+ <desc>
+
+ <p>Searches for the first occurrence of <c>Pattern</c> in <c>Subject</c> and
+ returns the position and length.</p>
+
+ <p>The function will return <c>{Pos,Length}</c> for the binary
+ in <c>Pattern</c> starting at the lowest position in
+ <c>Subject</c>, Example:</p>
+
+<code>
+1> binary:match(&lt;&lt;"abcde"&gt;&gt;, [&lt;&lt;"bcde"&gt;&gt;,&lt;&lt;"cd"&gt;&gt;],[]).
+{1,4}
+</code>
+
+ <p>Even though <c>&lt;&lt;"cd"&gt;&gt;</c> ends before
+ <c>&lt;&lt;"bcde"&gt;&gt;</c>, <c>&lt;&lt;"bcde"&gt;&gt;</c>
+ begins first and is therefore the first match. If two
+ overlapping matches begin at the same position, the longest is
+ returned.</p>
+
+ <p>Summary of the options:</p>
+
+ <taglist>
+ <tag>{scope, {Start, Length}}</tag>
+ <item><p>Only the given part is searched. Return values still have
+ offsets from the beginning of <c>Subject</c>. A negative <c>Length</c> is
+ allowed as described in the <c>TYPES</c> section of this manual.</p></item>
+ </taglist>
+
+ <p>If none of the strings in
+ <c>Pattern</c> is found, the atom <c>nomatch</c> is returned.</p>
+
+ <p>For a description of <c>Pattern</c>, see
+ <seealso marker="#compile_pattern-1">compile_pattern/1</seealso>.</p>
+
+ <p>If <c>{scope, {Start,Length}}</c> is given in the options
+ such that <c>Start</c> is larger than the size of
+ <c>Subject</c>, <c>Start + Length</c> is less than zero or
+ <c>Start + Length</c> is larger than the size of
+ <c>Subject</c>, a <c>badarg</c> exception is raised.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>matches(Subject, Pattern) -> Found</name>
+ <fsummary>Searches for all matches of a pattern in a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pattern = binary() | [ binary() ] | cp()</v>
+ <v>Found = [ part() ] | []</v>
+ </type>
+ <desc>
+ <p>The same as <c>matches(Subject, Pattern, [])</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>matches(Subject,Pattern,Options) -> Found</name>
+ <fsummary>Searches for all matches of a pattern in a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pattern = binary() | [ binary() ] | cp()</v>
+ <v>Found = [ part() ] | []</v>
+ <v>Options = [ Option ]</v>
+ <v>Option = {scope, part()}</v>
+ </type>
+ <desc>
+
+ <p>Works like match, but the <c>Subject</c> is searched until
+ exhausted and a list of all non-overlapping parts matching
+ <c>Pattern</c> is returned (in order). </p>
+
+ <p>The first and longest match is preferred to a shorter,
+ which is illustrated by the following example:</p>
+
+<code>
+1> binary:matches(&lt;&lt;"abcde"&gt;&gt;,
+ [&lt;&lt;"bcde"&gt;&gt;,&lt;&lt;"bc"&gt;&gt;>,&lt;&lt;"de"&gt;&gt;],[]).
+[{1,4}]
+</code>
+
+ <p>The result shows that &lt;&lt;bcde"&gt;&gt; is selected instead of the
+ shorter match &lt;&lt;"bc"&gt;&gt; (which would have given raise to one
+ more match,&lt;&lt;"de"&gt;&gt;). This corresponds to the behavior of posix
+ regular expressions (and programs like awk), but is not
+ consistent with alternative matches in re (and Perl), where
+ instead lexical ordering in the search pattern selects which
+ string matches.</p>
+
+ <p>If none of the strings in pattern is found, an empty list is returned.</p>
+
+ <p>For a description of <c>Pattern</c>, see <seealso marker="#compile_pattern-1">compile_pattern/1</seealso> and for a
+ description of available options, see <seealso marker="#match-3">match/3</seealso>.</p>
+
+ <p>If <c>{scope, {Start,Length}}</c> is given in the options such that
+ <c>Start</c> is larger than the size of <c>Subject</c>, <c>Start + Length</c> is
+ less than zero or <c>Start + Length</c> is larger than the size of
+ <c>Subject</c>, a <c>badarg</c> exception is raised.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>part(Subject, PosLen) -> binary()</name>
+ <fsummary>Extracts a part of a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>PosLen = part()</v>
+ </type>
+ <desc>
+
+ <p>Extracts the part of the binary <c>Subject</c> described by <c>PosLen</c>.</p>
+
+ <p>Negative length can be used to extract bytes at the end of a binary:</p>
+
+<code>
+1> Bin = &lt;&lt;1,2,3,4,5,6,7,8,9,10&gt;&gt;.
+2> binary:part(Bin,{byte_size(Bin), -5)).
+&lt;&lt;6,7,8,9,10&gt;&gt;
+</code>
+
+ <note>
+ <p><seealso marker="#part-2">part/2</seealso>and <seealso
+ marker="#part-3">part/3</seealso> are also available in the
+ <c>erlang</c> module under the names <c>binary_part/2</c> and
+ <c>binary_part/3</c>. Those BIFs are allowed in guard tests.</p>
+ </note>
+
+ <p>If <c>PosLen</c> in any way references outside the binary, a <c>badarg</c> exception
+ is raised.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>part(Subject, Pos, Len) -> binary()</name>
+ <fsummary>Extracts a part of a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pos = int()</v>
+ <v>Len = int()</v>
+ </type>
+ <desc>
+ <p>The same as <c>part(Subject, {Pos, Len})</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>referenced_byte_size(binary()) -> int()</name>
+ <fsummary>Determines the size of the actual binary pointed out by a sub-binary</fsummary>
+ <desc>
+
+ <p>If a binary references a larger binary (often described as
+ being a sub-binary), it can be useful to get the size of the
+ actual referenced binary. This function can be used in a program
+ to trigger the use of <c>copy/1</c>. By copying a binary, one might
+ dereference the original, possibly large, binary which a smaller
+ binary is a reference to.</p>
+
+ <p>Example:</p>
+
+ <code>
+store(Binary, GBSet) ->
+ NewBin =
+ case binary:referenced_byte_size(Binary) of
+ Large when Large > 2 * byte_size(Binary) ->
+ binary:copy(Binary);
+ _ ->
+ Binary
+ end,
+ gb_sets:insert(NewBin,GBSet).
+ </code>
+
+ <p>In this example, we chose to copy the binary content before
+ inserting it in the <c>gb_set()</c> if it references a binary more than
+ twice the size of the data we're going to keep. Of course
+ different rules for when copying will apply to different
+ programs.</p>
+
+ <p>Binary sharing will occur whenever binaries are taken apart,
+ this is the fundamental reason why binaries are fast,
+ decomposition can always be done with O(1) complexity. In rare
+ circumstances this data sharing is however undesirable, why this
+ function together with <c>copy/1</c> might be useful when optimizing
+ for memory use.</p>
+
+ <p>Example of binary sharing:</p>
+
+ <code>
+1> A = binary:copy(&lt;&lt;1&gt;&gt;,100).
+&lt;&lt;1,1,1,1,1 ...
+2> byte_size(A).
+100
+3> binary:referenced_byte_size(A)
+100
+4> &lt;&lt;_:10/binary,B:10/binary,_/binary&gt;&gt; = A.
+&lt;&lt;1,1,1,1,1 ...
+5> byte_size(B).
+10
+6> binary:referenced_byte_size(B)
+100
+ </code>
+
+ <note>
+ <p>Binary data is shared among processes. If another process
+ still references the larger binary, copying the part this
+ process uses only consumes more memory and will not free up the
+ larger binary for garbage collection. Use this kind of intrusive
+ functions with extreme care, and only if a real problem is
+ detected.</p>
+ </note>
+
+ </desc>
+ </func>
+ <func>
+ <name>replace(Subject,Pattern,Replacement) -> Result</name>
+ <fsummary>Replaces bytes in a binary according to a pattern</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pattern = binary() | [ binary() ] | cp()</v>
+ <v>Replacement = binary()</v>
+ <v>Result = binary()</v>
+ </type>
+ <desc>
+ <p>The same as <c>replace(Subject,Pattern,Replacement,[])</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>replace(Subject,Pattern,Replacement,Options) -> Result</name>
+ <fsummary>Replaces bytes in a binary according to a pattern</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pattern = binary() | [ binary() ] | cp()</v>
+ <v>Replacement = binary()</v>
+ <v>Result = binary()</v>
+ <v>Options = [ Option ]</v>
+ <v>Option = global | {scope, part()} | {insert_replaced, InsPos}</v>
+ <v>InsPos = OnePos | [ OnePos ]</v>
+ <v>OnePos = int() =&lt; byte_size(Replacement)</v>
+ </type>
+ <desc>
+
+ <p>Constructs a new binary by replacing the parts in
+ <c>Subject</c> matching <c>Pattern</c> with the content of
+ <c>Replacement</c>.</p>
+
+ <p>If the matching sub-part of <c>Subject</c> giving raise to the
+ replacement is to be inserted in the result, the option
+ <c>{insert_replaced, InsPos}</c> will insert the matching part into
+ <c>Replacement</c> at the given position (or positions) before actually
+ inserting <c>Replacement</c> into the <c>Subject</c>. Example:</p>
+
+<code>
+1> binary:replace(&lt;&lt;"abcde"&gt;&gt;,&lt;&lt;"b"&gt;&gt;,&lt;&lt;"[]"&gt;&gt;,[{insert_replaced,1}]).
+&lt;&lt;"a[b]cde"&gt;&gt;
+2> binary:replace(&lt;&lt;"abcde"&gt;&gt;,[&lt;&lt;"b"&gt;&gt;,&lt;&lt;"d"&gt;&gt;],&lt;&lt;"[]"&gt;&gt;,
+ [global,{insert_replaced,1}]).
+&lt;&lt;"a[b]c[d]e"&gt;&gt;
+3> binary:replace(&lt;&lt;"abcde"&gt;&gt;,[&lt;&lt;"b"&gt;&gt;,&lt;&lt;"d"&gt;&gt;],&lt;&lt;"[]"&gt;&gt;,
+ [global,{insert_replaced,[1,1]}]).
+&lt;&lt;"a[bb]c[dd]e"&gt;&gt;
+4> binary:replace(&lt;&lt;"abcde"&gt;&gt;,[&lt;&lt;"b"&gt;&gt;,&lt;&lt;"d"&gt;&gt;],&lt;&lt;"[-]"&gt;&gt;,
+ [global,{insert_replaced,[1,2]}]).
+&lt;&lt;"a[b-b]c[d-d]e"&gt;&gt;
+</code>
+
+ <p>If any position given in <c>InsPos</c> is greater than the size of the replacement binary, a <c>badarg</c> exception is raised.</p>
+
+ <p>The options <c>global</c> and <c>{scope, part()}</c> work as for <seealso marker="#split-3">split/3</seealso>. The return type is always a <c>binary()</c>.</p>
+
+ <p>For a description of <c>Pattern</c>, see <seealso marker="#compile_pattern-1">compile_pattern/1</seealso>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>split(Subject,Pattern) -> Parts</name>
+ <fsummary>Splits a binary according to a pattern</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pattern = binary() | [ binary() ] | cp()</v>
+ <v>Parts = [ binary() ]</v>
+ </type>
+ <desc>
+ <p>The same as <c>split(Subject, Pattern, [])</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>split(Subject,Pattern,Options) -> Parts</name>
+ <fsummary>Splits a binary according to a pattern</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pattern = binary() | [ binary() ] | cp()</v>
+ <v>Parts = [ binary() ]</v>
+ <v>Options = [ Option ]</v>
+ <v>Option = {scope, part()} | trim | global</v>
+ </type>
+ <desc>
+
+ <p>Splits Binary into a list of binaries based on Pattern. If
+ the option global is not given, only the first occurrence of
+ Pattern in Subject will give rise to a split.</p>
+
+ <p>The parts of Pattern actually found in Subject are not included in the result.</p>
+
+ <p>Example:</p>
+
+<code>
+1> binary:split(&lt;&lt;1,255,4,0,0,0,2,3&gt;&gt;, [&lt;&lt;0,0,0&gt;&gt;,&lt;&lt;2&gt;&gt;],[]).
+[&lt;&lt;1,255,4&gt;&gt;, &lt;&lt;2,3&gt;&gt;]
+2> binary:split(&lt;&lt;0,1,0,0,4,255,255,9&gt;&gt;, [&lt;&lt;0,0&gt;&gt;, &lt;&lt;255,255&gt;&gt;],[global]).
+[&lt;&lt;0,1&gt;&gt;,&lt;&lt;4&gt;&gt;,&lt;&lt;9&gt;&gt;]
+</code>
+
+ <p>Summary of options:</p>
+ <taglist>
+
+ <tag>{scope, part()}</tag>
+
+ <item><p>Works as in <seealso marker="#match-3">match/3</seealso> and
+ <seealso marker="#matches-3">matches/3</seealso>. Note that
+ this only defines the scope of the search for matching strings,
+ it does not cut the binary before splitting. The bytes before
+ and after the scope will be kept in the result. See example
+ below.</p></item>
+
+ <tag>trim</tag>
+
+ <item><p>Removes trailing empty parts of the result (as does trim in <c>re:split/3</c>)</p></item>
+
+ <tag>global</tag>
+
+ <item><p>Repeats the split until the <c>Subject</c> is
+ exhausted. Conceptually the global option makes split work on
+ the positions returned by <seealso marker="#matches-3">matches/3</seealso>,
+ while it normally
+ works on the position returned by
+ <seealso marker="#match-3">match/3</seealso>.</p></item>
+
+ </taglist>
+
+ <p>Example of the difference between a scope and taking the
+ binary apart before splitting:</p>
+
+<code>
+1> binary:split(&lt;&lt;"banana"&gt;&gt;,[&lt;&lt;"a"&gt;&gt;],[{scope,{2,3}}]).
+[&lt;&lt;"ban"&gt;&gt;,&lt;&lt;"na"&gt;&gt;]
+2> binary:split(binary:part(&lt;&lt;"banana"&gt;&gt;,{2,3}),[&lt;&lt;"a"&gt;&gt;],[]).
+[&lt;&lt;"n"&gt;&gt;,&lt;&lt;"n"&gt;&gt;]
+</code>
+
+ <p>The return type is always a list of binaries that are all
+ referencing <c>Subject</c>. This means that the data in <c>Subject</c> is not
+ actually copied to new binaries and that <c>Subject</c> cannot be
+ garbage collected until the results of the split are no longer
+ referenced.</p>
+
+ <p>For a description of <c>Pattern</c>, see <seealso marker="#compile_pattern-1">compile_pattern/1</seealso>.</p>
+
+ </desc>
+ </func>
+ </funcs>
+</erlref>
diff --git a/lib/stdlib/doc/src/dets.xml b/lib/stdlib/doc/src/dets.xml
index 8d1398d3b7..b002af6616 100644
--- a/lib/stdlib/doc/src/dets.xml
+++ b/lib/stdlib/doc/src/dets.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -109,7 +109,7 @@ bool() = true | false
file() = string()
int() = integer() >= 0
keypos() = integer() >= 1
-name() = atom() | ref()
+name() = atom() | reference()
no_slots() = integer() >= 0 | default
object() = tuple()
object_cont() = tuple()
@@ -759,7 +759,7 @@ ok
<fsummary>Open an existing Dets table.</fsummary>
<type>
<v>FileName = file()</v>
- <v>Reference = ref()</v>
+ <v>Reference = reference()</v>
</type>
<desc>
<p>Opens an existing table. If the table has not been properly
@@ -816,7 +816,7 @@ ok
</item>
<item>
<p><c>{max_no_slots, no_slots()}</c>, the maximum number
- of slots that will be used. The default value is 2 M, and
+ of slots that will be used. The default value as well as
the maximal value is 32 M. Note that a higher value may
increase the fragmentation of the table, and conversely,
that a smaller value may decrease the fragmentation, at
diff --git a/lib/stdlib/doc/src/erl_id_trans.xml b/lib/stdlib/doc/src/erl_id_trans.xml
index 7c821d2efc..cfb18ec131 100644
--- a/lib/stdlib/doc/src/erl_id_trans.xml
+++ b/lib/stdlib/doc/src/erl_id_trans.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2007</year>
+ <year>2010</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -70,7 +70,8 @@
<section>
<title>See Also</title>
- <p><seealso marker="erl_parse">erl_parse(3)</seealso>, compile(3).</p>
+ <p><seealso marker="erl_parse">erl_parse(3)</seealso>,
+ <seealso marker="compiler:compile">compile(3)</seealso>.</p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/erl_lint.xml b/lib/stdlib/doc/src/erl_lint.xml
index 6a7d37765c..8639d678fa 100644
--- a/lib/stdlib/doc/src/erl_lint.xml
+++ b/lib/stdlib/doc/src/erl_lint.xml
@@ -96,8 +96,8 @@
<p>The <c>AbsForms</c> of a module which comes from a file
that is read through <c>epp</c>, the Erlang pre-processor,
can come from many files. This means that any references to
- errors must include the file name (see <seealso marker="epp">epp(3)</seealso>, or parser <seealso marker="erl_parse">erl_parse(3)</seealso> The warnings and
- errors returned have the following format:
+ errors must include the file name (see <seealso marker="epp">epp(3)</seealso>, or parser <seealso marker="erl_parse">erl_parse(3)</seealso>).
+ The warnings and errors returned have the following format:
</p>
<code type="none">
[{FileName2,[ErrorInfo]}] </code>
diff --git a/lib/stdlib/doc/src/erl_parse.xml b/lib/stdlib/doc/src/erl_parse.xml
index ae8a8afd5c..18b592deea 100644
--- a/lib/stdlib/doc/src/erl_parse.xml
+++ b/lib/stdlib/doc/src/erl_parse.xml
@@ -39,7 +39,7 @@
expressions, or terms. The Abstract Format is described in the ERTS
User's Guide.
Note that a token list must end with the <em>dot</em> token in order
- to be acceptable to the parse functions (see erl_scan).</p>
+ to be acceptable to the parse functions (see <seealso marker="erl_scan">erl_scan(3)</seealso>).</p>
</description>
<funcs>
<func>
diff --git a/lib/stdlib/doc/src/erl_scan.xml b/lib/stdlib/doc/src/erl_scan.xml
index 4175146c3c..1199c34f0f 100644
--- a/lib/stdlib/doc/src/erl_scan.xml
+++ b/lib/stdlib/doc/src/erl_scan.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>erl_scan</title>
@@ -103,7 +103,7 @@ attributes() = line() | list() | tuple()</code>
Info, atom()}</c>,
<c>{char, Info, integer()}</c>, <c>{comment, Info,
string()}</c>, <c>{float, Info, float()}</c>, <c>{integer,
- Info, integer()}</c>, <c>{var, Info, atom()}</c>,
+ Info, integer()}</c>, <c>{var, Info, atom()}</c>,
and <c>{white_space, Info, string()}</c>.</p>
<p>The valid options are:</p>
<taglist>
@@ -149,7 +149,8 @@ attributes() = line() | list() | tuple()</code>
<v>StartLocation = EndLocation = location()</v>
<v>Options = Option | [Option]</v>
<v>Option = {reserved_word_fun,reserved_word_fun()}
- | return_comments | return_white_spaces | return</v>
+ | return_comments | return_white_spaces | return
+ | text</v>
</type>
<desc>
<p>This is the re-entrant scanner which scans characters until
@@ -173,7 +174,7 @@ attributes() = line() | list() | tuple()</code>
<tag><c>{error, ErrorInfo, EndLocation}</c></tag>
<item>
<p>An error occurred. <c>LeftOverChars</c> is the remaining
- characters of the input data,
+ characters of the input data,
starting from <c>EndLocation</c>.</p>
</item>
</taglist>
@@ -278,7 +279,7 @@ attributes() = line() | list() | tuple()</code>
<item><p>The token's symbol.</p>
</item>
<tag><c>{text, string()}</c></tag>
- <item><p>The token's text..</p>
+ <item><p>The token's text.</p>
</item>
</taglist>
</desc>
@@ -315,7 +316,7 @@ attributes() = line() | list() | tuple()</code>
<type>
<v>Attributes = attributes()</v>
<v>AttributeItemSpec = AttributeItem | [AttributeItem]</v>
- <v>AttributesInfo = AttributeInfoTuple | undefined
+ <v>AttributesInfo = AttributeInfoTuple | undefined
| [AttributeInfoTuple]</v>
<v>AttributeInfoTuple = {AttributeItem, Info}</v>
<v>AttributeItem = atom()</v>
@@ -352,7 +353,7 @@ attributes() = line() | list() | tuple()</code>
just the line if the column unknown.</p>
</item>
<tag><c>{text, string()}</c></tag>
- <item><p>The token's text..</p>
+ <item><p>The token's text.</p>
</item>
</taglist>
</desc>
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index 7b9f0e7772..746f94d3f4 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>ets</title>
@@ -56,8 +56,8 @@
Even if there are no references to a table from any process, it
will not automatically be destroyed unless the owner process
terminates. It can be destroyed explicitly by using
- <c>delete/1</c>.</p>
- <p>Since R13B01, table ownership can be transferred at process termination
+ <c>delete/1</c>. The default owner is the process that created the
+ table. Table ownership can be transferred at process termination
by using the <seealso marker="#heir">heir</seealso> option or explicitly
by calling <seealso marker="#give_away/3">give_away/3</seealso>.</p>
<p>Some implementation details:</p>
@@ -82,11 +82,15 @@
<c>float()</c> that extends to the same value, hence the key
<c>1</c> and the key <c>1.0</c> are regarded as equal in an
<c>ordered_set</c> table.</p>
- <p>In general, the functions below will exit with reason
- <c>badarg</c> if any argument is of the wrong format, or if the
- table identifier is invalid.</p>
</description>
-
+ <section>
+ <title>Failure</title>
+ <p>In general, the functions below will exit with reason
+ <c>badarg</c> if any argument is of the wrong format, if the
+ table identifier is invalid or if the operation is denied due to
+ table access rights (<seealso marker="#protected">protected</seealso>
+ or <seealso marker="#private">private</seealso>).</p>
+ </section>
<section><marker id="concurrency"></marker>
<title>Concurrency</title>
<p>This module provides some limited support for concurrent access.
@@ -481,6 +485,9 @@ Error: fun containing local Erlang function calls
<item><c>Item=protection, Value=public|protected|private</c> <br></br>
The table access rights.</item>
+ <item><c>Item=compressed, Value=true|false</c> <br></br>
+
+ Indicates if the table is compressed or not.</item>
</list>
</desc>
</func>
@@ -947,9 +954,10 @@ ets:select(Table,MatchSpec),</code>
<type>
<v>Name = atom()</v>
<v>Options = [Option]</v>
- <v>&nbsp;Option = Type | Access | named_table | {keypos,Pos} | {heir,pid(),HeirData} | {heir,none} | {write_concurrency,bool()}</v>
+ <v>&nbsp;Option = Type | Access | named_table | {keypos,Pos} | {heir,pid(),HeirData} | {heir,none} | Tweaks</v>
<v>&nbsp;&nbsp;Type = set | ordered_set | bag | duplicate_bag</v>
<v>&nbsp;&nbsp;Access = public | protected | private</v>
+ <v>&nbsp;&nbsp;Tweaks = {write_concurrency,bool()} | {read_concurrency,bool()} | compressed</v>
<v>&nbsp;&nbsp;Pos = int()</v>
<v>&nbsp;&nbsp;HeirData = term()</v>
</type>
@@ -963,7 +971,7 @@ ets:select(Table,MatchSpec),</code>
table is named or not. If one or more options are left out,
the default values are used. This means that not specifying
any options (<c>[]</c>) is the same as specifying
- <c>[set,protected,{keypos,1},{heir,none},{write_concurrency,false}]</c>.</p>
+ <c>[set,protected,{keypos,1},{heir,none},{write_concurrency,false},{read_concurrency,false}]</c>.</p>
<list type="bulleted">
<item>
<p><c>set</c>
@@ -1002,12 +1010,14 @@ ets:select(Table,MatchSpec),</code>
Any process may read or write to the table.</p>
</item>
<item>
+ <marker id="protected"></marker>
<p><c>protected</c>
The owner process can read and write to the table. Other
processes can only read the table. This is the default
setting for the access rights.</p>
</item>
<item>
+ <marker id="private"></marker>
<p><c>private</c>
Only the owner process can read or write to the table.</p>
</item>
@@ -1039,15 +1049,22 @@ ets:select(Table,MatchSpec),</code>
the owner terminates.</p>
</item>
<item>
+ <marker id="new_2_write_concurrency"></marker>
<p><c>{write_concurrency,bool()}</c>
- Performance tuning. Default is <c>false</c>, which means that the table
- is optimized towards concurrent read access. An operation that
+ Performance tuning. Default is <c>false</c>, in which case an operation that
mutates (writes to) the table will obtain exclusive access,
blocking any concurrent access of the same table until finished.
If set to <c>true</c>, the table is optimized towards concurrent
write access. Different objects of the same table can be mutated
(and read) by concurrent processes. This is achieved to some degree
- at the expense of single access and concurrent reader performance.
+ at the expense of sequential access and concurrent reader performance.
+ The <c>write_concurrency</c> option can be combined with the
+ <seealso marker="#new_2_read_concurrency">read_concurrency</seealso>
+ option. You typically want to combine these when large concurrent
+ read bursts and large concurrent write bursts are common (see the
+ documentation of the
+ <seealso marker="#new_2_read_concurrency">read_concurrency</seealso>
+ option for more information).
Note that this option does not change any guarantees about
<seealso marker="#concurrency">atomicy and isolation</seealso>.
Functions that makes such promises over several objects (like
@@ -1055,6 +1072,38 @@ ets:select(Table,MatchSpec),</code>
<p>Table type <c>ordered_set</c> is not affected by this option in current
implementation.</p>
</item>
+ <item>
+ <marker id="new_2_read_concurrency"></marker>
+ <p><c>{read_concurrency,bool()}</c>
+ Performance tuning. Default is <c>false</c>. When set to
+ <c>true</c>, the table is optimized for concurrent read
+ operations. When this option is enabled on a runtime system with
+ SMP support, read operations become much cheaper; especially on
+ systems with multiple physical processors. However, switching
+ between read and write operations becomes more expensive. You
+ typically want to enable this option when concurrent read
+ operations are much more frequent than write operations, or when
+ concurrent reads and writes comes in large read and write
+ bursts (i.e., lots of reads not interrupted by writes, and lots
+ of writes not interrupted by reads). You typically do
+ <em>not</em> want to enable this option when the common access
+ pattern is a few read operations interleaved with a few write
+ operations repeatedly. In this case you will get a performance
+ degradation by enabling this option. The <c>read_concurrency</c>
+ option can be combined with the
+ <seealso marker="#new_2_write_concurrency">write_concurrency</seealso>
+ option. You typically want to combine these when large concurrent
+ read bursts and large concurrent write bursts are common.</p>
+ </item>
+ <item>
+ <marker id="new_2_compressed"></marker>
+ <p><c>compressed</c>
+ If this option is present, the table data will be stored in a more compact format to
+ consume less memory. The downside is that it will make table operations slower.
+ Especially operations that need to inspect entire objects,
+ such as <c>match</c> and <c>select</c>, will get much slower. The key element
+ is not compressed in current implementation.</p>
+ </item>
</list>
</desc>
</func>
@@ -1355,6 +1404,28 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</desc>
</func>
<func>
+ <name>select_count(Tab, MatchSpec) -> NumMatched</name>
+ <fsummary>Match the objects in an ETS table against a match_spec and returns the number of objects for which the match_spec returned 'true'</fsummary>
+ <type>
+ <v>Tab = tid() | atom()</v>
+ <v>Object = tuple()</v>
+ <v>MatchSpec = match_spec()</v>
+ <v>NumMatched = integer()</v>
+ </type>
+ <desc>
+ <p>Matches the objects in the table <c>Tab</c> using a
+ <seealso marker="#match_spec">match_spec</seealso>. If the
+ match_spec returns <c>true</c> for an object, that object
+ considered a match and is counted. For any other result from
+ the match_spec the object is not considered a match and is
+ therefore not counted.</p>
+ <p>The function could be described as a <c>match_delete/2</c>
+ that does not actually delete any elements, but only counts
+ them.</p>
+ <p>The function returns the number of objects matched.</p>
+ </desc>
+ </func>
+ <func>
<name>select_delete(Tab, MatchSpec) -> NumDeleted</name>
<fsummary>Match the objects in an ETS table against a match_spec and deletes objects where the match_spec returns 'true'</fsummary>
<type>
@@ -1381,25 +1452,82 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</desc>
</func>
<func>
- <name>select_count(Tab, MatchSpec) -> NumMatched</name>
- <fsummary>Match the objects in an ETS table against a match_spec and returns the number of objects for which the match_spec returned 'true'</fsummary>
+ <name>select_reverse(Tab, MatchSpec) -> [Match]</name>
+ <fsummary>Match the objects in an ETS table against a match_spec.</fsummary>
<type>
<v>Tab = tid() | atom()</v>
- <v>Object = tuple()</v>
+ <v>Match = term()</v>
<v>MatchSpec = match_spec()</v>
- <v>NumMatched = integer()</v>
</type>
<desc>
- <p>Matches the objects in the table <c>Tab</c> using a
- <seealso marker="#match_spec">match_spec</seealso>. If the
- match_spec returns <c>true</c> for an object, that object
- considered a match and is counted. For any other result from
- the match_spec the object is not considered a match and is
- therefore not counted.</p>
- <p>The function could be described as a <c>match_delete/2</c>
- that does not actually delete any elements, but only counts
- them.</p>
- <p>The function returns the number of objects matched.</p>
+
+ <p>Works like <c>select/2</c>, but returns the list in reverse
+ order for the <c>ordered_set</c> table type. For all other table
+ types, the return value is identical to that of <c>select/2</c>.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>select_reverse(Tab, MatchSpec, Limit) -> {[Match],Continuation} | '$end_of_table'</name>
+ <fsummary>Match the objects in an ETS table against a match_spec and returns part of the answers.</fsummary>
+ <type>
+ <v>Tab = tid() | atom()</v>
+ <v>Match = term()</v>
+ <v>MatchSpec = match_spec()</v>
+ <v>Continuation = term()</v>
+ </type>
+ <desc>
+
+ <p>Works like <c>select/3</c>, but for the <c>ordered_set</c>
+ table type, traversing is done starting at the last object in
+ Erlang term order and moves towards the first. For all other
+ table types, the return value is identical to that of
+ <c>select/3</c>.</p>
+
+ <p>Note that this is <em>not</em> equivalent to
+ reversing the result list of a <c>select/3</c> call, as the result list
+ is not only reversed, but also contains the last <c>Limit</c>
+ matching objects in the table, not the first.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>select_reverse(Continuation) -> {[Match],Continuation} | '$end_of_table'</name>
+ <fsummary>Continue matching objects in an ETS table.</fsummary>
+ <type>
+ <v>Match = term()</v>
+ <v>Continuation = term()</v>
+ </type>
+ <desc>
+
+ <p>Continues a match started with
+ <c>ets:select_reverse/3</c>. If the table is an
+ <c>ordered_set</c>, the traversal of the table will continue
+ towards objects with keys earlier in the Erlang term order. The
+ returned list will also contain objects with keys in reverse
+ order.</p>
+
+ <p>For all other table types, the behaviour is exatly that of <c>select/1</c>.</p>
+ <p>Example:</p>
+ <code>
+1> T = ets:new(x,[ordered_set]).
+2> [ ets:insert(T,{N}) || N &lt;- lists:seq(1,10) ].
+...
+3> {R0,C0} = ets:select_reverse(T,[{'_',[],['$_']}],4).
+...
+4> R0.
+[{10},{9},{8},{7}]
+5> {R1,C1} = ets:select_reverse(C0).
+...
+6> R1.
+[{6},{5},{4},{3}]
+7> {R2,C2} = ets:select_reverse(C1).
+...
+8> R2.
+[{2},{1}]
+9> '$end_of_table' = ets:select_reverse(C2).
+...
+ </code>
</desc>
</func>
<func>
@@ -1686,7 +1814,7 @@ true</pre>
</desc>
</func>
<func>
- <name>to_dets(Tab, DetsTab) -> Tab</name>
+ <name>to_dets(Tab, DetsTab) -> DetsTab</name>
<fsummary>Fill a Dets table with objects from an ETS table.</fsummary>
<type>
<v>Tab = tid() | atom()</v>
diff --git a/lib/stdlib/doc/src/filelib.xml b/lib/stdlib/doc/src/filelib.xml
index c1c4ca9350..969aff4fcb 100644
--- a/lib/stdlib/doc/src/filelib.xml
+++ b/lib/stdlib/doc/src/filelib.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -36,14 +36,23 @@
<description>
<p>This module contains utilities on a higher level than the <c>file</c>
module.</p>
+ <p>The module supports Unicode file names, so that it will match against regular expressions given in Unicode and that it will find and process raw file names (i.e. files named in a way that does not confirm to the expected encoding).</p>
+ <p>If the VM operates in Unicode file naming mode on a machine with transparent file naming, the <c>fun()</c> provided to <c>fold_files/5</c> needs to be prepared to handle binary file names.</p>
+ <p>For more information about raw file names, see the <seealso marker="kernel:file">file</seealso> module.</p>
</description>
<section>
<title>DATA TYPES</title>
<code type="none">
-filename() = string() | atom() | DeepList
-dirname() = filename()
-DeepList = [char() | atom() | DeepList]</code>
+filename() = = string() | atom() | DeepList | RawFilename
+ DeepList = [char() | atom() | DeepList]
+ RawFilename = binary()
+ If VM is in unicode filename mode, string() and char() are allowed to be > 255.
+ RawFilename is a filename not subject to Unicode translation, meaning that it
+ can contain characters not conforming to the Unicode encoding expected from the
+ filesystem (i.e. non-UTF-8 characters although the VM is started in Unicode
+ filename mode).
+dirname() = filename()</code>
</section>
<funcs>
@@ -90,6 +99,18 @@ DeepList = [char() | atom() | DeepList]</code>
If <c>Recursive</c> is true all sub-directories to <c>Dir</c>
are processed. The regular expression matching is done on just
the filename without the directory part.</p>
+
+ <p>If Unicode file name translation is in effect and the file
+ system is completely transparent, file names that cannot be
+ interpreted as Unicode may be encountered, in which case the
+ <c>fun()</c> must be prepared to handle raw file names
+ (i.e. binaries). If the regular expression contains
+ codepoints beyond 255, it will not match file names that does
+ not conform to the expected character encoding (i.e. are not
+ encoded in valid UTF-8).</p>
+
+ <p>For more information about raw file names, see the
+ <seealso marker="kernel:file">file</seealso> module.</p>
</desc>
</func>
<func>
@@ -160,6 +181,12 @@ DeepList = [char() | atom() | DeepList]</code>
<p>Matches any number of characters up to the end of
the filename, the next dot, or the next slash.</p>
</item>
+ <tag>[Character1,Character2,...]</tag>
+ <item>
+ <p>Matches any of the characters listed. Two characters
+ separated by a hyphen will match a range of characters.
+ Example: <c>[A-Z]</c> will match any uppercase letter.</p>
+ </item>
<tag>{Item,...}</tag>
<item>
<p>Alternation. Matches one of the alternatives.</p>
diff --git a/lib/stdlib/doc/src/filename.xml b/lib/stdlib/doc/src/filename.xml
index 0cf82fa48b..cdee6e4a81 100644
--- a/lib/stdlib/doc/src/filename.xml
+++ b/lib/stdlib/doc/src/filename.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2009</year>
+ <year>1997</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -43,13 +43,22 @@
only, even if the arguments contain back slashes. Use
<c>join/1</c> to normalize a file name by removing redundant
directory separators.</p>
+ <p>The module supports raw file names in the way that if a binary is present, or the file name cannot be interpreted according to the return value of
+ <seealso marker="kernel:file#native_name_encoding/0">file:native_name_encoding/0</seealso>, a raw file name will also be returned. For example filename:join/1 provided with a path component being a binary (and also not being possible to interpret under the current native file name encoding) will result in a raw file name being returned (the join operation will have been performed of course). For more information about raw file names, see the <seealso marker="kernel:file">file</seealso> module.</p>
</description>
<section>
<title>DATA TYPES</title>
<code type="none">
-name() = string() | atom() | DeepList
- DeepList = [char() | atom() | DeepList]</code>
+name() = string() | atom() | DeepList | RawFilename
+ DeepList = [char() | atom() | DeepList]
+ RawFilename = binary()
+ If VM is in unicode filename mode, string() and char() are allowed to be > 255.
+ RawFilename is a filename not subject to Unicode translation, meaning that it
+ can contain characters not conforming to the Unicode encoding expected from the
+ filesystem (i.e. non-UTF-8 characters although the VM is started in Unicode
+ filename mode).
+ </code>
</section>
<funcs>
<func>
diff --git a/lib/stdlib/doc/src/gen_event.xml b/lib/stdlib/doc/src/gen_event.xml
index df09294de6..2234a62ac3 100644
--- a/lib/stdlib/doc/src/gen_event.xml
+++ b/lib/stdlib/doc/src/gen_event.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>gen_event</title>
@@ -630,12 +630,66 @@ gen_event:stop -----> Module:terminate/2
<p>The function should return the updated internal state.</p>
</desc>
</func>
+ <func>
+ <name>Module:format_status(Opt, [PDict, State]) -> Status</name>
+ <fsummary>Optional function for providing a term describing the
+ current event handler state.</fsummary>
+ <type>
+ <v>Opt = normal | terminate</v>
+ <v>PDict = [{Key, Value}]</v>
+ <v>State = term()</v>
+ <v>Status = term()</v>
+ </type>
+ <desc>
+ <note>
+ <p>This callback is optional, so event handler modules need
+ not export it. If a handler does not export this function,
+ the gen_event module uses the handler state directly for
+ the purposes described below.</p>
+ </note>
+ <p>This function is called by a gen_event process when:</p>
+ <list typed="bulleted">
+ <item>One
+ of <seealso marker="sys#get_status/1">sys:get_status/1,2</seealso>
+ is invoked to get the gen_event status. <c>Opt</c> is set
+ to the atom <c>normal</c> for this case.</item>
+ <item>The event handler terminates abnormally and gen_event
+ logs an error. <c>Opt</c> is set to the
+ atom <c>terminate</c> for this case.</item>
+ </list>
+ <p>This function is useful for customising the form and
+ appearance of the event handler state for these cases. An
+ event handler callback module wishing to customise
+ the <c>sys:get_status/1,2</c> return value as well as how
+ its state appears in termination error logs exports an
+ instance of <c>format_status/2</c> that returns a term
+ describing the current state of the event handler.</p>
+ <p><c>PDict</c> is the current value of the gen_event's
+ process dictionary.</p>
+ <p><c>State</c> is the internal state of the event
+ handler.</p>
+ <p>The function should return <c>Status</c>, a term that
+ customises the details of the current state of the event
+ handler. Any term is allowed for <c>Status</c>. The
+ gen_event module uses <c>Status</c> as follows:</p>
+ <list typed="bulleted">
+ <item>When <c>sys:get_status/1,2</c> is called, gen_event
+ ensures that its return value contains <c>Status</c> in
+ place of the event handler's actual state term.</item>
+ <item>When an event handler terminates abnormally, gen_event
+ logs <c>Status</c> in place of the event handler's actual
+ state term.</item>
+ </list>
+ <p>One use for this function is to return compact alternative
+ state representations to avoid having large state terms
+ printed in logfiles.</p>
+ </desc>
+ </func>
</funcs>
<section>
<title>SEE ALSO</title>
- <p><seealso marker="supervisor">supervisor(3)</seealso>,
+ <p><seealso marker="supervisor">supervisor(3)</seealso>,
<seealso marker="sys">sys(3)</seealso></p>
</section>
</erlref>
-
diff --git a/lib/stdlib/doc/src/gen_fsm.xml b/lib/stdlib/doc/src/gen_fsm.xml
index 739cd0bffd..d15383c621 100644
--- a/lib/stdlib/doc/src/gen_fsm.xml
+++ b/lib/stdlib/doc/src/gen_fsm.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>gen_fsm</title>
@@ -730,33 +730,58 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
</desc>
</func>
<func>
- <name>Module:format_status(normal, [PDict, StateData]) -> Status</name>
+ <name>Module:format_status(Opt, [PDict, StateData]) -> Status</name>
<fsummary>Optional function for providing a term describing the
current gen_fsm status.</fsummary>
<type>
+ <v>Opt = normal | terminate</v>
<v>PDict = [{Key, Value}]</v>
<v>StateData = term()</v>
- <v>Status = [term()]</v>
+ <v>Status = term()</v>
</type>
<desc>
- <p><em>This callback is optional, so callback modules need not
- export it. The gen_fsm module provides a default
- implementation of this function that returns the callback
- module state data.</em></p>
- <p>This function is called by a gen_fsm process when one
- of <seealso marker="sys#get_status/1">sys:get_status/1,2</seealso>
- is invoked to get the gen_fsm status. A callback module
- wishing to customise the <c>sys:get_status/1,2</c> return
- value exports an instance of <c>format_status/2</c> that
- returns a term describing the current status of the
- gen_fsm.</p>
+ <note>
+ <p>This callback is optional, so callback modules need not
+ export it. The gen_fsm module provides a default
+ implementation of this function that returns the callback
+ module state data.</p>
+ </note>
+ <p>This function is called by a gen_fsm process when:</p>
+ <list typed="bulleted">
+ <item>One
+ of <seealso marker="sys#get_status/1">sys:get_status/1,2</seealso>
+ is invoked to get the gen_fsm status. <c>Opt</c> is set to
+ the atom <c>normal</c> for this case.</item>
+ <item>The gen_fsm terminates abnormally and logs an
+ error. <c>Opt</c> is set to the atom <c>terminate</c> for
+ this case.</item>
+ </list>
+ <p>This function is useful for customising the form and
+ appearance of the gen_fsm status for these cases. A callback
+ module wishing to customise the <c>sys:get_status/1,2</c>
+ return value as well as how its status appears in
+ termination error logs exports an instance
+ of <c>format_status/2</c> that returns a term describing the
+ current status of the gen_fsm.</p>
<p><c>PDict</c> is the current value of the gen_fsm's
process dictionary.</p>
<p><c>StateData</c> is the internal state data of the
gen_fsm.</p>
- <p>The function should return <c>Status</c>, a list of one or
- more terms that customise the details of the current state
- and status of the gen_fsm.</p>
+ <p>The function should return <c>Status</c>, a term that
+ customises the details of the current state and status of
+ the gen_fsm. There are no restrictions on the
+ form <c>Status</c> can take, but for
+ the <c>sys:get_status/1,2</c> case (when <c>Opt</c>
+ is <c>normal</c>), the recommended form for
+ the <c>Status</c> value is <c>[{data, [{"StateData",
+ Term}]}]</c> where <c>Term</c> provides relevant details of
+ the gen_fsm state data. Following this recommendation isn't
+ required, but doing so will make the callback module status
+ consistent with the rest of the <c>sys:get_status/1,2</c>
+ return value.</p>
+ <p>One use for this function is to return compact alternative
+ state data representations to avoid having large state terms
+ printed in logfiles.</p>
</desc>
</func>
</funcs>
@@ -770,4 +795,3 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<seealso marker="sys">sys(3)</seealso></p>
</section>
</erlref>
-
diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml
index 30c04d1d52..1045766e01 100644
--- a/lib/stdlib/doc/src/gen_server.xml
+++ b/lib/stdlib/doc/src/gen_server.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>gen_server</title>
@@ -599,32 +599,57 @@ gen_server:abcast -----> Module:handle_cast/2
</desc>
</func>
<func>
- <name>Module:format_status(normal, [PDict, State]) -> Status</name>
+ <name>Module:format_status(Opt, [PDict, State]) -> Status</name>
<fsummary>Optional function for providing a term describing the
current gen_server status.</fsummary>
<type>
+ <v>Opt = normal | terminate</v>
<v>PDict = [{Key, Value}]</v>
<v>State = term()</v>
- <v>Status = [term()]</v>
+ <v>Status = term()</v>
</type>
<desc>
- <p><em>This callback is optional, so callback modules need not
- export it. The gen_server module provides a default
- implementation of this function that returns the callback
- module state.</em></p>
- <p>This function is called by a gen_server process when one
+ <note>
+ <p>This callback is optional, so callback modules need not
+ export it. The gen_server module provides a default
+ implementation of this function that returns the callback
+ module state.</p>
+ </note>
+ <p>This function is called by a gen_server process when:</p>
+ <list typed="bulleted">
+ <item>One
of <seealso marker="sys#get_status/1">sys:get_status/1,2</seealso>
- is invoked to get the gen_server status. A callback module
- wishing to customise the <c>sys:get_status/1,2</c> return
- value exports an instance of <c>format_status/2</c> that
- returns a term describing the current status of the
- gen_server.</p>
+ is invoked to get the gen_server status. <c>Opt</c> is set
+ to the atom <c>normal</c> for this case.</item>
+ <item>The gen_server terminates abnormally and logs an
+ error. <c>Opt</c> is set to the atom <c>terminate</c> for this
+ case.</item>
+ </list>
+ <p>This function is useful for customising the form and
+ appearance of the gen_server status for these cases. A
+ callback module wishing to customise
+ the <c>sys:get_status/1,2</c> return value as well as how
+ its status appears in termination error logs exports an
+ instance of <c>format_status/2</c> that returns a term
+ describing the current status of the gen_server.</p>
<p><c>PDict</c> is the current value of the gen_server's
process dictionary.</p>
<p><c>State</c> is the internal state of the gen_server.</p>
- <p>The function should return <c>Status</c>, a list of one or
- more terms that customise the details of the current state
- and status of the gen_server.</p>
+ <p>The function should return <c>Status</c>, a term that
+ customises the details of the current state and status of
+ the gen_server. There are no restrictions on the
+ form <c>Status</c> can take, but for
+ the <c>sys:get_status/1,2</c> case (when <c>Opt</c>
+ is <c>normal</c>), the recommended form for
+ the <c>Status</c> value is <c>[{data, [{"State",
+ Term}]}]</c> where <c>Term</c> provides relevant details of
+ the gen_server state. Following this recommendation isn't
+ required, but doing so will make the callback module status
+ consistent with the rest of the <c>sys:get_status/1,2</c>
+ return value.</p>
+ <p>One use for this function is to return compact alternative
+ state representations to avoid having large state terms
+ printed in logfiles.</p>
</desc>
</func>
</funcs>
diff --git a/lib/stdlib/doc/src/io_protocol.xml b/lib/stdlib/doc/src/io_protocol.xml
index 201787f7b5..a97d996d98 100644
--- a/lib/stdlib/doc/src/io_protocol.xml
+++ b/lib/stdlib/doc/src/io_protocol.xml
@@ -79,7 +79,7 @@ sends the reply to.</item>
io_reply. The io-module in the Erlang standard library simply uses the pid()
of the io_server as the ReplyAs datum, but a more complicated client
could have several outstanding io-requests to the same server and
-would then use i.e. a ref() or something else to differentiate among
+would then use i.e. a reference() or something else to differentiate among
the incoming io_reply's. The ReplyAs element should be considered
opaque by the io_server. Note that the pid() of the server is not
explicitly present in the io_reply. The reply can be sent from any
@@ -195,7 +195,7 @@ latin1, Module, Function, Args} respectively. </p>
below).</p>
<p>The function will be called with the data the io_server finds on
- it's device, returning {done, Result, RestChars} when enough data is
+ its device, returning {done, Result, RestChars} when enough data is
read (in which case Result is sent to the client and RestChars are
kept in the io_server as a buffer for subsequent input) or {more,
Continuation}, indicating that more characters are needed to
@@ -741,7 +741,7 @@ optimize anything however. It is important though that the returned
data is of the right type depending on the options set, so we convert
the lists to binaries in the correct encoding <em>if possible</em>
before returning. The function supplied in the get_until request may,
-as it's final result return anything, so only functions actually
+as its final result return anything, so only functions actually
returning lists can get them converted to binaries. If the request
contained the encoding tag unicode, the lists can contain all unicode
codepoints and the binaries should be in UTF-8, if the encoding tag
diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml
index 855a7e0244..92c4eb4f4c 100644
--- a/lib/stdlib/doc/src/lists.xml
+++ b/lib/stdlib/doc/src/lists.xml
@@ -48,7 +48,7 @@
<item><p>if x <c>F</c> y and y <c>F</c> x then x = y (<c>F</c>
is antisymmetric);</p>
</item>
- <item><p>if x <c>F</c> y and and y <c>F</c> z then x <c>F</c> z
+ <item><p>if x <c>F</c> y and y <c>F</c> z then x <c>F</c> z
(<c>F</c> is transitive);</p>
</item>
<item><p>x <c>F</c> y or y <c>F</c> x (<c>F</c> is total).</p>
@@ -220,7 +220,7 @@
follows:</p>
<code type="none">
flatmap(Fun, List1) ->
- append(map(Fun, List1))</code>
+ append(map(Fun, List1)).</code>
<p>Example:</p>
<pre>
> <input>lists:flatmap(fun(X)->[X,X] end, [a,b,c]).</input>
@@ -443,7 +443,7 @@ flatmap(Fun, List1) ->
<desc>
<p>Returns a list containing the sorted elements of the list
<c>TupleList1</c>. Sorting is performed on the <c>N</c>th
- element of the tuples.</p>
+ element of the tuples. The sort is stable.</p>
</desc>
</func>
<func>
@@ -466,7 +466,7 @@ flatmap(Fun, List1) ->
</desc>
</func>
<func>
- <name>keytake(Key, N, TupleList1) -> {value, Tuple, TupleList2}
+ <name>keytake(Key, N, TupleList1) -> {value, Tuple, TupleList2}
| false</name>
<fsummary>Extract an element from a list of tuples</fsummary>
<type>
@@ -523,7 +523,7 @@ flatmap(Fun, List1) ->
<v>&nbsp;A = B = term()</v>
</type>
<desc>
- <p><c>mapfold</c> combines the operations of <c>map/2</c> and
+ <p><c>mapfoldl</c> combines the operations of <c>map/2</c> and
<c>foldl/3</c> into one pass. An example, summing
the elements in a list and double them at the same time:</p>
<pre>
@@ -543,7 +543,7 @@ flatmap(Fun, List1) ->
<v>&nbsp;A = B = term()</v>
</type>
<desc>
- <p><c>mapfold</c> combines the operations of <c>map/2</c> and
+ <p><c>mapfoldr</c> combines the operations of <c>map/2</c> and
<c>foldr/3</c> into one pass.</p>
</desc>
</func>
@@ -840,7 +840,7 @@ length(lists:seq(From, To, Incr)) == (To-From+Incr) div Incr</code>
<c>Pred</c>. <c>splitwith/2</c> behaves as if it is defined
as follows:</p>
<code type="none">
-splitwith(Pred, List) ->
+splitwith(Pred, List) ->
{takewhile(Pred, List), dropwhile(Pred, List)}.</code>
<p>Examples:</p>
<pre>
diff --git a/lib/stdlib/doc/src/ms_transform.xml b/lib/stdlib/doc/src/ms_transform.xml
index 9f178b426c..ba9f89685b 100644
--- a/lib/stdlib/doc/src/ms_transform.xml
+++ b/lib/stdlib/doc/src/ms_transform.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2002</year><year>2009</year>
+ <year>2002</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>ms_transform</title>
@@ -245,7 +245,7 @@ ets:select(emp_tab, ets:fun2ms(
fun(#emp{empno = [$0 | Rest] }) ->
{[$0|Rest],[$1|Rest]}
end)). </code>
- <p>As a matter of fact, this query hit's the feature of partially bound
+ <p>As a matter of fact, this query hits the feature of partially bound
keys in the table type <c>ordered_set</c>, so that not the whole
table need be searched, only the part of the table containing keys
beginning with <c>0</c> is in fact looked into. </p>
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index c55eafc8b8..940044d0a8 100644
--- a/lib/stdlib/doc/src/notes.xml
+++ b/lib/stdlib/doc/src/notes.xml
@@ -30,6 +30,539 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
+<section><title>STDLIB 1.17.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> When several clients accessed a Dets table
+ simultaneously, one of them calling
+ <c>dets:insert_new/2</c>, the Dets server could crash.
+ Alternatively, under the same conditions, <c>ok</c> was
+ sometimes returned instead of <c>true</c>. (Thanks to
+ John Hughes.) </p>
+ <p>
+ Own Id: OTP-8856</p>
+ </item>
+ <item>
+ <p> When several clients accessed a Dets table
+ simultaneously, inserted or updated objects were
+ sometimes lost due to the Dets file being truncated.
+ (Thanks to John Hughes.) </p>
+ <p>
+ Own Id: OTP-8898</p>
+ </item>
+ <item>
+ <p> When several clients accessed a Dets table
+ simultaneously, modifications of the Dets server's
+ internal state were sometimes thrown away. The symptoms
+ are diverse: error with reason <c>bad_object</c>;
+ inserted objects not returned by <c>lookup()</c>; et
+ cetera. (Thanks to John Hughes.) </p>
+ <p>
+ Own Id: OTP-8899</p>
+ </item>
+ <item>
+ <p> If a Dets table was closed after calling
+ <c>bchunk/2</c>, <c>match/1,3</c>,
+ <c>match_object/1,3</c>, or <c>select/1,3</c> and then
+ opened again, a subsequent call using the returned
+ continuation would normally return a reply. This bug has
+ fixed; now the call fails with reason <c>badarg</c>. </p>
+ <p>
+ Own Id: OTP-8903</p>
+ </item>
+ <item>
+ <p> Cover did not collect coverage data for files such as
+ Yecc parses containing include directives. The bug has
+ been fixed by modifying <c>epp</c>, the Erlang Code
+ Preprocessor. </p>
+ <p>
+ Own Id: OTP-8911</p>
+ </item>
+ <item>
+ <p> If a Dets table with fewer slots than keys was opened
+ and then closed after just a lookup, the contents were no
+ longer well-formed. This bug has been fixed. (Thanks to
+ Matthew Evans.) </p>
+ <p>
+ Own Id: OTP-8923</p>
+ </item>
+ <item>
+ <p>
+ In a supervisor, when it terminates a child, if that
+ child happens to have exited fractionally early, with
+ normal, the supervisor reports this as an error. This
+ should not be reported as an error.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8938 Aux Id: seq11615 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The documentation filelib:wildcard/1,2 now describes the
+ character set syntax for wildcards.</p>
+ <p>
+ Own Id: OTP-8879 Aux Id: seq11683 </p>
+ </item>
+ <item>
+ <p>Buffer overflows have been prevented in <c>erlc</c>,
+ <c>dialyzer</c>, <c>typer</c>, <c>run_test</c>,
+ <c>heart</c>, <c>escript</c>, and <c>erlexec</c>.</p>
+ (Thanks to Michael Santos.)
+ <p>
+ Own Id: OTP-8892</p>
+ </item>
+ <item>
+ <p>
+ Using a float for the number of copies for
+ <c>string:copies/2</c> resulted in an infinite loop. Now
+ it will fail with an exception instead. (Thanks to
+ Michael Santos.)</p>
+ <p>
+ Own Id: OTP-8915</p>
+ </item>
+ <item>
+ <p>
+ New ETS option <c>compressed</c>, to enable a more
+ compact storage format at the expence of heavier table
+ operations. For test and evaluation, <c>erl +ec</c> can
+ be used to force compression on all ETS tables.</p>
+ <p>
+ Own Id: OTP-8922 Aux Id: seq11658 </p>
+ </item>
+ <item>
+ <p> The default maximum number of slots of a Dets table
+ has been changed as to be equal to the maximum number of
+ slots. (Thanks to Richard Carlsson.) </p>
+ <p>
+ Own Id: OTP-8959</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>STDLIB 1.17.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>reference() has been substituted for ref() in the
+ documentation.</p>
+ <p>
+ Own Id: OTP-8733</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The ms_transform now warns if the fun head shadows
+ surrounding variables (just like the warnings you would
+ get for an ordinary fun in the same context).</p>
+ <p>
+ Own Id: OTP-6759</p>
+ </item>
+ <item>
+ <p>
+ ets:select_reverse/{1,2,3} are now documented.</p>
+ <p>
+ Own Id: OTP-7863</p>
+ </item>
+ <item>
+ <p>
+ Large parts of the <c>ethread</c> library have been
+ rewritten. The <c>ethread</c> library is an Erlang
+ runtime system internal, portable thread library used by
+ the runtime system itself.</p>
+ <p>
+ Most notable improvement is a reader optimized rwlock
+ implementation which dramatically improve the performance
+ of read-lock/read-unlock operations on multi processor
+ systems by avoiding ping-ponging of the rwlock cache
+ lines. The reader optimized rwlock implementation is used
+ by miscellaneous rwlocks in the runtime system that are
+ known to be read-locked frequently, and can be enabled on
+ ETS tables by passing the <seealso
+ marker="stdlib:ets#new_2_read_concurrency">{read_concurrency,
+ true}</seealso> option upon table creation. See the
+ documentation of <seealso
+ marker="stdlib:ets#new/2">ets:new/2</seealso> for more
+ information. The reader optimized rwlock implementation
+ can be fine tuned when starting the runtime system. For
+ more information, see the documentation of the <seealso
+ marker="erts:erl#+rg">+rg</seealso> command line argument
+ of <c>erl</c>.</p>
+ <p>
+ There is also a new implementation of rwlocks that is not
+ optimized for readers. Both implementations interleaves
+ readers and writers during contention as opposed to,
+ e.g., the NPTL (Linux) pthread rwlock implementation
+ which use either a reader or writer preferred strategy.
+ The reader/writer preferred strategy is problematic since
+ it starves threads doing the non-preferred operation.</p>
+ <p>
+ The new rwlock implementations in general performs better
+ in ERTS than common pthread implementations. However, in
+ some extremely heavily contended cases this is not the
+ case. Such heavy contention can more or less only appear
+ on ETS tables. This when multiple processes do very large
+ amounts of write locked operations simultaneously on the
+ same table. Such use of ETS is bad regardless of rwlock
+ implementation, will never scale, and is something we
+ strongly advise against.</p>
+ <p>
+ The new rwlock implementations depend on atomic
+ operations. If no native atomic implementation is found,
+ a fallback solution will be used. Using the fallback
+ implies a performance degradation. That is, it is more
+ important now than before to build OTP with a native
+ atomic implementation.</p>
+ <p>
+ The <c>ethread</c> library contains native atomic
+ implementations for, x86 (32 and 64 bit), powerpc (32
+ bit), sparc V9 (32 and 64 bit), and tilera (32 bit). On
+ other hardware gcc's builtin support for atomic memory
+ access will be used if such exists. If no such support is
+ found, <c>configure</c> will warn about no atomic
+ implementation available.</p>
+ <p>
+ The <c>ethread</c> library can now also use the
+ <c>libatomic_ops</c> library for atomic memory accesses.
+ This makes it possible for the Erlang runtime system to
+ utilize optimized native atomic operations on more
+ platforms than before. If <c>configure</c> warns about no
+ atomic implementation available, try using the
+ <c>libatomic_ops</c> library. Use the <seealso
+ marker="doc/installation_guide:INSTALL#How-to-Build-and-Install-ErlangOTP_A-Closer-Look-at-the-individual-Steps_Configuring">--with-libatomic_ops=PATH</seealso>
+ <c>configure</c> command line argument when specifying
+ where the <c>libatomic_ops</c> installation is located.
+ The <c>libatomic_ops</c> library can be downloaded from:
+ <url
+ href="http://www.hpl.hp.com/research/linux/atomic_ops/">http://www.hpl.hp.com/research/linux/atomic_ops/</url></p>
+ <p>
+ The changed API of the <c>ethread</c> library has also
+ caused modifications in the Erlang runtime system.
+ Preparations for the to come "delayed deallocation"
+ feature has also been done since it depends on the
+ <c>ethread</c> library.</p>
+ <p>
+ <em>Note</em>: When building for x86, the <c>ethread</c>
+ library will now use instructions that first appeared on
+ the pentium 4 processor. If you want the runtime system
+ to be compatible with older processors (back to 486) you
+ need to pass the <seealso
+ marker="doc/installation_guide:INSTALL#How-to-Build-and-Install-ErlangOTP_A-Closer-Look-at-the-individual-Steps_Configuring">--enable-ethread-pre-pentium4-compatibility</seealso>
+ <c>configure</c> command line argument when configuring
+ the system.</p>
+ <p>
+ Own Id: OTP-8544</p>
+ </item>
+ <item>
+ <p>
+ Some Built In Functions (BIFs) from the module erlang was
+ never made autoimported for backward compatibility
+ reasons. As local functions now override autoimports, new
+ autoimports is no longer a problem, why the following
+ BIFs are finally made autoimported: monitor/2, monitor/3,
+ demonitor/2, demonitor/3, error/1, error/2,
+ integer_to_list/2, list_to_integer/2.</p>
+ <p>
+ Own Id: OTP-8763</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>STDLIB 1.17</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>The Erlang code preprocessor (<c>epp</c>) sent extra
+ messages on the form <c>{eof,Location}</c> to the client
+ when parsing the <c>file</c> attribute. This bug,
+ introduced in R11B, has been fixed.</p>
+ <p>
+ Own Id: OTP-8470</p>
+ </item>
+ <item>
+ <p>The abstract type 'fun' could not be printed by the
+ Erlang pretty printer (<c>erl_pp</c>). This bug has been
+ fixed.</p>
+ <p>
+ Own Id: OTP-8473</p>
+ </item>
+ <item>
+ <p>The function <c>erl_scan:reserved_word/1</c> no longer
+ returns <c>true</c> when given the word <c>spec</c>. This
+ bug was introduced in STDLIB-1.15.3 (R12B-3).</p>
+ <p>
+ Own Id: OTP-8567</p>
+ </item>
+ <item>
+ <p>The documentation of <c>lists:keysort/2</c> states
+ that the sort is stable.</p>
+ <p>
+ Own Id: OTP-8628 Aux Id: seq11576 </p>
+ </item>
+ <item>
+ <p>
+ The shell's line editing has been improved to more
+ resemble the behaviour of readline and other shells.
+ (Thanks to Dave Peticolas)</p>
+ <p>
+ Own Id: OTP-8635</p>
+ </item>
+ <item>
+ <p>The Erlang code preprocessor (<c>epp</c>) did not
+ correctly handle premature end-of-input when defining
+ macros. This bug, introduced in STDLIB 1.16, has been
+ fixed.</p>
+ <p>
+ Own Id: OTP-8665 Aux Id: OTP-7810 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The module binary from EEP31 (and EEP9) is implemented.</p>
+ <p>
+ Own Id: OTP-8217</p>
+ </item>
+ <item>
+ <p>The erlang pretty printer (<c>erl_pp</c>) no longer
+ quotes atoms in types.</p>
+ <p>
+ Own Id: OTP-8501</p>
+ </item>
+ <item>
+ <p>The Erlang code preprocessor (<c>epp</c>) now
+ considers records with no fields as typed.</p>
+ <p>
+ Own Id: OTP-8503</p>
+ </item>
+ <item>
+ <p>
+ Added function <c>zip:foldl/3</c> to iterate over zip
+ archives.</p>
+ <p>
+ Added functions to create and extract escripts. See
+ <c>escript:create/2</c> and <c>escript:extract/2</c>.</p>
+ <p>
+ The undocumented function <c>escript:foldl/3</c> has been
+ removed. The same functionality can be achieved with the
+ more flexible functions <c>escript:extract/2</c> and
+ <c>zip:foldl/3</c>.</p>
+ <p>
+ Record fields has been annotated with type info. Source
+ files as been adapted to fit within 80 chars and trailing
+ whitespace has been removed.</p>
+ <p>
+ Own Id: OTP-8521</p>
+ </item>
+ <item>
+ <p>The Erlang parser no longer duplicates the singleton
+ type <c>undefined</c> in the type of record fields
+ without initial value.</p>
+ <p>
+ Own Id: OTP-8522</p>
+ </item>
+ <item>
+ <p>A regular expression with many levels of parenthesis
+ could cause a buffer overflow. That has been corrected.
+ (Thanks to Michael Santos.)</p>
+ <p>
+ Own Id: OTP-8539</p>
+ </item>
+ <item>
+ <p>When defining macros the closing right parenthesis
+ before the dot is now mandatory.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8562</p>
+ </item>
+ <item>
+ <p>
+ Some properties of a compiled re pattern are defined to
+ allow for guard tests.</p>
+ <p>
+ Own Id: OTP-8577</p>
+ </item>
+ <item>
+ <p>Local and imported functions now override the
+ auto-imported BIFs when the names clash. The pre R14
+ behaviour was that auto-imported BIFs would override
+ local functions. To avoid that old programs change
+ behaviour, the following will generate an error:</p>
+ <list><item><p>Doing a call without explicit module name
+ to a local function having a name clashing with the name
+ of an auto-imported BIF that was present (and
+ auto-imported) before OTP R14A</p></item>
+ <item><p>Explicitly importing a function having a name
+ clashing with the name of an autoimported BIF that was
+ present (and autoimported) before OTP R14A</p></item>
+ <item><p>Using any form of the old compiler directive
+ <c>nowarn_bif_clash</c></p></item> </list> <p>If the BIF
+ was added or auto-imported in OTP R14A or later,
+ overriding it with an import or a local function will
+ only result in a warning,</p> <p>To resolve clashes, you
+ can either use the explicit module name <c>erlang</c> to
+ call the BIF, or you can remove the auto-import of that
+ specific BIF by using the new compiler directive
+ <c>-compile({no_auto_import,[F/A]}).</c>, which makes all
+ calls to the local or imported function without explicit
+ module name pass without warnings or errors.</p> <p>The
+ change makes it possible to add auto-imported BIFs
+ without breaking or silently changing old code in the
+ future. However some current code ingeniously utilizing
+ the old behaviour or the <c>nowarn_bif_clash</c> compiler
+ directive, might need changing to be accepted by the
+ compiler.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8579</p>
+ </item>
+ <item>
+ <p>The undocumented, unsupport, and deprecated function
+ <c>lists:flat_length/1</c> has been removed.</p>
+ <p>
+ Own Id: OTP-8584</p>
+ </item>
+ <item>
+ <p>
+ A bug in re that could cause certain regular expression
+ matches never to terminate is corrected. (Thanks to
+ Michael Santos and Gordon Guthrie.)</p>
+ <p>
+ Own Id: OTP-8589</p>
+ </item>
+ <item>
+ <p>Nested records can now be accessed without
+ parenthesis. See the Reference Manual for examples.
+ (Thanks to YAMASHINA Hio and Tuncer Ayaz.)</p>
+ <p>
+ Own Id: OTP-8597</p>
+ </item>
+ <item>
+ <p><c>receive</c> statements that can only read out a
+ newly created reference are now specially optimized so
+ that it will execute in constant time regardless of the
+ number of messages in the receive queue for the process.
+ That optimization will benefit calls to
+ <c>gen_server:call()</c>. (See <c>gen:do_call/4</c> for
+ an example of a receive statement that will be
+ optimized.)</p>
+ <p>
+ Own Id: OTP-8623</p>
+ </item>
+ <item>
+ <p>The beam_lib:cmp/2 function now compares BEAM files in
+ stricter way. The BEAM files will be considered different
+ if there are any changes except in the compilation
+ information ("CInf") chunk. beam_lib:cmp/2 used to ignore
+ differences in the debug information (significant for
+ Dialyzer) and other chunks that did not directly change
+ the run-time behavior.</p>
+ <p>
+ Own Id: OTP-8625</p>
+ </item>
+ <item>
+ <p>
+ When a gen_server, gen_fsm process, or gen_event
+ terminates abnormally, sometimes the text representation
+ of the process state can occupy many lines of the error
+ log, depending on the definition of the state term. A
+ mechanism to trim out parts of the state from the log has
+ been added (using a format_status/2 callback). See the
+ documentation.</p>
+ <p>
+ Own Id: OTP-8630</p>
+ </item>
+ <item>
+ <p>
+ Calling <c>sys:get_status()</c> for processes that have
+ globally registered names that were not atoms would cause
+ a crash. Corrected. (Thanks to Steve Vinoski.)</p>
+ <p>
+ Own Id: OTP-8656</p>
+ </item>
+ <item>
+ <p>The Erlang scanner has been augmented with two new
+ tokens: <c>..</c> and <c>...</c>.</p>
+ <p>
+ Own Id: OTP-8657</p>
+ </item>
+ <item>
+ <p>Expressions evaluating to integers can now be used in
+ types and function specifications where hitherto only
+ integers were allowed ("Erlang_Integer").</p>
+ <p>
+ Own Id: OTP-8664</p>
+ </item>
+ <item>
+ <p>The compiler optimizes record operations better.</p>
+ <p>
+ Own Id: OTP-8668</p>
+ </item>
+ <item>
+ <p>
+ The recently added BIFs erlang:min/2, erlang:max/2 and
+ erlang:port_command/3 are now auto-imported (as they were
+ originally intended to be). Due to the recent compiler
+ change (OTP-8579), the only impact on old code defining
+ it's own min/2, max/2 or port_command/3 functions will be
+ a warning, the local functions will still be used. The
+ warning can be removed by using
+ -compile({no_auto_import,[min/2,max/2,port_command/3]}).
+ in the source file.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8669 Aux Id: OTP-8579 </p>
+ </item>
+ <item>
+ <p>
+ Now, binary_to_term/2 is auto-imported. This will cause a
+ compile warning if and only if a module has got a local
+ function with that name.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8671</p>
+ </item>
+ <item>
+ <p>
+ The predefined builtin type tid() has been removed.
+ Instead, ets:tid() should be used.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8687</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 1.16.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -50,7 +583,7 @@
<item>
<p>A number of bugs concerning re and unicode are
corrected:</p>
- <p>re:compile no longer looses unicode option, which also
+ <p>re:compile no longer loses unicode option, which also
fixes bug in re:split.</p>
<p>re:replace now handles unicode charlist replacement
argument</p>
diff --git a/lib/stdlib/doc/src/re.xml b/lib/stdlib/doc/src/re.xml
index 4d2a0e0995..056e7bc9b9 100644
--- a/lib/stdlib/doc/src/re.xml
+++ b/lib/stdlib/doc/src/re.xml
@@ -37,29 +37,24 @@
<modulesummary>Perl like regular expressions for Erlang</modulesummary>
<description>
- <p>This module contains functions for regular expression
- matching for strings and binaries.</p>
+ <p>This module contains regular expression matching functions for
+ strings and binaries.</p>
<p>The regular expression syntax and semantics resemble that of
- Perl. This library in many ways replaces the old regexp library
- written purely in Erlang, as it has a richer syntax as well as
- many more options. The library is also faster than the
- older regexp implementation.</p>
-
- <p>Although the library's matching algorithms are currently based
- on the PCRE library, it is not to be viewed as an Erlang to PCRE
- mapping. Only parts of the PCRE library is interfaced and the re
- library in some ways extend PCRE. The PCRE documentation contains
- many parts of no interest to the Erlang programmer, why only the
- relevant part of the documentation is included here. There should
- bee no need to go directly to the PCRE library documentation.</p>
+ Perl. This library replaces the deprecated pure-Erlang regexp
+ library; it has a richer syntax, more options and is faster.</p>
+
+ <p>The library's matching algorithms are currently based on the
+ PCRE library, but not all of the PCRE library is interfaced and
+ some parts of the library go beyond what PCRE offers. The sections of
+ the PCRE documentation which are relevant to this module are included
+ here.</p>
<note>
- <p>The Erlang literal syntax for strings give special
- meaning to the &quot;\&quot; (backslash) character. To literally write
- a regular expression or a replacement string containing a
- backslash in your code or in the shell, two backslashes have to be written:
- &quot;\\&quot;.</p>
+ <p>The Erlang literal syntax for strings uses the &quot;\&quot;
+ (backslash) character as an escape code. You need to escape
+ backslashes in literal strings, both in your code and in the shell,
+ with an additional backslash, i.e.: &quot;\\&quot;.</p>
</note>
@@ -72,7 +67,7 @@
- a binary is allowed as the tail of the list</code>
<code type="none">
unicode_binary() = binary() with characters encoded in UTF-8 coding standard
- unicode_char() = integer() representing valid unicode codepoint
+ unicode_char() = integer() representing a valid unicode codepoint
chardata() = charlist() | unicode_binary()
@@ -80,7 +75,11 @@
- a unicode_binary is allowed as the tail of the list</code>
<code type="none">
- mp() = Opaque datatype containing a compiled regular expression.</code>
+ mp() = Opaque datatype containing a compiled regular expression.
+ - The mp() is guaranteed to be a tuple() having the atom
+ 're_pattern' as its first element, to allow for matching in
+ guards. The arity of the tuple() or the content of the other fields
+ may change in future releases.</code>
</section>
<funcs>
<func>
@@ -128,7 +127,7 @@
<tag><c>dollar_endonly</c></tag>
<item>A dollar metacharacter in the pattern matches only at the end of the subject string. Without this option, a dollar also matches immediately before a newline at the end of the string (but not before any other newlines). The <c>dollar_endonly</c> option is ignored if <c>multiline</c> is given. There is no equivalent option in Perl, and no way to set it within a pattern.</item>
<tag><c>dotall</c></tag>
- <item>A dot maturate in the pattern matches all characters, including those that indicate newline. Without it, a dot does not match when the current position is at a newline. This option is equivalent to Perl's /s option, and it can be changed within a pattern by a (?s) option setting. A negative class such as [^a] always matches newline characters, independent of the setting of this option.</item>
+ <item>A dot in the pattern matches all characters, including those that indicate newline. Without it, a dot does not match when the current position is at a newline. This option is equivalent to Perl's /s option, and it can be changed within a pattern by a (?s) option setting. A negative class such as [^a] always matches newline characters, independent of this option's setting.</item>
<tag><c>extended</c></tag>
<item>Whitespace data characters in the pattern are ignored except when escaped or inside a character class. Whitespace does not include the VT character (ASCII 11). In addition, characters between an unescaped # outside a character class and the next newline, inclusive, are also ignored. This is equivalent to Perl's /x option, and it can be changed within a pattern by a (?x) option setting.
@@ -210,9 +209,10 @@ This option makes it possible to include comments inside complicated patterns. N
or as a pre compiled <c>mp()</c> in which case it is executed
against the subject directly.</p>
- <p>When compilation is involved, the exception <c>badarg</c> is thrown if
- a compilation error occurs. To locate the error in the regular
- expression, use the function <c>re:compile/2</c> to get more information.</p>
+ <p>When compilation is involved, the exception <c>badarg</c> is
+ thrown if a compilation error occurs. Call <c>re:compile/2</c>
+ to get information about the location of the error in the
+ regular expression.</p>
<p>If the regular expression is previously compiled, the option
list can only contain the options <c>anchored</c>,
@@ -242,7 +242,7 @@ This option makes it possible to include comments inside complicated patterns. N
how captured substrings are to be returned (as index tuples,
lists or binaries). The <c>capture</c> option makes the function
quite flexible and powerful. The different options are described
- in detail below</p>
+ in detail below.</p>
<p>If the capture options describe that no substring capturing
at all is to be done (<c>{capture, none}</c>), the function will
@@ -252,7 +252,7 @@ This option makes it possible to include comments inside complicated patterns. N
be done either by specifying <c>none</c> or an empty list as
<c>ValueSpec</c>.</p>
- <p>A description of all the options relevant for execution follows:</p>
+ <p>The options relevant for execution are:</p>
<taglist>
<tag><c>anchored</c></tag>
@@ -266,27 +266,25 @@ This option makes it possible to include comments inside complicated patterns. N
<tag><c>global</c></tag>
<item>
- <p>Implements global (repetitive) search as the <c>g</c> flag in
- i.e. Perl. Each match found is returned as a separate
+ <p>Implements global (repetitive) search (the <c>g</c> flag in
+ Perl). Each match is returned as a separate
<c>list()</c> containing the specific match as well as any
matching subexpressions (or as specified by the <c>capture
option</c>). The <c>Captured</c> part of the return value will
- hence be a <c>list()</c> of <c>list()</c>'s when this
+ hence be a <c>list()</c> of <c>list()</c>s when this
option is given.</p>
- <p>When the regular expression matches an empty string, the
- behaviour might seem non-intuitive, why the behaviour requites
- some clarifying. With the global option, <c>re:run/3</c>
- handles empty matches in the same way as Perl, meaning that a
- match at any point giving an empty string (with length 0) will
- be retried with the options
- <c>[anchored, notempty]</c> as well. If that
- search gives a result of length &gt; 0, the result is included.
- An example:</p>
+ <p>The interaction of the global option with a regular
+ expression which matches an empty string surprises some users.
+ When the global option is given, <c>re:run/3</c> handles empty
+ matches in the same way as Perl: a zero-length match at any
+ point will be retried with the options <c>[anchored,
+ notempty]</c> as well. If that search gives a result of length
+ &gt; 0, the result is included. For example:</p>
<code> re:run("cat","(|at)",[global]).</code>
- <p>The matching will be performed as following:</p>
+ <p>The following matching will be performed:</p>
<taglist>
<tag>At offset <c>0</c></tag>
<item>The regexp <c>(|at)</c> will first match at the initial
@@ -298,11 +296,11 @@ This option makes it possible to include comments inside complicated patterns. N
<item> The search is retried
with the options <c>[anchored, notempty]</c> at the same
position, which does not give any interesting result of longer
- length, why the search position is now advanced to the next
+ length, so the search position is now advanced to the next
character (<c>a</c>).</item>
<tag>At offset <c>1</c></tag>
- <item>Now the search results in
- <c>[{1,0},{1,0}]</c> meaning this search will also be repeated
+ <item>This time, the search results in
+ <c>[{1,0},{1,0}]</c>, so this search will also be repeated
with the extra options.</item>
<tag>At offset <c>1</c> with <c>[anchored, notempty]</c></tag>
<item>Now the <c>ab</c> alternative
@@ -329,16 +327,17 @@ This option makes it possible to include comments inside complicated patterns. N
entire match fails. For example, if the pattern</p>
<code> a?b?</code>
<p>is applied to a string not beginning with "a" or "b", it
- matches the empty string at the start of the subject. With
- <c>notempty</c> given, this match is not valid, so re:run/3 searches
- further into the string for occurrences of "a" or "b".</p>
+ would normally match the empty string at the start of the
+ subject. With the <c>notempty</c> option, this match is not
+ valid, so re:run/3 searches further into the string for
+ occurrences of "a" or "b".</p>
<p>Perl has no direct equivalent of <c>notempty</c>, but it does
make a special case of a pattern match of the empty string
within its split() function, and when using the /g modifier. It
is possible to emulate Perl's behavior after matching a null
string by first trying the match again at the same offset with
- <c>notempty</c> and <c>anchored</c>, and then if that fails by
+ <c>notempty</c> and <c>anchored</c>, and then, if that fails, by
advancing the starting offset (see below) and trying an ordinary
match again.</p>
</item>
@@ -348,7 +347,7 @@ This option makes it possible to include comments inside complicated patterns. N
string is not the beginning of a line, so the circumflex
metacharacter should not match before it. Setting this without
<c>multiline</c> (at compile time) causes circumflex never to
- match. This option affects only the behavior of the circumflex
+ match. This option only affects the behavior of the circumflex
metacharacter. It does not affect \A.</item>
<tag><c>noteol</c></tag>
@@ -384,7 +383,7 @@ This option makes it possible to include comments inside complicated patterns. N
</taglist>
</item>
<tag><c>bsr_anycrlf</c></tag>
- <item>Specifies specifically that \R is to match only the cr, lf or crlf sequences, not the Unicode specific newline characters.(overrides compilation option)</item>
+ <item>Specifies specifically that \R is to match only the cr, lf or crlf sequences, not the Unicode specific newline characters. (overrides compilation option)</item>
<tag><c>bsr_unicode</c></tag>
<item>Specifies specifically that \R is to match all the Unicode newline characters (including crlf etc, the default).(overrides compilation option)</item>
@@ -440,7 +439,7 @@ This option makes it possible to include comments inside complicated patterns. N
<tag><c>none</c></tag>
<item>Do not return matching subpatterns at all, yielding the single atom <c>match</c> as the return value of the function when matching successfully instead of the <c>{match, list()}</c> return. Specifying an empty list gives the same behavior.</item>
</taglist>
- <p>The value list is a list of indexes for the subpatterns to return, where index 0 is for all of the pattern, and 1 is for the first explicit capturing subpattern in the regular expression, and so forth. When using named captured subpatterns (see below) in the regular expression, one can use <c>atom()</c>'s or <c>string()</c>'s to specify the subpatterns to be returned. This deserves an example, consider the following regular expression:</p>
+ <p>The value list is a list of indexes for the subpatterns to return, where index 0 is for all of the pattern, and 1 is for the first explicit capturing subpattern in the regular expression, and so forth. When using named captured subpatterns (see below) in the regular expression, one can use <c>atom()</c>s or <c>string()</c>s to specify the subpatterns to be returned. For example, consider the regular expression:</p>
<code> ".*(abcd).*"</code>
<p>matched against the string ""ABCabcdABC", capturing only the "abcd" part (the first explicit subpattern):</p>
<code> re:run("ABCabcdABC",".*(abcd).*",[{capture,[1]}]).</code>
@@ -451,7 +450,7 @@ This option makes it possible to include comments inside complicated patterns. N
<code> ".*(?&lt;FOO&gt;abcd).*"</code>
<p>With this expression, we could still give the index of the subpattern with the following call:</p>
<code> re:run("ABCabcdABC",".*(?&lt;FOO&gt;abcd).*",[{capture,[1]}]).</code>
- <p>giving the same result as before. But as the subpattern is named, we can also give its name in the value list:</p>
+ <p>giving the same result as before. But, since the subpattern is named, we can also specify its name in the value list:</p>
<code> re:run("ABCabcdABC",".*(?&lt;FOO&gt;abcd).*",[{capture,['FOO']}]).</code>
<p>which would yield the same result as the earlier examples, namely:</p>
<code> {match,[{3,4}]}</code>
@@ -469,15 +468,15 @@ This option makes it possible to include comments inside complicated patterns. N
<item><p>Optionally specifies how captured substrings are to be returned. If omitted, the default of <c>index</c> is used. The <c>Type</c> can be one of the following:</p>
<taglist>
<tag><c>index</c></tag>
- <item>Return captured substrings as pairs of byte indexes into the subject string and length of the matching string in the subject (as if the subject string was flattened with <c>iolist_to_binary/1</c> or <c>unicode:characters_to_binary/2</c> prior to matching). Note that the <c>unicode</c> option results in <em>byte-oriented</em> indexes in a (possibly imagined) <em>UTF-8 encoded</em> binary. A byte index tuple <c>{0,2}</c> might therefore represent one or two characters when <c>unicode</c> is in effect. This might seem contra-intuitive, but has been deemed the most effective and useful way to way to do it. To return lists instead might result in simpler code if that is desired. This return type is the default.</item>
+ <item>Return captured substrings as pairs of byte indexes into the subject string and length of the matching string in the subject (as if the subject string was flattened with <c>iolist_to_binary/1</c> or <c>unicode:characters_to_binary/2</c> prior to matching). Note that the <c>unicode</c> option results in <em>byte-oriented</em> indexes in a (possibly virtual) <em>UTF-8 encoded</em> binary. A byte index tuple <c>{0,2}</c> might therefore represent one or two characters when <c>unicode</c> is in effect. This might seem counter-intuitive, but has been deemed the most effective and useful way to way to do it. To return lists instead might result in simpler code if that is desired. This return type is the default.</item>
<tag><c>list</c></tag>
- <item>Return matching substrings as lists of characters (Erlang <c>string()</c>'s). It the <c>unicode</c> option is used in combination with the \C sequence in the regular expression, a captured subpattern can contain bytes that has is not valid UTF-8 (\C matches bytes regardless of character encoding). In that case the <c>list</c> capturing may result in the same types of tuples that <c>unicode:characters_to_list/2</c> can return, namely three-tuples with the tag <c>incomplete</c> or <c>error</c>, the successfully converted characters and the invalid UTF-8 tail of the conversion as a binary. The best strategy is to avoid using the \C sequence when capturing lists.</item>
+ <item>Return matching substrings as lists of characters (Erlang <c>string()</c>s). It the <c>unicode</c> option is used in combination with the \C sequence in the regular expression, a captured subpattern can contain bytes that are not valid UTF-8 (\C matches bytes regardless of character encoding). In that case the <c>list</c> capturing may result in the same types of tuples that <c>unicode:characters_to_list/2</c> can return, namely three-tuples with the tag <c>incomplete</c> or <c>error</c>, the successfully converted characters and the invalid UTF-8 tail of the conversion as a binary. The best strategy is to avoid using the \C sequence when capturing lists.</item>
<tag><c>binary</c></tag>
- <item>Return matching substrings as binaries. If the <c>unicode</c> option is used, these binaries is in UTF-8. If the \C sequence is used together with <c>unicode</c> the binaries may be invalid UTF-8.</item>
+ <item>Return matching substrings as binaries. If the <c>unicode</c> option is used, these binaries are in UTF-8. If the \C sequence is used together with <c>unicode</c> the binaries may be invalid UTF-8.</item>
</taglist>
</item>
</taglist>
- <p>In general, subpatterns that got assigned no value in the match are returned as the tuple <c>{-1,0}</c> when <c>type</c> is <c>index</c>. Unassigned subpatterns are returned as the empty binary or list respectively for other return types. Consider the regular expression:</p>
+ <p>In general, subpatterns that were not assigned a value in the match are returned as the tuple <c>{-1,0}</c> when <c>type</c> is <c>index</c>. Unassigned subpatterns are returned as the empty binary or list, respectively, for other return types. Consider the regular expression:</p>
<code> ".*((?&lt;FOO&gt;abdd)|a(..d)).*"</code>
<p>There are three explicitly capturing subpatterns, where the opening parenthesis position determines the order in the result, hence <c>((?&lt;FOO&gt;abdd)|a(..d))</c> is subpattern index 1, <c>(?&lt;FOO&gt;abdd)</c> is subpattern index 2 and <c>(..d)</c> is subpattern index 3. When matched against the following string:</p>
<code> "ABCabcdABC"</code>
@@ -529,8 +528,8 @@ This option makes it possible to include comments inside complicated patterns. N
<v>NLSpec = cr | crlf | lf | anycrlf | any </v>
</type>
<desc>
- <p>Replaces the matched part of the <c>Subject</c> string with the content of <c>Replacement</c>.</p>
- <p>Options are given as to the <c>re:run/3</c> function except that the <c>capture</c> option of <c>re:run/3</c> is not allowed.
+ <p>Replaces the matched part of the <c>Subject</c> string with the contents of <c>Replacement</c>.</p>
+ <p>The permissible options are the same as for <c>re:run/3</c>, except that the <c>capture</c> option is not allowed.
Instead a <c>{return, ReturnType}</c> is present. The default return type is <c>iodata</c>, constructed in a
way to minimize copying. The <c>iodata</c> result can be used directly in many i/o-operations. If a flat <c>list()</c> is
desired, specify <c>{return, list}</c> and if a binary is preferred, specify <c>{return, binary}</c>.</p>
@@ -540,7 +539,7 @@ This option makes it possible to include comments inside complicated patterns. N
a Unicode <c>charlist()</c>. If compilation is done implicitly
and the <c>unicode</c> compilation option is given to this
function, both the regular expression and the <c>Subject</c>
- should be given as valid Unicode <c>charlist()</c>'s.</p>
+ should be given as valid Unicode <c>charlist()</c>s.</p>
<p>The replacement string can contain the special character
<c>&amp;</c>, which inserts the whole matching expression in the
@@ -550,7 +549,7 @@ This option makes it possible to include comments inside complicated patterns. N
generated by the regular expression, nothing is inserted.</p>
<p>To insert an <c>&amp;</c> or <c>\</c> in the result, precede it
with a <c>\</c>. Note that Erlang already gives a special
- meaning to <c>\</c> in literal strings, why a single <c>\</c>
+ meaning to <c>\</c> in literal strings, so a single <c>\</c>
has to be written as <c>"\\"</c> and therefore a double <c>\</c>
as <c>"\\\\"</c>. Example:</p>
<code> re:replace("abcd","c","[&amp;]",[{return,list}]).</code>
@@ -607,7 +606,7 @@ This option makes it possible to include comments inside complicated patterns. N
a Unicode <c>charlist()</c>. If compilation is done implicitly
and the <c>unicode</c> compilation option is given to this
function, both the regular expression and the <c>Subject</c>
- should be given as valid Unicode <c>charlist()</c>'s.</p>
+ should be given as valid Unicode <c>charlist()</c>s.</p>
<p>The result is given as a list of &quot;strings&quot;, the
preferred datatype given in the <c>return</c> option (default iodata).</p>
@@ -652,25 +651,25 @@ This option makes it possible to include comments inside complicated patterns. N
<p>Here the regular expression matched first the &quot;l&quot;,
causing &quot;Er&quot; to be the first part in the result. When
the regular expression matched, the (only) subexpression was
- bound to the &quot;l&quot;, why the &quot;l&quot; is inserted
+ bound to the &quot;l&quot;, so the &quot;l&quot; is inserted
in the group together with &quot;Er&quot;. The next match is of
the &quot;n&quot;, making &quot;a&quot; the next part to be
- returned. As the subexpression is bound to the substring
+ returned. Since the subexpression is bound to the substring
&quot;n&quot; in this case, the &quot;n&quot; is inserted into
this group. The last group consists of the rest of the string,
as no more matches are found.</p>
<p>By default, all parts of the string, including the empty
- strings are returned from the function. As an example:</p>
+ strings, are returned from the function. For example:</p>
<code> re:split("Erlang","[lg]",[{return,list}]).</code>
- <p>The result will be:</p>
+ <p>will return:</p>
<code> ["Er","an",[]]</code>
- <p>as the matching of the &quot;g&quot; in the end of the string
+ <p>since the matching of the &quot;g&quot; in the end of the string
leaves an empty rest which is also returned. This behaviour
differs from the default behaviour of the split function in
Perl, where empty strings at the end are by default removed. To
@@ -697,10 +696,10 @@ This option makes it possible to include comments inside complicated patterns. N
<p>Note that the last part is &quot;ang&quot;, not
&quot;an&quot;, as we only specified splitting into two parts,
- and the splitting stops when enough parts are given, why the
- result differs from that of <c>trim</c>.</p>
+ and the splitting stops when enough parts are given, which is
+ why the result differs from that of <c>trim</c>.</p>
- <p>More than three parts are not possible with this indata, why</p>
+ <p>More than three parts are not possible with this indata, so</p>
<code> re:split("Erlang","[lg]",[{return,list},{parts,4}]).</code>
@@ -741,7 +740,7 @@ This option makes it possible to include comments inside complicated patterns. N
the parts of the string matching the subexpressions of the
regexp.</p>
<p>The return value from the function will in this case be a
- <c>list()</c> of <c>list()</c>'s. Each sublist begins with the
+ <c>list()</c> of <c>list()</c>s. Each sublist begins with the
string picked out of the subject string, followed by the parts
matching each of the subexpressions in order of occurrence in the
regular expression.</p>
@@ -778,10 +777,8 @@ This option makes it possible to include comments inside complicated patterns. N
<title>PERL LIKE REGULAR EXPRESSIONS SYNTAX</title>
<p>The following sections contain reference material for the
regular expressions used by this module. The regular expression
- reference is taken from the PCRE documentation, but converted as
- needed.</p>
- <p>The documentation is altered where appropriate and where the re
- module behaves differently than the PCRE library.</p>
+ reference is based on the PCRE documentation, with changes in
+ cases where the re module behaves differently to the PCRE library.</p>
</section>
<section><title>PCRE regular expression details</title>
diff --git a/lib/stdlib/doc/src/ref_man.xml b/lib/stdlib/doc/src/ref_man.xml
index f6ae368e92..85aae6151d 100644
--- a/lib/stdlib/doc/src/ref_man.xml
+++ b/lib/stdlib/doc/src/ref_man.xml
@@ -4,7 +4,7 @@
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>STDLIB Reference Manual</title>
@@ -37,6 +37,7 @@
<xi:include href="array.xml"/>
<xi:include href="base64.xml"/>
<xi:include href="beam_lib.xml"/>
+ <xi:include href="binary.xml"/>
<xi:include href="c.xml"/>
<xi:include href="calendar.xml"/>
<xi:include href="dets.xml"/>
diff --git a/lib/stdlib/doc/src/sofs.xml b/lib/stdlib/doc/src/sofs.xml
index 8c8ae51262..729df1e678 100644
--- a/lib/stdlib/doc/src/sofs.xml
+++ b/lib/stdlib/doc/src/sofs.xml
@@ -210,7 +210,7 @@
X[i] to Y[i] and S a subset of
X[1]&nbsp;&times;&nbsp;...&nbsp;&times;&nbsp;X[n].
The <marker id="multiple_relative_product"></marker><em>multiple
- relative product</em> of TR and and S is defined to be the
+ relative product</em> of TR and S is defined to be the
set {z&nbsp;: z&nbsp;= ((x[1],&nbsp;...,&nbsp;x[n]), (y[1],...,y[n]))
for some (x[1],&nbsp;...,&nbsp;x[n])&nbsp;in&nbsp;S and for some
(x[i],&nbsp;y[i]) in R[i],
diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml
index c696434d49..45fa0847a8 100644
--- a/lib/stdlib/doc/src/supervisor.xml
+++ b/lib/stdlib/doc/src/supervisor.xml
@@ -156,7 +156,7 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
the child process to terminate by calling
<c>exit(Child,shutdown)</c> and then wait for an exit signal
with reason <c>shutdown</c> back from the child process. If
- no exit signal is received within the specified time,
+ no exit signal is received within the specified number of milliseconds,
the child process is unconditionally terminated using
<c>exit(Child,kill)</c>.</p>
<p>If the child process is another supervisor, <c>Shutdown</c>
diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml
index 10ead62073..8cbfb9387b 100644
--- a/lib/stdlib/doc/src/sys.xml
+++ b/lib/stdlib/doc/src/sys.xml
@@ -34,7 +34,7 @@
<module>sys</module>
<modulesummary>A Functional Interface to System Messages</modulesummary>
<description>
- <p>This module contains functions for sending system messages used by programs, and messaged used for debugging purposes.
+ <p>This module contains functions for sending system messages used by programs, and messages used for debugging purposes.
</p>
<p>Functions used for implementation of processes
should also understand system messages such as debugging
diff --git a/lib/stdlib/doc/src/timer.xml b/lib/stdlib/doc/src/timer.xml
index 0b6807dd6c..1b34e71490 100644
--- a/lib/stdlib/doc/src/timer.xml
+++ b/lib/stdlib/doc/src/timer.xml
@@ -202,18 +202,33 @@
</func>
<func>
<name>tc(Module, Function, Arguments) -> {Time, Value}</name>
- <fsummary>Measure the real time it takes to evaluate <c>apply(Module, Function, Arguments)</c></fsummary>
+ <name>tc(Fun, Arguments) -> {Time, Value}</name>
+ <fsummary>Measure the real time it takes to evaluate <c>apply(Module,
+ Function, Arguments)</c> or <c>apply(Fun, Arguments)</c></fsummary>
<type>
<v>Module = Function = atom()</v>
+ <v>Fun = fun()</v>
<v>Arguments = [term()]</v>
<v>Time = integer() in microseconds</v>
<v>Value = term()</v>
</type>
<desc>
- <p>Evaluates <c>apply(Module, Function, Arguments)</c> and measures
- the elapsed real time. Returns <c>{Time, Value}</c>, where
- <c>Time</c> is the elapsed real time in <em>microseconds</em>,
- and <c>Value</c> is what is returned from the apply.</p>
+ <p></p>
+ <taglist>
+ <tag><c>tc/3</c></tag>
+ <item>
+ <p>Evaluates <c>apply(Module, Function, Arguments)</c> and measures
+ the elapsed real time as reported by <c>now/0</c>.
+ Returns <c>{Time, Value}</c>, where
+ <c>Time</c> is the elapsed real time in <em>microseconds</em>,
+ and <c>Value</c> is what is returned from the apply.</p>
+ </item>
+ <tag><c>tc/2</c></tag>
+ <item>
+ <p>Evaluates <c>apply(Fun, Arguments)</c>. Otherwise works
+ like <c>tc/3</c>.</p>
+ </item>
+ </taglist>
</desc>
</func>
<func>
diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml
index c5bf10b63d..c02ea3cbcb 100644
--- a/lib/stdlib/doc/src/unicode_usage.xml
+++ b/lib/stdlib/doc/src/unicode_usage.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2009</year>
+ <year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -143,7 +143,7 @@ en_US.UTF-8</pre>
<pre>
$ echo <input>$LC_CTYPE</input>
en_US.UTF-8</pre>
-<p>The LANG or LC_CTYPE setting should be consistent with what the terminal is capable of, there is no portable way for Erlang to ask the actual terminal about it's UTF-8 capacity, we have to rely on the language and character type settings.</p>
+<p>The LANG or LC_CTYPE setting should be consistent with what the terminal is capable of, there is no portable way for Erlang to ask the actual terminal about its UTF-8 capacity, we have to rely on the language and character type settings.</p>
<p>To investigate what Erlang thinks about the terminal, the <c>io:getopts()</c> call can be used when the shell is started:</p>
<pre>
$ <input>LC_CTYPE=en_US.ISO-8859-1 erl</input>
@@ -168,6 +168,48 @@ Eshell V5.7 (abort with ^G)
<image file="ushell2.gif"><icaption>Unicode characters in allowed and disallowed context</icaption></image>
</section>
<section>
+<title>Unicode file names</title>
+<p>Most modern operating systems support Unicode file names in some way or another. There are several different ways to do this and Erlang by default treats the different approaches differently:</p>
+<taglist>
+<tag>Mandatory Unicode file naming</tag>
+<item>
+<p>Windows and, for most common uses, MacOSX enforces Unicode support for file names. All files created in the filesystem has names that can consistently be interpreted. In MacOSX, all file names are retrieved in UTF-8 encoding, while Windows have selected an approach where each system call handling file names has a special Unicode aware variant, giving much the same effect. There are no file names on these systems that are not Unicode file names, why the default behavior of the Erlang VM is to work in &quot;Unicode file name translation mode&quot;, meaning that a file name can be given as a Unicode list and that will be automatically translated to the proper name encoding for the underlying operating and file system.</p>
+<p>Doing i.e. a <c>file:list_dir/1</c> on one of these systems may return Unicode lists with codepoints beyond 255, depending on the content of the actual filesystem.</p>
+<p>As the feature is fairly new, you may still stumble upon non core applications that cannot handle being provided with file names containing characters with codepoints larger than 255, but the core Erlang system should have no problems with Unicode file names.</p>
+</item>
+<tag>Transparent file naming</tag>
+<item>
+<p>Most Unix operating systems have adopted a simpler approach, namely that Unicode file naming is not enforced, but by convention. Those systems usually use UTF-8 encoding for Unicode file names, but do not enforce it. On such a system, a file name containing characters having codepoints between 128 and 255 may be named either as plain ISO-latin-1 or using UTF-8 encoding. As no consistency is enforced, the Erlang VM can do no consistent translation of all file names. If the VM would automatically select encoding based on heuristics, one could get unexpected behavior on these systems, therefore file names not being encoded in UTF-8 are returned as &quot;raw file names&quot; if Unicode file naming support is turned on.</p>
+<p>A raw file name is not a list, but a binary. Many non core applications still does not handle file names given as binaries, why such raw names are avoided by default. This means that systems having implemented Unicode file naming through transparent file systems and an UTF-8 convention, does not by default have Unicode file naming turned on. Explicitly turning Unicode file name handling on for these types of systems is considered experimental.</p>
+</item>
+</taglist>
+<p>The Unicode file naming support was introduced with OTP release R14B01. A VM operating in Unicode file mode can work with files having names in any language or character set (as long as it's supported by the underlying OS and file system). The Unicode character list is used to denote file or directory names and if the file system content is listed, you will also be able to get Unicode lists as return value. The support lies in the kernel and stdlib modules, why most applications (that does not explicitly require the file names to be in the ISO-latin-1 range) will benefit from the Unicode support without change.</p>
+
+<p>On Operating systems with mandatory Unicode file names, this means that you more easily conform to the file names of other (non Erlang) applications, and you can also process file names that, at least on Windows, where completely inaccessible (due to having names that could not be represented in ISO-latin-1). Also you will avoid creating incomprehensible file names on MacOSX as the vfs layer of the OS will accept all your file names as UTF-8 and will not rewrite them.</p>
+
+<p>For most systems, turning on Unicode file name translation is no problem even if it uses transparent file naming. Very few systems have mixed file name encodings. A consistent UTF-8 named system will work perfectly in Unicode file name mode. It is still however considered experimental in R14B01. Unicode file name translation is turned on with the <c>+fnu</c> switch to the <c>erl</c> program. If the VM is started in Unicode file name translation mode, <c>file:native_name_encoding/0</c> will return the atom <c>utf8</c>.</p>
+
+<p>In Unicode file name mode, file names given to the BIF <c>open_port/2</c> with the option <c>{spawn_executable,...}</c> are also interpreted as Unicode. So is the parameter list given in the <c>args</c> option available when using <c>spawn_executable</c>. The UTF-8 translation of arguments can be avoided using binaries, see the discussion about raw file names below.</p>
+
+<p>It is worth noting that the file <c>encoding</c> options given when opening a file has nothing to do with the file <em>name</em> encoding convention. You can very well open files containing UTF-8 but having file names in ISO-latin-1 or vice versa.</p>
+
+<note>Erlang drivers and NIF shared objects still can not be named with names containing codepoints beyond 127. This is a known limitation to be removed in a future release. Erlang modules however can, but it is definitely not a good idea and is still considered experimental.</note>
+
+<section>
+<title>Notes about raw file names and automatic file name conversion</title>
+<p>Raw file names is introduced together with Unicode file name support in erts-5.8.2 (OTP R14B01). The reason the &quot;raw file names&quot; is introduced in the system is to be able to consistently represent file names given in different encodings on the same system. Having the VM automatically translate a file name that is not in UTF-8 when to a list of Unicode characters might seem practical, but this would open up for both duplicate file names and other inconsistent behavior. Consider a directory containing a file named &quot;bj�rn&quot; in ISO-latin-1, while the Erlang VM is operating in Unicode file name mode (and therefore expecting UTF-8 file naming). The ISO-latin-1 name is not valid UTF-8 and one could be tempted to think that automatic conversion in for example <c>file:list_dir/1</c> is a good idea. But what would happen if we later tried to open the file and has the name as a Unicode list (magically converted from the ISO-latin-1 file name)? The VM will convert the file name given to UTF-8, as this is the encoding expected. Effectively this means trying to open the file named &lt;&lt;&quot;bj�rn&quot;/utf8&gt;&gt;. This file does not exist, and even if it existed it would not be the same file as the one that was listed. We could even create two files named &quot;bj�rn&quot;, one named in the UTF-8 encoding and one not. If <c>file:list_dir/1</c> would automatically convert the ISO-latin-1 file name to a list, we would get two identical file names as the result. To avoid this, we need to differentiate between file names being properly encoded according to the Unicode file naming convention (i.e. UTF-8) and file names being invalid under the encoding. This is done by representing invalid encoding as &quot;raw&quot; file names, i.e. as binaries.</p>
+<p>The core system of Erlang (kernel and stdlib) accept raw file names except for loadable drivers and executables invoked using <c>open_port({spawn, ...} ...)</c>. <c>open_port({spawn_executable, ...} ...)</c> however does accept them. As mentioned earlier, the arguments given in the option list to <c>open_port({spawn_executable, ...} ...)</c> undergo the same conversion as the file names, meaning that the executable will be provided with arguments in UTF-8 as well. This translation is avoided consistently with how the file names are treated, by giving the argument as a binary.</p>
+<p>To force Unicode file name translation mode on systems where this is not the default is considered experimental in OTP R14B01 due to the raw file names possibly being a new experience to the programmer and that the non core applications of OTP are not tested for compliance with raw file names yet. Unicode file name translation is expected to be default in future releases.</p>
+<p>If working with raw file names, one can still conform to the encoding convention of the Erlang VM by using the <c>file:native_name_encoding/0</c> function, which returns either the atom <c>latin1</c> or the atom <c>utf8</c> depending on the file name translation mode. On Linux, an VM started without explicitly stating the file name translation mode will default to <c>latin1</c> as the native file name encoding, why file names on the disk encoded as UTF-8 will be returned as a list of the names interpreted as ISO-latin-1. The &quot;UTF-8 list&quot; is not a practical type for displaying or operating on in Erlang, but it is backward compatible and usable in all functions requiring a file name. On Windows and MacOSX, the default behavior is that of file name translation, why the <c>file:native_name_encoding/0</c> by default returns <c>utf8</c> on those systems (the fact that Windows actually does not use UTF-8 on the file system level can safely be ignored by the Erlang programmer). The default behavior can be changed using the <c>+fnu</c> or <c>+fnl</c> options to the VM, see the <c>erl</c> command manual page.</p>
+<p>Even if you are operating without Unicode file naming translation automatically done by the VM, you can access and create files with names in UTF-8 encoding by using raw file names encoded as UTF-8. Enforcing the UTF-8 encoding regardless of the mode the Erlang VM is started in might, in some circumstances be a good idea, as the convention of using UTF-8 file names is spreading.</p>
+</section>
+<section>
+<title>Notes about MacOSX</title>
+<p>MacOSXs vfs layer enforces UTF-8 file names in a quite aggressive way. Older versions did this by simply refusing to create non UTF-8 conforming file names, while newer versions replace offending bytes with the sequence &quot;%HH&quot;, where HH is the original character in hexadecimal notation. As Unicode translation is enabled by default on MacOSX, the only way to come up against this is to either start the VM with the <c>+fnl</c> flag or to use a raw file name in <c>latin1</c> encoding. In that case, the file can not be opened with the same name as the one used to create this. The problem is by design in newer versions of MacOSX.</p>
+<p>MacOSX also reorganizes the names of files so that the representation of accents etc is denormalized, i.e. the character <c>�</c> is represented as the codepoints [111,776], where 111 is the character <c>o</c> and 776 is a special accent character. This type of denormalized Unicode is otherwise very seldom used and Erlang normalizes those file names on retrieval, so that denormalized file names is not passed up to the Erlang application. In Erlang the file name &quot;bj�rn&quot; is retrieved as [98,106,246,114,110], not as [98,106,117,776,114,110], even though the file system might think differently.</p>
+</section>
+</section>
+<section>
<title>Unicode-aware modules</title>
<p>Most of the modules in Erlang/OTP are of course Unicode-unaware in the sense that they have no notion of Unicode and really shouldn't have. Typically they handle non-textual or byte-oriented data (like <c>gen_tcp</c> etc).</p>
<p>Modules that actually handle textual data (like <c>io_lib</c>, <c>string</c> etc) are sometimes subject to conversion or extension to be able to handle Unicode characters.</p>
@@ -185,7 +227,7 @@ Eshell V5.7 (abort with ^G)
<tag><c>file</c>, <c>group</c> and <c>user</c></tag>
<item>
<p>I/O-servers throughout the system are able both to handle Unicode data and has options for converting data upon actual output or input to/from the device. As shown earlier, the <seealso marker="stdlib:shell">shell</seealso> has support for Unicode terminals and the <seealso marker="kernel:file">file</seealso> module allows for translation to and from various Unicode formats on disk.</p>
-<p>The actual reading and writing of files with Unicode data is however not best done with the <c>file</c> module as it's interface is byte oriented. A file opened with a Unicode encoding (like UTF-8), is then best read or written using the <seealso marker="stdlib:io">io</seealso> module.</p>
+<p>The actual reading and writing of files with Unicode data is however not best done with the <c>file</c> module as its interface is byte oriented. A file opened with a Unicode encoding (like UTF-8), is then best read or written using the <seealso marker="stdlib:io">io</seealso> module.</p>
</item>
<tag><c>re</c></tag>
<item>
diff --git a/lib/stdlib/doc/src/zip.xml b/lib/stdlib/doc/src/zip.xml
index e2ecfec8f0..4d98a20206 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>2009</year>
+ <year>2006</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>zip</title>
@@ -42,16 +42,18 @@
<p>By convention, the name of a zip file should end in "<c>.zip</c>".
To abide to the convention, you'll need to add "<c>.zip</c>" yourself
to the name.</p>
- <p>Zip archives are created with the
- <seealso marker="#zip_2">zip/2</seealso> or the
+ <p>Zip archives are created with the
+ <seealso marker="#zip_2">zip/2</seealso> or the
<seealso marker="#zip_2">zip/3</seealso> function. (They are
also available as <c>create</c>, to resemble the <c>erl_tar</c>
module.)</p>
- <p>To extract files from a zip archive, use the
- <seealso marker="#unzip_1">unzip/1</seealso> or the
+ <p>To extract files from a zip archive, use the
+ <seealso marker="#unzip_1">unzip/1</seealso> or the
<seealso marker="#unzip_2">unzip/2</seealso> function. (They are
also available as <c>extract</c>.)</p>
- <p>To return a list of the files in a zip archive, use the
+ <p>To fold a function over all files in a zip archive, use the
+ <seealso marker="#foldl_3">foldl_3</seealso>.</p>
+ <p>To return a list of the files in a zip archive, use the
<seealso marker="#list_dir_1">list_dir/1</seealso> or the
<seealso marker="#list_dir_2">list_dir/2</seealso> function. (They
are also available as <c>table</c>.)</p>
@@ -132,7 +134,7 @@ zip_file() </code>
<type>
<v>Name = filename()</v>
<v>FileList = [FileSpec]</v>
- <v>FileSpec = filename() | {filename(), binary()}</v>
+ <v>FileSpec = filename() | {filename(), binary()} | {filename(), binary(), #file_info{}}</v>
<v>Options = [Option]</v>
<v>Option = memory | cooked | verbose | {comment, Comment} | {cwd, CWD} | {compress, What} | {uncompress, What}</v>
<v>What = all | [Extension] | {add, [Extension]} | {del, [Extension]}</v>
@@ -212,16 +214,16 @@ zip_file() </code>
<taglist>
<tag><c>all</c></tag>
<item><p> means that all files will be compressed (as long
- as they pass the <c>uncompress</c> condition).</p></item>
+ as they pass the <c>uncompress</c> condition).</p></item>
<tag><c>[Extension]</c></tag>
<item><p>means that only files with exactly these extensions
- will be compressed.</p></item>
+ will be compressed.</p></item>
<tag><c>{add,[Extension]}</c></tag>
<item><p>adds these extensions to the list of compress
- extensions.</p></item>
+ extensions.</p></item>
<tag><c>{del,[Extension]}</c></tag>
<item><p>deletes these extensions from the list of compress
- extensions.</p></item>
+ extensions.</p></item>
</taglist>
</item>
<tag><c>{uncompress, What}</c></tag>
@@ -231,16 +233,16 @@ zip_file() </code>
The following values of <c>What</c> are allowed:</p>
<taglist>
<tag><c>all</c></tag>
- <item><p> means that no files will be compressed.</p></item>
+ <item><p> means that no files will be compressed.</p></item>
<tag><c>[Extension]</c></tag>
<item><p>means that files with these extensions will be
- uncompressed.</p></item>
+ uncompressed.</p></item>
<tag><c>{add,[Extension]}</c></tag>
<item><p>adds these extensions to the list of uncompress
- extensions.</p></item>
+ extensions.</p></item>
<tag><c>{del,[Extension]}</c></tag>
<item><p>deletes these extensions from the list of uncompress
- extensions.</p></item>
+ extensions.</p></item>
</taglist>
</item>
</taglist>
@@ -283,7 +285,7 @@ zip_file() </code>
the <c>unzip/2</c> function will only extract the files
whose names are included in <c>FileList</c>. The full
paths, including the names of all sub directories within
- the zip archive, must be specified.</p>
+ the zip archive, must be specified.</p>
</item>
<tag><c>cooked</c></tag>
<item>
@@ -327,6 +329,64 @@ zip_file() </code>
</desc>
</func>
<func>
+ <name>foldl(Fun, Acc0, Archive) -> {ok, Acc1} | {error, Reason}</name>
+ <fsummary>Fold a function over all files in a zip archive</fsummary>
+ <type>
+ <v>Fun = fun(FileInArchive, GetInfo, GetBin, AccIn) -> AccOut</v>
+ <v>FileInArchive = filename()</v>
+ <v>GetInfo = fun() -> #file_info{}</v>
+ <v>GetBin = fun() -> binary()</v>
+ <v>Acc0 = Acc1 = AccIn = AccOut = term()</v>
+ <v>Archive = filename() | {filename(), binary()}</v>
+ </type>
+ <desc>
+ <p>The <marker id="foldl_3"></marker> <c>foldl/3</c> function
+ calls <c>Fun(FileInArchive, GetInfo, GetBin, AccIn)</c> on
+ successive files in the <c>Archive</c>, starting with <c>AccIn
+ == Acc0</c>. <c>FileInArchive</c> is the name that the file
+ has in the archive. <c>GetInfo</c> is a fun that returns info
+ about the the file. <c>GetBin</c> returns the contents of the
+ file. Both <c>GetInfo</c> and <c>GetBin</c> must be called
+ within the <c>Fun</c>. Their behavior is undefined if they are
+ called outside the context of the <c>Fun</c>. The <c>Fun</c>
+ must return a new accumulator which is passed to the next
+ call. <c>foldl/3</c> returns the final value of the
+ accumulator. <c>Acc0</c> is returned if the archive is
+ empty. It is not necessary to iterate over all files in the
+ archive. The iteration may be ended prematurely in a
+ controlled manner by throwing an exception.</p>
+
+ <p>For example:</p>
+ <pre>
+&gt; <input>Name = "dummy.zip".</input>
+"dummy.zip"
+&gt; <input>{ok, {Name, Bin}} = zip:create(Name, [{"foo", &lt;&lt;"FOO"&gt;&gt;}, {"bar", &lt;&lt;"BAR"&gt;&gt;}], [memory]).</input>
+{ok,{"dummy.zip",
+ &lt;&lt;80,75,3,4,20,0,0,0,0,0,74,152,97,60,171,39,212,26,3,0,
+ 0,0,3,0,0,...&gt;&gt;}}
+&gt; <input>{ok, FileSpec} = zip:foldl(fun(N, I, B, Acc) -> [{N, B(), I()} | Acc] end, [], {Name, Bin}).</input>
+{ok,[{"bar",&lt;&lt;"BAR"&gt;&gt;,
+ {file_info,3,regular,read_write,
+ {{2010,3,1},{19,2,10}},
+ {{2010,3,1},{19,2,10}},
+ {{2010,3,1},{19,2,10}},
+ 54,1,0,0,0,0,0}},
+ {"foo",&lt;&lt;"FOO"&gt;&gt;,
+ {file_info,3,regular,read_write,
+ {{2010,3,1},{19,2,10}},
+ {{2010,3,1},{19,2,10}},
+ {{2010,3,1},{19,2,10}},
+ 54,1,0,0,0,0,0}}]}
+&gt; <input>{ok, {Name, Bin}} = zip:create(Name, lists:reverse(FileSpec), [memory]).</input>
+{ok,{"dummy.zip",
+ &lt;&lt;80,75,3,4,20,0,0,0,0,0,74,152,97,60,171,39,212,26,3,0,
+ 0,0,3,0,0,...&gt;&gt;}}
+&gt; <input>catch zip:foldl(fun("foo", _, B, _) -> throw(B()); (_, _, _, Acc) -> Acc end, [], {Name, Bin}). </input>
+&lt;&lt;"FOO"&gt;&gt;
+</pre>
+ </desc>
+ </func>
+ <func>
<name>list_dir(Archive) -> RetValue</name>
<name>list_dir(Archive, Options)</name>
<name>table(Archive) -> RetValue</name>
diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile
index 237818c08b..600303d7e1 100644
--- a/lib/stdlib/src/Makefile
+++ b/lib/stdlib/src/Makefile
@@ -43,6 +43,7 @@ MODULES= \
array \
base64 \
beam_lib \
+ binary \
c \
calendar \
dets \
diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl
index 820afd3739..74d4ad3da7 100644
--- a/lib/stdlib/src/beam_lib.erl
+++ b/lib/stdlib/src/beam_lib.erl
@@ -1,24 +1,28 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(beam_lib).
-behaviour(gen_server).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
+%% Avoid warning for local function error/2 clashing with autoimported BIF.
+-compile({no_auto_import,[error/2]}).
-export([info/1,
cmp/2,
cmp_dirs/2,
@@ -41,12 +45,11 @@
terminate/2,code_change/3]).
-export([make_crypto_key/2, get_crypto_key/1]). %Utilities used by compiler
+-export_type([attrib_entry/0, compinfo_entry/0, labeled_entry/0]).
+
-import(lists, [append/1, delete/2, foreach/2, keysort/2,
member/2, reverse/1, sort/1, splitwith/2]).
--include_lib("kernel/include/file.hrl").
--include("erl_compile.hrl").
-
%%-------------------------------------------------------------------------
-type beam() :: module() | file:filename() | binary().
@@ -106,6 +109,7 @@
| info_rsn().
-type cmp_rsn() :: {'modules_different', module(), module()}
| {'chunks_different', chunkid()}
+ | 'different_chunks'
| info_rsn().
%%-------------------------------------------------------------------------
@@ -331,13 +335,11 @@ beam_files(Dir) ->
%% -> ok | throw(Error)
cmp_files(File1, File2) ->
- {ok, {M1, L1}} = read_significant_chunks(File1),
- {ok, {M2, L2}} = read_significant_chunks(File2),
+ {ok, {M1, L1}} = read_all_but_useless_chunks(File1),
+ {ok, {M2, L2}} = read_all_but_useless_chunks(File2),
if
M1 =:= M2 ->
- List1 = filter_funtab(L1),
- List2 = filter_funtab(L2),
- cmp_lists(List1, List2);
+ cmp_lists(L1, L2);
true ->
error({modules_different, M1, M2})
end.
@@ -408,6 +410,20 @@ pad(Size) ->
end.
%% -> {ok, {Module, Chunks}} | throw(Error)
+read_all_but_useless_chunks(File0) when is_atom(File0);
+ is_list(File0);
+ is_binary(File0) ->
+ File = beam_filename(File0),
+ {ok, Module, ChunkIds0} = scan_beam(File, info),
+ ChunkIds = [Name || {Name,_,_} <- ChunkIds0,
+ not is_useless_chunk(Name)],
+ {ok, Module, Chunks} = scan_beam(File, ChunkIds),
+ {ok, {Module, lists:reverse(Chunks)}}.
+
+is_useless_chunk("CInf") -> true;
+is_useless_chunk(_) -> false.
+
+%% -> {ok, {Module, Chunks}} | throw(Error)
read_significant_chunks(File) ->
case read_chunk_data(File, significant_chunks(), [allow_missing_chunks]) of
{ok, {Module, Chunks0}} ->
diff --git a/lib/stdlib/src/binary.erl b/lib/stdlib/src/binary.erl
new file mode 100644
index 0000000000..f6489788b2
--- /dev/null
+++ b/lib/stdlib/src/binary.erl
@@ -0,0 +1,177 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(binary).
+%%
+%% The following functions implemented as BIF's
+%% binary:compile_pattern/1
+%% binary:match/{2,3}
+%% binary:matches/{2,3}
+%% binary:longest_common_prefix/1
+%% binary:longest_common_suffix/1
+%% binary:first/1
+%% binary:last/1
+%% binary:at/2
+%% binary:part/{2,3}
+%% binary:bin_to_list/{1,2,3}
+%% binary:list_to_bin/1
+%% binary:copy/{1,2}
+%% binary:referenced_byte_size/1
+%% binary:decode_unsigned/{1,2}
+%% - Not yet:
+%%
+%% Implemented in this module:
+-export([split/2,split/3,replace/3,replace/4]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% split
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+split(H,N) ->
+ split(H,N,[]).
+split(Haystack,Needles,Options) ->
+ try
+ {Part,Global,Trim} = get_opts_split(Options,{no,false,false}),
+ Moptlist = case Part of
+ no ->
+ [];
+ {A,B} ->
+ [{scope,{A,B}}]
+ end,
+ MList = if
+ Global ->
+ binary:matches(Haystack,Needles,Moptlist);
+ true ->
+ case binary:match(Haystack,Needles,Moptlist) of
+ nomatch -> [];
+ Match -> [Match]
+ end
+ end,
+ do_split(Haystack,MList,0,Trim)
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+do_split(H,[],N,true) when N >= byte_size(H) ->
+ [];
+do_split(H,[],N,_) ->
+ [binary:part(H,{N,byte_size(H)-N})];
+do_split(H,[{A,B}|T],N,Trim) ->
+ case binary:part(H,{N,A-N}) of
+ <<>> ->
+ Rest = do_split(H,T,A+B,Trim),
+ case {Trim, Rest} of
+ {true,[]} ->
+ [];
+ _ ->
+ [<<>> | Rest]
+ end;
+ Oth ->
+ [Oth | do_split(H,T,A+B,Trim)]
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% replace
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+replace(H,N,R) ->
+ replace(H,N,R,[]).
+replace(Haystack,Needles,Replacement,Options) ->
+ try
+ true = is_binary(Replacement), % Make badarg instead of function clause
+ {Part,Global,Insert} = get_opts_replace(Options,{no,false,[]}),
+ Moptlist = case Part of
+ no ->
+ [];
+ {A,B} ->
+ [{scope,{A,B}}]
+ end,
+ MList = if
+ Global ->
+ binary:matches(Haystack,Needles,Moptlist);
+ true ->
+ case binary:match(Haystack,Needles,Moptlist) of
+ nomatch -> [];
+ Match -> [Match]
+ end
+ end,
+ ReplList = case Insert of
+ [] ->
+ Replacement;
+ Y when is_integer(Y) ->
+ splitat(Replacement,0,[Y]);
+ Li when is_list(Li) ->
+ splitat(Replacement,0,lists:sort(Li))
+ end,
+ erlang:iolist_to_binary(do_replace(Haystack,MList,ReplList,0))
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+
+do_replace(H,[],_,N) ->
+ [binary:part(H,{N,byte_size(H)-N})];
+do_replace(H,[{A,B}|T],Replacement,N) ->
+ [binary:part(H,{N,A-N}),
+ if
+ is_list(Replacement) ->
+ do_insert(Replacement, binary:part(H,{A,B}));
+ true ->
+ Replacement
+ end
+ | do_replace(H,T,Replacement,A+B)].
+
+do_insert([X],_) ->
+ [X];
+do_insert([H|T],R) ->
+ [H,R|do_insert(T,R)].
+
+splitat(H,N,[]) ->
+ [binary:part(H,{N,byte_size(H)-N})];
+splitat(H,N,[I|T]) ->
+ [binary:part(H,{N,I-N})|splitat(H,I,T)].
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Simple helper functions
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+get_opts_split([],{Part,Global,Trim}) ->
+ {Part,Global,Trim};
+get_opts_split([{scope,{A,B}} | T],{_Part,Global,Trim}) ->
+ get_opts_split(T,{{A,B},Global,Trim});
+get_opts_split([global | T],{Part,_Global,Trim}) ->
+ get_opts_split(T,{Part,true,Trim});
+get_opts_split([trim | T],{Part,Global,_Trim}) ->
+ get_opts_split(T,{Part,Global,true});
+get_opts_split(_,_) ->
+ throw(badopt).
+
+get_opts_replace([],{Part,Global,Insert}) ->
+ {Part,Global,Insert};
+get_opts_replace([{scope,{A,B}} | T],{_Part,Global,Insert}) ->
+ get_opts_replace(T,{{A,B},Global,Insert});
+get_opts_replace([global | T],{Part,_Global,Insert}) ->
+ get_opts_replace(T,{Part,true,Insert});
+get_opts_replace([{insert_replaced,N} | T],{Part,Global,_Insert}) ->
+ get_opts_replace(T,{Part,Global,N});
+get_opts_replace(_,_) ->
+ throw(badopt).
+
diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl
index 433833e233..d04d8f191f 100644
--- a/lib/stdlib/src/c.erl
+++ b/lib/stdlib/src/c.erl
@@ -1,25 +1,27 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(c).
%% Utilities to use from shell.
+%% Avoid warning for local function error/2 clashing with autoimported BIF.
+-compile({no_auto_import,[error/2]}).
-export([help/0,lc/1,c/1,c/2,nc/1,nc/2, nl/1,l/1,i/0,i/1,ni/0,
y/1, y/2,
lc_batch/0, lc_batch/1,
@@ -31,10 +33,14 @@
-export([display_info/1]).
-export([appcall/4]).
--import(lists, [reverse/1,flatten/1,sublist/3,sort/1,keysearch/3,keysort/2,
+-import(lists, [reverse/1,flatten/1,sublist/3,sort/1,keysort/2,
concat/1,max/1,min/1,foreach/2,foldl/3,flatmap/2]).
-import(io, [format/1, format/2]).
+%%-----------------------------------------------------------------------
+
+-spec help() -> 'ok'.
+
help() ->
format("bt(Pid) -- stack backtrace for a process\n"
"c(File) -- compile and load code in <File>\n"
@@ -65,8 +71,12 @@ help() ->
%% c(FileName)
%% Compile a file/module.
+-spec c(file:name()) -> {'ok', module()} | 'error'.
+
c(File) -> c(File, []).
+-spec c(file:name(), [compile:option()]) -> {'ok', module()} | 'error'.
+
c(File, Opts0) when is_list(Opts0) ->
Opts = [report_errors,report_warnings|Opts0],
case compile:file(File, Opts) of
@@ -82,6 +92,8 @@ c(File, Opt) ->
%%% Obtain the 'outdir' option from the argument. Return "." if no
%%% such option was given.
+-spec outdir([compile:option()]) -> file:filename().
+
outdir([]) ->
".";
outdir([Opt|Rest]) ->
@@ -118,8 +130,8 @@ machine_load(Mod, File, Opts) ->
%%% loaded from some other place than current directory.
%%% Now, loading from other than current directory is supposed to work.
%%% so this function does nothing special.
-check_load({error, R}, _) -> {error, R};
-check_load(_, X) -> {ok, X}.
+check_load({error, _R} = Error, _) -> Error;
+check_load(_, Mod) -> {ok, Mod}.
%% Compile a list of modules
%% enables the nice unix shell cmd
@@ -128,6 +140,8 @@ check_load(_, X) -> {ok, X}.
%% with constant c2 defined, c1=v1 (v1 must be a term!), include dir
%% IDir, outdir ODir.
+-spec lc([erl_compile:cmd_line_arg()]) -> 'ok' | 'error'.
+
lc(Args) ->
case catch split(Args, [], []) of
error -> error;
@@ -145,7 +159,7 @@ lc_batch() ->
io:format("Error: no files to compile~n"),
halt(1).
--spec lc_batch([_]) -> no_return().
+-spec lc_batch([erl_compile:cmd_line_arg()]) -> no_return().
lc_batch(Args) ->
try split(Args, [], []) of
@@ -191,8 +205,13 @@ make_term(Str) ->
throw(error)
end.
+-spec nc(file:name()) -> {'ok', module()} | 'error'.
+
nc(File) -> nc(File, []).
+-spec nc(file:name(), [compile:option()] | compile:option()) ->
+ {'ok', module} | 'error'.
+
nc(File, Opts0) when is_list(Opts0) ->
Opts = Opts0 ++ [report_errors, report_warnings],
case compile:file(File, Opts) of
@@ -215,26 +234,37 @@ nc(File, Opt) when is_atom(Opt) ->
%% l(Mod)
%% Reload module Mod from file of same name
+-spec l(module()) -> code:load_ret().
l(Mod) ->
code:purge(Mod),
code:load_file(Mod).
%% Network version of l/1
+%%-spec nl(module()) ->
nl(Mod) ->
case code:get_object_code(Mod) of
{_Module, Bin, Fname} ->
- rpc:eval_everywhere(code,load_binary,[Mod,Fname,Bin]);
+ rpc:eval_everywhere(code, load_binary, [Mod, Fname, Bin]);
Other ->
Other
end.
+-spec i() -> 'ok'.
+
i() -> i(processes()).
+
+-spec ni() -> 'ok'.
+
ni() -> i(all_procs()).
+-spec i([pid()]) -> 'ok'.
+
i(Ps) ->
i(Ps, length(Ps)).
+-spec i([pid()], non_neg_integer()) -> 'ok'.
+
i(Ps, N) when N =< 100 ->
iformat("Pid", "Initial Call", "Heap", "Reds",
"Msgs"),
@@ -275,7 +305,6 @@ paged_i(Ps, Acc, N, Page) ->
paged_i([], NewAcc, 0, Page)
end.
-
choice(F) ->
case get_line('(c)ontinue (q)uit -->', "c\n") of
"c\n" ->
@@ -285,7 +314,6 @@ choice(F) ->
_ ->
choice(F)
end.
-
get_line(P, Default) ->
case io:get_line(P) of
@@ -305,7 +333,6 @@ mfa_string({M,F,A}) ->
mfa_string(X) ->
w(X).
-
display_info(Pid) ->
case pinfo(Pid) of
undefined -> {0,0,0,0};
@@ -317,7 +344,7 @@ display_info(Pid) ->
Other ->
Other
end,
- Reds = fetch(reductions, Info),
+ Reds = fetch(reductions, Info),
LM = length(fetch(messages, Info)),
HS = fetch(heap_size, Info),
SS = fetch(stack_size, Info),
@@ -364,21 +391,30 @@ pinfo(Pid) ->
end.
fetch(Key, Info) ->
- case keysearch(Key, 1, Info) of
- {value, {_, Val}} -> Val;
+ case lists:keyfind(Key, 1, Info) of
+ {_, Val} -> Val;
false -> 0
end.
-pid(X,Y,Z) ->
+-spec pid(non_neg_integer(), non_neg_integer(), non_neg_integer()) -> pid().
+
+pid(X, Y, Z) ->
list_to_pid("<" ++ integer_to_list(X) ++ "." ++
integer_to_list(Y) ++ "." ++
integer_to_list(Z) ++ ">").
-i(X,Y,Z) -> pinfo(pid(X,Y,Z)).
+-spec i(non_neg_integer(), non_neg_integer(), non_neg_integer()) ->
+ [{atom(), term()}].
+
+i(X, Y, Z) -> pinfo(pid(X, Y, Z)).
+
+-spec q() -> no_return().
q() ->
init:stop().
+-spec bt(pid()) -> 'ok' | 'undefined'.
+
bt(Pid) ->
case catch erlang:process_display(Pid, backtrace) of
{'EXIT', _} ->
@@ -387,6 +423,8 @@ bt(Pid) ->
ok
end.
+-spec m() -> 'ok'.
+
m() ->
mformat("Module", "File"),
foreach(fun ({Mod,File}) -> mformat(Mod, File) end, sort(code:all_loaded())).
@@ -414,8 +452,8 @@ error(Fmt, Args) ->
f_p_e(P, F) ->
case file:path_eval(P, F) of
- {error, enoent} ->
- {error, enoent};
+ {error, enoent} = Enoent ->
+ Enoent;
{error, E={Line, _Mod, _Term}} ->
error("file:path_eval(~p,~p): error on line ~p: ~s~n",
[P, F, Line, file:format_error(E)]),
@@ -438,10 +476,11 @@ bi(I) ->
%%
%% Short and nice form of module info
%%
+-spec m(module()) -> 'ok'.
m(M) ->
L = M:module_info(),
- {value,{exports,E}} = keysearch(exports, 1, L),
+ {exports,E} = lists:keyfind(exports, 1, L),
Time = get_compile_time(L),
COpts = get_compile_options(L),
format("Module ~w compiled: ",[M]), print_time(Time),
@@ -470,10 +509,10 @@ get_compile_options(L) ->
end.
get_compile_info(L, Tag) ->
- case keysearch(compile, 1, L) of
- {value, {compile, I}} ->
- case keysearch(Tag, 1, I) of
- {value, {Tag, Val}} -> {ok,Val};
+ case lists:keyfind(compile, 1, L) of
+ {compile, I} ->
+ case lists:keyfind(Tag, 1, I) of
+ {Tag, Val} -> {ok,Val};
false -> error
end;
false -> error
@@ -523,6 +562,8 @@ month(11) -> "November";
month(12) -> "December".
%% Just because we can't eval receive statements...
+-spec flush() -> 'ok'.
+
flush() ->
receive
X ->
@@ -533,9 +574,13 @@ flush() ->
end.
%% Print formatted info about all registered names in the system
+-spec nregs() -> 'ok'.
+
nregs() ->
foreach(fun (N) -> print_node_regs(N) end, all_regs()).
+-spec regs() -> 'ok'.
+
regs() ->
print_node_regs({node(),registered()}).
@@ -609,14 +654,18 @@ portformat(Name, Id, Cmd) ->
%% cd(Directory)
%% These are just wrappers around the file:get/set_cwd functions.
+-spec pwd() -> 'ok'.
+
pwd() ->
case file:get_cwd() of
{ok, Str} ->
- ok = io:format("~s\n", [Str]);
+ ok = io:format("~ts\n", [fixup_one_bin(Str)]);
{error, _} ->
ok = io:format("Cannot determine current directory\n")
end.
+-spec cd(file:name()) -> 'ok'.
+
cd(Dir) ->
file:set_cwd(Dir),
pwd().
@@ -625,17 +674,37 @@ cd(Dir) ->
%% ls(Directory)
%% The strategy is to print in fixed width files.
+-spec ls() -> 'ok'.
+
ls() ->
ls(".").
+-spec ls(file:name()) -> 'ok'.
+
ls(Dir) ->
case file:list_dir(Dir) of
{ok, Entries} ->
- ls_print(sort(Entries));
+ ls_print(sort(fixup_bin(Entries)));
{error,_E} ->
format("Invalid directory\n")
end.
+fixup_one_bin(X) when is_binary(X) ->
+ L = binary_to_list(X),
+ [ if
+ El > 127 ->
+ $?;
+ true ->
+ El
+ end || El <- L];
+fixup_one_bin(X) ->
+ X.
+fixup_bin([H|T]) ->
+ [fixup_one_bin(H) | fixup_bin(T)];
+fixup_bin([]) ->
+ [].
+
+
ls_print([]) -> ok;
ls_print(L) ->
Width = min([max(lengths(L, [])), 40]) + 5,
@@ -645,7 +714,7 @@ ls_print(X, Width, Len) when Width + Len >= 80 ->
io:nl(),
ls_print(X, Width, 0);
ls_print([H|T], Width, Len) ->
- io:format("~-*s",[Width,H]),
+ io:format("~-*ts",[Width,H]),
ls_print(T, Width, Len+Width);
ls_print([], _, _) ->
io:nl().
@@ -660,24 +729,31 @@ w(X) ->
%% memory/[0,1]
%%
-memory() -> erlang:memory().
+-spec memory() -> [{atom(), non_neg_integer()}].
+
+memory() -> erlang:memory().
+
+-spec memory(atom()) -> non_neg_integer()
+ ; ([atom()]) -> [{atom(), non_neg_integer()}].
+
memory(TypeSpec) -> erlang:memory(TypeSpec).
%%
%% Cross Reference Check
%%
-
+%%-spec xm(module() | file:filename()) -> xref:m/1 return
xm(M) ->
appcall(tools, xref, m, [M]).
%%
%% Call yecc
%%
-
+%%-spec y(file:name()) -> yecc:file/2 return
y(File) -> y(File, []).
+%%-spec y(file:name(), [yecc:option()]) -> yecc:file/2 return
y(File, Opts) ->
- appcall(parsetools, yecc, file, [File,Opts]).
+ appcall(parsetools, yecc, file, [File, Opts]).
%%
@@ -699,4 +775,3 @@ appcall(App, M, F, Args) ->
erlang:raise(error, undef, Stk)
end
end.
-
diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl
index 7f1c13770b..6c91f1efb7 100644
--- a/lib/stdlib/src/dets.erl
+++ b/lib/stdlib/src/dets.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(dets).
@@ -88,6 +88,7 @@
%% Not documented, or not ready for publication.
-export([lookup_keys/2]).
+-export_type([tab_name/0]).
-compile({inline, [{einval,2},{badarg,2},{undefined,1},
{badarg_exit,2},{lookup_reply,2}]}).
@@ -146,6 +147,7 @@
bin, % small chunk not consumed, or 'eof' at end-of-file
alloc, % the part of the file not yet scanned, mostly a binary
tab,
+ proc, % the pid of the Dets process
match_program % true | compiled_match_spec() | undefined
}).
@@ -207,8 +209,6 @@ all() ->
bchunk(Tab, start) ->
badarg(treq(Tab, {bchunk_init, Tab}), [Tab, start]);
-bchunk(Tab, #dets_cont{bin = eof, tab = Tab}) ->
- '$end_of_table';
bchunk(Tab, #dets_cont{what = bchunk, tab = Tab} = State) ->
badarg(treq(Tab, {bchunk, State}), [Tab, State]);
bchunk(Tab, Term) ->
@@ -721,11 +721,14 @@ init_chunk_match(Tab, Pat, What, N) when is_integer(N), N >= 0;
N =:= default ->
case compile_match_spec(What, Pat) of
{Spec, MP} ->
- case req(dets_server:get_pid(Tab), {match, MP, Spec, N}) of
+ Proc = dets_server:get_pid(Tab),
+ case req(Proc, {match, MP, Spec, N}) of
{done, L} ->
- {L, #dets_cont{tab = Tab, what = What, bin = eof}};
+ {L, #dets_cont{tab = Tab, proc = Proc, what = What,
+ bin = eof}};
{cont, State} ->
- chunk_match(State#dets_cont{what = What, tab = Tab});
+ chunk_match(State#dets_cont{what = What, tab = Tab,
+ proc = Proc});
Error ->
Error
end;
@@ -735,34 +738,28 @@ init_chunk_match(Tab, Pat, What, N) when is_integer(N), N >= 0;
init_chunk_match(_Tab, _Pat, _What, _) ->
badarg.
-chunk_match(State) ->
- case catch dets_server:get_pid(State#dets_cont.tab) of
- {'EXIT', _Reason} ->
- badarg;
- _Proc when State#dets_cont.bin =:= eof ->
- '$end_of_table';
- Proc ->
- case req(Proc, {match_init, State}) of
- {cont, {Bins, NewState}} ->
- MP = NewState#dets_cont.match_program,
- case catch do_foldl_bins(Bins, MP) of
- {'EXIT', _} ->
- case ets:is_compiled_ms(MP) of
- true ->
- Bad = dets_utils:bad_object(chunk_match,
- Bins),
- req(Proc, {corrupt, Bad});
- false ->
- badarg
- end;
- [] ->
- chunk_match(NewState);
- Terms ->
- {Terms, NewState}
- end;
- Error ->
- Error
- end
+chunk_match(#dets_cont{proc = Proc}=State) ->
+ case req(Proc, {match_init, State}) of
+ '$end_of_table'=Reply ->
+ Reply;
+ {cont, {Bins, NewState}} ->
+ MP = NewState#dets_cont.match_program,
+ case catch do_foldl_bins(Bins, MP) of
+ {'EXIT', _} ->
+ case ets:is_compiled_ms(MP) of
+ true ->
+ Bad = dets_utils:bad_object(chunk_match, Bins),
+ req(Proc, {corrupt, Bad});
+ false ->
+ badarg
+ end;
+ [] ->
+ chunk_match(NewState);
+ Terms ->
+ {Terms, NewState}
+ end;
+ Error ->
+ Error
end.
do_foldl_bins(Bins, true) ->
@@ -1093,7 +1090,9 @@ do_apply_op(Op, From, Head, N) ->
{N2, H2} when is_record(H2, head), is_integer(N2) ->
open_file_loop(H2, N2);
H2 when is_record(H2, head) ->
- open_file_loop(H2, N)
+ open_file_loop(H2, N);
+ {{more,From1,Op1,N1}, NewHead} ->
+ do_apply_op(Op1, From1, NewHead, N1)
catch
exit:normal ->
exit(normal);
@@ -1362,37 +1361,35 @@ start_auto_save_timer(Head) ->
%% lookup requests in parallel. Evalute delete_object, delete and
%% insert as well.
stream_op(Op, Pid, Pids, Head, N) ->
- stream_op(Head, Pids, [], N, Pid, Op, Head#head.fixed).
+ #head{fixed = Fxd, update_mode = M} = Head,
+ stream_op(Head, Pids, [], N, Pid, Op, Fxd, M).
-stream_loop(Head, Pids, C, N, false = Fxd) ->
+stream_loop(Head, Pids, C, N, false = Fxd, M) ->
receive
?DETS_CALL(From, Message) ->
- stream_op(Head, Pids, C, N, From, Message, Fxd)
+ stream_op(Head, Pids, C, N, From, Message, Fxd, M)
after 0 ->
stream_end(Head, Pids, C, N, no_more)
end;
-stream_loop(Head, Pids, C, N, _Fxd) ->
+stream_loop(Head, Pids, C, N, _Fxd, _M) ->
stream_end(Head, Pids, C, N, no_more).
-stream_op(Head, Pids, C, N, Pid, {lookup_keys,Keys}, Fxd) ->
+stream_op(Head, Pids, C, N, Pid, {lookup_keys,Keys}, Fxd, M) ->
NC = [{{lookup,Pid},Keys} | C],
- stream_loop(Head, Pids, NC, N, Fxd);
-stream_op(Head, Pids, C, N, Pid, {insert, _Objects} = Op, Fxd) ->
+ stream_loop(Head, Pids, NC, N, Fxd, M);
+stream_op(Head, Pids, C, N, Pid, {insert, _Objects} = Op, Fxd, dirty = M) ->
NC = [Op | C],
- stream_loop(Head, [Pid | Pids], NC, N, Fxd);
-stream_op(Head, Pids, C, N, Pid, {insert_new, _Objects} = Op, Fxd) ->
+ stream_loop(Head, [Pid | Pids], NC, N, Fxd, M);
+stream_op(Head, Pids, C, N, Pid, {delete_key, _Keys} = Op, Fxd, dirty = M) ->
NC = [Op | C],
- stream_loop(Head, [Pid | Pids], NC, N, Fxd);
-stream_op(Head, Pids, C, N, Pid, {delete_key, _Keys} = Op, Fxd) ->
+ stream_loop(Head, [Pid | Pids], NC, N, Fxd, M);
+stream_op(Head, Pids, C, N, Pid, {delete_object, _Os} = Op, Fxd, dirty = M) ->
NC = [Op | C],
- stream_loop(Head, [Pid | Pids], NC, N, Fxd);
-stream_op(Head, Pids, C, N, Pid, {delete_object, _Objects} = Op, Fxd) ->
- NC = [Op | C],
- stream_loop(Head, [Pid | Pids], NC, N, Fxd);
-stream_op(Head, Pids, C, N, Pid, {member, Key}, Fxd) ->
+ stream_loop(Head, [Pid | Pids], NC, N, Fxd, M);
+stream_op(Head, Pids, C, N, Pid, {member, Key}, Fxd, M) ->
NC = [{{lookup,[Pid]},[Key]} | C],
- stream_loop(Head, Pids, NC, N, Fxd);
-stream_op(Head, Pids, C, N, Pid, Op, _Fxd) ->
+ stream_loop(Head, Pids, NC, N, Fxd, M);
+stream_op(Head, Pids, C, N, Pid, Op, _Fxd, _M) ->
stream_end(Head, Pids, C, N, {Pid,Op}).
stream_end(Head, Pids0, C, N, Next) ->
@@ -1437,7 +1434,7 @@ stream_end2([], Ps, no_more, N, C, Head, _Reply) ->
penalty(Head, Ps, C),
{N, Head};
stream_end2([], _Ps, {From, Op}, N, _C, Head, _Reply) ->
- apply_op(Op, From, Head, N).
+ {{more,From,Op,N},Head}.
penalty(H, _Ps, _C) when H#head.fixed =:= false ->
ok;
@@ -1577,13 +1574,18 @@ do_bchunk_init(Head, Tab) ->
L = dets_utils:all_allocated(H2),
C0 = #dets_cont{no_objs = default, bin = <<>>, alloc = L},
BinParms = term_to_binary(Parms),
- {H2, {C0#dets_cont{tab = Tab, what = bchunk}, [BinParms]}}
+ {H2, {C0#dets_cont{tab = Tab, proc = self(),what = bchunk},
+ [BinParms]}}
end;
{NewHead, _} = HeadError when is_record(NewHead, head) ->
HeadError
end.
%% -> {NewHead, {cont(), [binary()]}} | {NewHead, Error}
+do_bchunk(Head, #dets_cont{proc = Proc}) when Proc =/= self() ->
+ {Head, badarg};
+do_bchunk(Head, #dets_cont{bin = eof}) ->
+ {Head, '$end_of_table'};
do_bchunk(Head, State) ->
case dets_v9:read_bchunks(Head, State#dets_cont.alloc) of
{error, Reason} ->
@@ -1953,6 +1955,8 @@ flookup_keys(Head, Keys) ->
end.
%% -> {NewHead, Result}
+fmatch_init(Head, #dets_cont{bin = eof}) ->
+ {Head, '$end_of_table'};
fmatch_init(Head, C) ->
case scan(Head, C) of
{scan_error, Reason} ->
diff --git a/lib/stdlib/src/dets.hrl b/lib/stdlib/src/dets.hrl
index 6e59770753..fbffc9d008 100644
--- a/lib/stdlib/src/dets.hrl
+++ b/lib/stdlib/src/dets.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,7 +18,7 @@
%%
-define(DEFAULT_MIN_NO_SLOTS, 256).
--define(DEFAULT_MAX_NO_SLOTS, 2*1024*1024).
+-define(DEFAULT_MAX_NO_SLOTS, 32*1024*1024).
-define(DEFAULT_AUTOSAVE, 3). % minutes
-define(DEFAULT_CACHE, {3000, 14000}). % {delay,size} in {milliseconds,bytes}
diff --git a/lib/stdlib/src/dets_sup.erl b/lib/stdlib/src/dets_sup.erl
index 5c6caa787d..8ea2ba9b3f 100644
--- a/lib/stdlib/src/dets_sup.erl
+++ b/lib/stdlib/src/dets_sup.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(dets_sup).
@@ -22,9 +22,16 @@
-export([start_link/0, init/1]).
+-spec start_link() -> {'ok', pid()} | 'ignore' | {'error', term()}.
+
start_link() ->
supervisor:start_link({local, dets_sup}, dets_sup, []).
+-spec init([]) ->
+ {'ok', {{'simple_one_for_one', 4, 3600},
+ [{'dets', {'dets', 'istart_link', []},
+ 'temporary', 30000, 'worker', ['dets']}]}}.
+
init([]) ->
SupFlags = {simple_one_for_one, 4, 3600},
Child = {dets, {dets, istart_link, []}, temporary, 30000, worker, [dets]},
diff --git a/lib/stdlib/src/dets_v8.erl b/lib/stdlib/src/dets_v8.erl
index 1f9f84cd27..af36958c1c 100644
--- a/lib/stdlib/src/dets_v8.erl
+++ b/lib/stdlib/src/dets_v8.erl
@@ -1074,6 +1074,8 @@ wl([], _Type, Del, Lookup, I, Objs) ->
[{Del, Lookup, Objs} | I].
%% -> {NewHead, ok} | {NewHead, Error}
+may_grow(Head, 0, once) ->
+ {Head, ok};
may_grow(Head, _N, _How) when Head#head.fixed =/= false ->
{Head, ok};
may_grow(#head{access = read}=Head, _N, _How) ->
diff --git a/lib/stdlib/src/dets_v9.erl b/lib/stdlib/src/dets_v9.erl
index 53238e962f..132af01f79 100644
--- a/lib/stdlib/src/dets_v9.erl
+++ b/lib/stdlib/src/dets_v9.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1908,6 +1908,9 @@ write_cache(Head) ->
end.
%% -> {NewHead, ok} | {NewHead, Error}
+may_grow(Head, 0, once) ->
+ %% Do not re-hash if there is a chance that the file is not dirty.
+ {Head, ok};
may_grow(Head, _N, _How) when Head#head.fixed =/= false ->
{Head, ok};
may_grow(#head{access = read}=Head, _N, _How) ->
diff --git a/lib/stdlib/src/digraph.erl b/lib/stdlib/src/digraph.erl
index 9bdea671a9..5edc868a94 100644
--- a/lib/stdlib/src/digraph.erl
+++ b/lib/stdlib/src/digraph.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(digraph).
@@ -36,6 +36,8 @@
-export([get_short_path/3, get_short_cycle/2]).
+-export_type([digraph/0, d_type/0, vertex/0]).
+
-record(digraph, {vtab = notable :: ets:tab(),
etab = notable :: ets:tab(),
ntab = notable :: ets:tab(),
diff --git a/lib/stdlib/src/edlin.erl b/lib/stdlib/src/edlin.erl
index 6cb441dbed..026bd9038f 100644
--- a/lib/stdlib/src/edlin.erl
+++ b/lib/stdlib/src/edlin.erl
@@ -24,6 +24,7 @@
-export([init/0,start/1,edit_line/2,prefix_arg/1]).
-export([erase_line/1,erase_inp/1,redraw_line/1]).
-export([length_before/1,length_after/1,prompt/1]).
+-export([current_line/1]).
%%-export([expand/1]).
-export([edit_line1/2]).
@@ -421,6 +422,7 @@ over_paren_auto([], _, _, _) ->
%% length_before(Line)
%% length_after(Line)
%% prompt(Line)
+%% current_line(Line)
%% Various functions for accessing bits of a line.
erase_line({line,Pbs,{Bef,Aft},_}) ->
@@ -447,6 +449,9 @@ length_after({line,_,{_Bef,Aft},_}) ->
prompt({line,Pbs,_,_}) ->
Pbs.
+current_line({line,_,{Bef, Aft},_}) ->
+ reverse(Bef, Aft ++ "\n").
+
%% %% expand(CurrentBefore) ->
%% %% {yes,Expansion} | no
%% %% Try to expand the word before as either a module name or a function
diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl
index 424aed3d2e..e5ccaddbb4 100644
--- a/lib/stdlib/src/epp.erl
+++ b/lib/stdlib/src/epp.erl
@@ -33,7 +33,9 @@
%% Epp state record.
-record(epp, {file, %Current file
location, %Current location
+ delta, %Offset from Location (-file)
name="", %Current file name
+ name2="", %-"-, modified by -file
istk=[], %Ifdef stack
sstk=[], %State stack
path=[], %Include-path
@@ -109,6 +111,10 @@ format_error(cannot_parse) ->
io_lib:format("cannot parse file, giving up", []);
format_error({bad,W}) ->
io_lib:format("badly formed '~s'", [W]);
+format_error(missing_parenthesis) ->
+ io_lib:format("badly formed define: missing closing right parenthesis",[]);
+format_error(premature_end) ->
+ "premature end";
format_error({call,What}) ->
io_lib:format("illegal macro call '~s'",[What]);
format_error({undefined,M,none}) ->
@@ -161,7 +167,7 @@ parse_file(Epp) ->
case normalize_typed_record_fields(Fields) of
{typed, NewFields} ->
[{attribute, La, record, {Record, NewFields}},
- {attribute, La, type,
+ {attribute, La, type,
{{record, Record}, Fields, []}}
|parse_file(Epp)];
not_typed ->
@@ -176,6 +182,8 @@ parse_file(Epp) ->
[{eof,Location}]
end.
+normalize_typed_record_fields([]) ->
+ {typed, []};
normalize_typed_record_fields(Fields) ->
normalize_typed_record_fields(Fields, [], false).
@@ -184,7 +192,7 @@ normalize_typed_record_fields([], NewFields, Typed) ->
true -> {typed, lists:reverse(NewFields)};
false -> not_typed
end;
-normalize_typed_record_fields([{typed_record_field,Field,_}|Rest],
+normalize_typed_record_fields([{typed_record_field,Field,_}|Rest],
NewFields, _Typed) ->
normalize_typed_record_fields(Rest, [Field|NewFields], true);
normalize_typed_record_fields([Field|Rest], NewFields, Typed) ->
@@ -228,8 +236,8 @@ init_server(Pid, Name, File, AtLocation, Path, Pdm, Pre) ->
case user_predef(Pdm, Ms0) of
{ok,Ms1} ->
epp_reply(Pid, {ok,self()}),
- St = #epp{file=File, location=AtLocation, name=Name,
- path=Path, macs=Ms1, pre_opened = Pre},
+ St = #epp{file=File, location=AtLocation, delta=0, name=Name,
+ name2=Name, path=Path, macs=Ms1, pre_opened = Pre},
From = wait_request(St),
enter_file_reply(From, Name, AtLocation, AtLocation),
wait_req_scan(St);
@@ -320,7 +328,7 @@ wait_req_scan(St) ->
wait_req_skip(St, Sis) ->
From = wait_request(St),
skip_toks(From, St, Sis).
-
+
%% enter_file(Path, FileName, IncludeToken, From, EppState)
%% leave_file(From, EppState)
%% Handle entering and leaving included files. Notify caller when the
@@ -352,8 +360,8 @@ enter_file2(NewF, Pname, From, St, AtLocation, ExtraPath) ->
enter_file_reply(From, Pname, Loc, AtLocation),
Ms = dict:store({atom,'FILE'}, {none,[{string,Loc,Pname}]}, St#epp.macs),
Path = St#epp.path ++ ExtraPath,
- #epp{location=Loc,file=NewF,
- name=Pname,sstk=[St|St#epp.sstk],path=Path,macs=Ms}.
+ #epp{file=NewF,location=Loc,name=Pname,delta=0,
+ sstk=[St|St#epp.sstk],path=Path,macs=Ms}.
enter_file_reply(From, Name, Location, AtLocation) ->
Attr = loc_attr(AtLocation),
@@ -376,23 +384,32 @@ file_name(N) when is_atom(N) ->
leave_file(From, St) ->
case St#epp.istk of
- [I|Cis] ->
+ [I|Cis] ->
epp_reply(From,
- {error,{St#epp.location,epp,
+ {error,{St#epp.location,epp,
{illegal,"unterminated",I}}}),
leave_file(wait_request(St),St#epp{istk=Cis});
[] ->
case St#epp.sstk of
[OldSt|Sts] ->
close_file(St),
- enter_file_reply(From, OldSt#epp.name,
- OldSt#epp.location, OldSt#epp.location),
+ #epp{location=OldLoc, delta=Delta, name=OldName,
+ name2=OldName2} = OldSt,
+ CurrLoc = add_line(OldLoc, Delta),
Ms = dict:store({atom,'FILE'},
- {none,
- [{string,OldSt#epp.location,
- OldSt#epp.name}]},
+ {none,[{string,CurrLoc,OldName2}]},
St#epp.macs),
- wait_req_scan(OldSt#epp{sstk=Sts,macs=Ms});
+ NextSt = OldSt#epp{sstk=Sts,macs=Ms},
+ enter_file_reply(From, OldName, CurrLoc, CurrLoc),
+ case OldName2 =:= OldName of
+ true ->
+ From;
+ false ->
+ NFrom = wait_request(NextSt),
+ enter_file_reply(NFrom, OldName2, OldLoc,
+ neg_line(CurrLoc))
+ end,
+ wait_req_scan(NextSt);
[] ->
epp_reply(From, {eof,St#epp.location}),
wait_req_scan(St)
@@ -413,7 +430,7 @@ scan_toks(From, St) ->
leave_file(From, St#epp{location=Cl});
{error,_E} ->
epp_reply(From, {error,{St#epp.location,epp,cannot_parse}}),
- leave_file(From, St) %This serious, just exit!
+ leave_file(wait_request(St), St) %This serious, just exit!
end.
scan_toks([{'-',_Lh},{atom,_Ld,define}=Define|Toks], From, St) ->
@@ -487,28 +504,34 @@ scan_extends(_Ts, _As, Ms) -> Ms.
%% scan_define(Tokens, DefineToken, From, EppState)
-scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{',',_Lc}|Toks], _Def, From, St)
+scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{',',Lc}|Toks], _Def, From, St)
when Type =:= atom; Type =:= var ->
- case dict:find({atom,M}, St#epp.macs) of
- {ok, Defs} when is_list(Defs) ->
- %% User defined macros: can be overloaded
- case proplists:is_defined(none, Defs) of
- true ->
- epp_reply(From, {error,{loc(Mac),epp,{redefine,M}}}),
+ case catch macro_expansion(Toks, Lc) of
+ Expansion when is_list(Expansion) ->
+ case dict:find({atom,M}, St#epp.macs) of
+ {ok, Defs} when is_list(Defs) ->
+ %% User defined macros: can be overloaded
+ case proplists:is_defined(none, Defs) of
+ true ->
+ epp_reply(From, {error,{loc(Mac),epp,{redefine,M}}}),
+ wait_req_scan(St);
+ false ->
+ scan_define_cont(From, St,
+ {atom, M},
+ {none, {none,Expansion}})
+ end;
+ {ok, _PreDef} ->
+ %% Predefined macros: cannot be overloaded
+ epp_reply(From, {error,{loc(Mac),epp,{redefine_predef,M}}}),
wait_req_scan(St);
- false ->
+ error ->
scan_define_cont(From, St,
{atom, M},
- {none, {none,macro_expansion(Toks)}})
+ {none, {none,Expansion}})
end;
- {ok, _PreDef} ->
- %% Predefined macros: cannot be overloaded
- epp_reply(From, {error,{loc(Mac),epp,{redefine_predef,M}}}),
- wait_req_scan(St);
- error ->
- scan_define_cont(From, St,
- {atom, M},
- {none, {none,macro_expansion(Toks)}})
+ {error,ErrL,What} ->
+ epp_reply(From, {error,{ErrL,epp,What}}),
+ wait_req_scan(St)
end;
scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{'(',_Lc}|Toks], Def, From, St)
when Type =:= atom; Type =:= var ->
@@ -534,6 +557,9 @@ scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{'(',_Lc}|Toks], Def, From, St)
error ->
scan_define_cont(From, St, {atom, M}, {Len, {As, Me}})
end;
+ {error,ErrL,What} ->
+ epp_reply(From, {error,{ErrL,epp,What}}),
+ wait_req_scan(St);
_ ->
epp_reply(From, {error,{loc(Def),epp,{bad,define}}}),
wait_req_scan(St)
@@ -595,7 +621,7 @@ scan_undef(_Toks, Undef, From, St) ->
%% scan_include(Tokens, IncludeToken, From, St)
-scan_include([{'(',_Llp},{string,_Lf,NewName0},{')',_Lrp},{dot,_Ld}], Inc,
+scan_include([{'(',_Llp},{string,_Lf,NewName0},{')',_Lrp},{dot,_Ld}], Inc,
From, St) ->
NewName = expand_var(NewName0),
enter_file(St#epp.path, NewName, Inc, From, St);
@@ -631,7 +657,7 @@ scan_include_lib([{'(',_Llp},{string,_Lf,NewName0},{')',_Lrp},{dot,_Ld}],
case file:open(LibName, [read]) of
{ok,NewF} ->
ExtraPath = [filename:dirname(LibName)],
- wait_req_scan(enter_file2(NewF, LibName, From,
+ wait_req_scan(enter_file2(NewF, LibName, From,
St, Loc, ExtraPath));
{error,_E2} ->
epp_reply(From,
@@ -753,14 +779,15 @@ scan_file([{'(',_Llp},{string,_Ls,Name},{',',_Lc},{integer,_Li,Ln},{')',_Lrp},
Ms = dict:store({atom,'FILE'}, {none,[{string,1,Name}]}, St#epp.macs),
Locf = loc(Tf),
NewLoc = new_location(Ln, St#epp.location, Locf),
- scan_toks(From, St#epp{name=Name,location=NewLoc,macs=Ms});
+ Delta = abs(get_line(element(2, Tf)))-Ln + St#epp.delta,
+ wait_req_scan(St#epp{name2=Name,location=NewLoc,delta=Delta,macs=Ms});
scan_file(_Toks, Tf, From, St) ->
epp_reply(From, {error,{loc(Tf),epp,{bad,file}}}),
wait_req_scan(St).
new_location(Ln, Le, Lf) when is_integer(Lf) ->
Ln+(Le-Lf);
-new_location(Ln, {Le,_}, {Lf,_}) ->
+new_location(Ln, {Le,_}, {Lf,_}) ->
{Ln+(Le-Lf),1}.
%% skip_toks(From, EppState, SkipIstack)
@@ -787,7 +814,7 @@ skip_toks(From, St, [I|Sis]) ->
leave_file(From, St#epp{location=Cl,istk=[I|Sis]});
{error,_E} ->
epp_reply(From, {error,{St#epp.location,epp,cannot_parse}}),
- leave_file(From, St) %This serious, just exit!
+ leave_file(wait_request(St), St) %This serious, just exit!
end;
skip_toks(From, St, []) ->
scan_toks(From, St).
@@ -801,22 +828,23 @@ skip_else(_Else, From, St, Sis) ->
skip_toks(From, St, Sis).
%% macro_pars(Tokens, ArgStack)
-%% macro_expansion(Tokens)
+%% macro_expansion(Tokens, Line)
%% Extract the macro parameters and the expansion from a macro definition.
-macro_pars([{')',_Lp}, {',',_Ld}|Ex], Args) ->
- {ok, {lists:reverse(Args), macro_expansion(Ex)}};
-macro_pars([{var,_,Name}, {')',_Lp}, {',',_Ld}|Ex], Args) ->
+macro_pars([{')',_Lp}, {',',Ld}|Ex], Args) ->
+ {ok, {lists:reverse(Args), macro_expansion(Ex, Ld)}};
+macro_pars([{var,_,Name}, {')',_Lp}, {',',Ld}|Ex], Args) ->
false = lists:member(Name, Args), %Prolog is nice
- {ok, {lists:reverse([Name|Args]), macro_expansion(Ex)}};
+ {ok, {lists:reverse([Name|Args]), macro_expansion(Ex, Ld)}};
macro_pars([{var,_L,Name}, {',',_}|Ts], Args) ->
- false = lists:member(Name, Args),
+ false = lists:member(Name, Args),
macro_pars(Ts, [Name|Args]).
-macro_expansion([{')',_Lp},{dot,_Ld}]) -> [];
-macro_expansion([{dot,_Ld}]) -> []; %Be nice, allow no right paren!
-macro_expansion([T|Ts]) ->
- [T|macro_expansion(Ts)].
+macro_expansion([{')',_Lp},{dot,_Ld}], _L0) -> [];
+macro_expansion([{dot,Ld}], _L0) -> throw({error,Ld,missing_parenthesis});
+macro_expansion([T|Ts], _L0) ->
+ [T|macro_expansion(Ts, element(2, T))];
+macro_expansion([], L0) -> throw({error,L0,premature_end}).
%% expand_macros(Tokens, Macros)
%% expand_macro(Tokens, MacroToken, RestTokens)
@@ -1071,11 +1099,11 @@ epp_reply(From, Rep) ->
wait_epp_reply(Epp, Mref) ->
receive
- {epp_reply,Epp,Rep} ->
+ {epp_reply,Epp,Rep} ->
erlang:demonitor(Mref),
receive {'DOWN',Mref,_,_,_} -> ok after 0 -> ok end,
Rep;
- {'DOWN',Mref,_,_,E} ->
+ {'DOWN',Mref,_,_,E} ->
receive {epp_reply,Epp,Rep} -> Rep
after 0 -> exit(E)
end
@@ -1116,6 +1144,9 @@ neg_line(L) ->
abs_line(L) ->
erl_scan:set_attribute(line, L, fun(Line) -> abs(Line) end).
+add_line(L, Offset) ->
+ erl_scan:set_attribute(line, L, fun(Line) -> Line+Offset end).
+
start_loc(Line) when is_integer(Line) ->
1;
start_loc({_Line, _Column}) ->
@@ -1132,7 +1163,7 @@ get_line({Line,_Column}) ->
%% mainly aimed at yecc, the parser generator, which uses the -file
%% attribute to get correct lines in messages referring to code
%% supplied by the user (actions etc in .yrl files).
-%%
+%%
%% In a perfect world (read: perfectly implemented applications such
%% as Xref, Cover, Debugger, etc.) it would not be necessary to
%% distinguish -file attributes from epp and the input file. The
@@ -1152,7 +1183,7 @@ get_line({Line,_Column}) ->
%% have been output by epp (corresponding to -include and
%% -include_lib) are kept, but the user's -file attributes are
%% removed. This seems sufficient for now.
-%%
+%%
%% It turns out to be difficult to distinguish -file attributes in the
%% input file from the ones added by epp unless some action is taken.
%% The (less than perfect) solution employed is to let epp assign
@@ -1164,7 +1195,7 @@ get_line({Line,_Column}) ->
interpret_file_attribute(Forms) ->
interpret_file_attr(Forms, 0, []).
-interpret_file_attr([{attribute,Loc,file,{File,Line}}=Form | Forms],
+interpret_file_attr([{attribute,Loc,file,{File,Line}}=Form | Forms],
Delta, Fs) ->
{line, L} = erl_scan:attributes_info(Loc, line),
if
@@ -1175,10 +1206,10 @@ interpret_file_attr([{attribute,Loc,file,{File,Line}}=Form | Forms],
%% -include or -include_lib
% true = L =:= Line,
case Fs of
- [_, Delta1, File | Fs1] -> % end of included file
- [Form | interpret_file_attr(Forms, Delta1, [File | Fs1])];
+ [_, File | Fs1] -> % end of included file
+ [Form | interpret_file_attr(Forms, 0, [File | Fs1])];
_ -> % start of included file
- [Form | interpret_file_attr(Forms, 0, [File, Delta | Fs])]
+ [Form | interpret_file_attr(Forms, 0, [File | Fs])]
end
end;
interpret_file_attr([Form0 | Forms], Delta, Fs) ->
diff --git a/lib/stdlib/src/erl_compile.erl b/lib/stdlib/src/erl_compile.erl
index d9d15e05f8..abff37e4bc 100644
--- a/lib/stdlib/src/erl_compile.erl
+++ b/lib/stdlib/src/erl_compile.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(erl_compile).
@@ -23,6 +23,8 @@
-export([compile_cmdline/1]).
+-export_type([cmd_line_arg/0]).
+
%% Mapping from extension to {M,F} to run the correct compiler.
compiler(".erl") -> {compile, compile};
diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl
index 6fa77f2c3b..61ce41f714 100644
--- a/lib/stdlib/src/erl_expand_records.erl
+++ b/lib/stdlib/src/erl_expand_records.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%% Purpose : Expand records into tuples.
@@ -95,8 +95,9 @@ forms([F | Fs0], St0) ->
forms([], St) -> {[],St}.
clauses([{clause,Line,H0,G0,B0} | Cs0], St0) ->
- {H,St1} = head(H0, St0),
- {G,St2} = guard(G0, St1),
+ {H1,St1} = head(H0, St0),
+ {G1,St2} = guard(G0, St1),
+ {H,G} = optimize_is_record(H1, G1, St2),
{B,St3} = exprs(B0, St2),
{Cs,St4} = clauses(Cs0, St3),
{[{clause,Line,H,G,B} | Cs],St4};
@@ -191,7 +192,6 @@ guard_test1(Test, St) ->
normalise_test(atom, 1) -> is_atom;
normalise_test(binary, 1) -> is_binary;
-normalise_test(constant, 1) -> is_constant;
normalise_test(float, 1) -> is_float;
normalise_test(function, 1) -> is_function;
normalise_test(integer, 1) -> is_integer;
@@ -346,9 +346,6 @@ expr({'fun',Line,{clauses,Cs0}}, St0) ->
{{'fun',Line,{clauses,Cs}},St1};
expr({call,Line,{atom,_,is_record},[A,{atom,_,Name}]}, St) ->
record_test(Line, A, Name, St);
-expr({'cond',Line,Cs0}, St0) ->
- {Cs,St1} = clauses(Cs0, St0),
- {{'cond',Line,Cs},St1};
expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,is_record}},
[A,{atom,_,Name}]}, St) ->
record_test(Line, A, Name, St);
@@ -804,5 +801,137 @@ imported(F, A, St) ->
error -> no
end.
+%%%
+%%% Replace is_record/3 in guards with matching if possible.
+%%%
+
+optimize_is_record(H0, G0, #exprec{compile=Opts}) ->
+ case opt_rec_vars(G0) of
+ [] ->
+ {H0,G0};
+ Rs0 ->
+ case lists:member(no_is_record_optimization, Opts) of
+ true ->
+ {H0,G0};
+ false ->
+ {H,Rs} = opt_pattern_list(H0, Rs0),
+ G = opt_remove(G0, Rs),
+ {H,G}
+ end
+ end.
+
+
+%% opt_rec_vars(Guards) -> Vars.
+%% Search through the guard expression, looking for
+%% variables referenced in those is_record/3 calls that
+%% will fail the entire guard if they evaluate to 'false'
+%%
+%% In the following code
+%%
+%% f(X, Y, Z) when is_record(X, r1) andalso
+%% (is_record(Y, r2) orelse is_record(Z, r3))
+%%
+%% the entire guard will be false if the record test for
+%% X fails, and the clause can be rewritten to:
+%%
+%% f({r1,...}=X, Y, Z) when true andalso
+%% (is_record(Y, r2) or is_record(Z, r3))
+%%
+opt_rec_vars([G|Gs]) ->
+ Rs = opt_rec_vars_1(G, orddict:new()),
+ opt_rec_vars(Gs, Rs);
+opt_rec_vars([]) -> orddict:new().
+
+opt_rec_vars([G|Gs], Rs0) ->
+ Rs1 = opt_rec_vars_1(G, orddict:new()),
+ Rs = ordsets:intersection(Rs0, Rs1),
+ opt_rec_vars(Gs, Rs);
+opt_rec_vars([], Rs) -> Rs.
+
+opt_rec_vars_1([T|Ts], Rs0) ->
+ Rs = opt_rec_vars_2(T, Rs0),
+ opt_rec_vars_1(Ts, Rs);
+opt_rec_vars_1([], Rs) -> Rs.
+
+opt_rec_vars_2({op,_,'and',A1,A2}, Rs) ->
+ opt_rec_vars_1([A1,A2], Rs);
+opt_rec_vars_2({op,_,'andalso',A1,A2}, Rs) ->
+ opt_rec_vars_1([A1,A2], Rs);
+opt_rec_vars_2({op,_,'orelse',Arg,{atom,_,fail}}, Rs) ->
+ %% Since the second argument guarantees failure,
+ %% it is safe to inspect the first argument.
+ opt_rec_vars_2(Arg, Rs);
+opt_rec_vars_2({call,_,{remote,_,{atom,_,erlang},{atom,_,is_record}},
+ [{var,_,V},{atom,_,Tag},{integer,_,Sz}]}, Rs) ->
+ orddict:store(V, {Tag,Sz}, Rs);
+opt_rec_vars_2({call,_,{atom,_,is_record},
+ [{var,_,V},{atom,_,Tag},{integer,_,Sz}]}, Rs) ->
+ orddict:store(V, {Tag,Sz}, Rs);
+opt_rec_vars_2(_, Rs) -> Rs.
+
+opt_pattern_list(Ps, Rs) ->
+ opt_pattern_list(Ps, Rs, []).
+
+opt_pattern_list([P0|Ps], Rs0, Acc) ->
+ {P,Rs} = opt_pattern(P0, Rs0),
+ opt_pattern_list(Ps, Rs, [P|Acc]);
+opt_pattern_list([], Rs, Acc) ->
+ {reverse(Acc),Rs}.
+
+opt_pattern({var,_,V}=Var, Rs0) ->
+ case orddict:find(V, Rs0) of
+ {ok,{Tag,Sz}} ->
+ Rs = orddict:store(V, {remove,Tag,Sz}, Rs0),
+ {opt_var(Var, Tag, Sz),Rs};
+ _ ->
+ {Var,Rs0}
+ end;
+opt_pattern({cons,Line,H0,T0}, Rs0) ->
+ {H,Rs1} = opt_pattern(H0, Rs0),
+ {T,Rs} = opt_pattern(T0, Rs1),
+ {{cons,Line,H,T},Rs};
+opt_pattern({tuple,Line,Es0}, Rs0) ->
+ {Es,Rs} = opt_pattern_list(Es0, Rs0),
+ {{tuple,Line,Es},Rs};
+opt_pattern({match,Line,Pa0,Pb0}, Rs0) ->
+ {Pa,Rs1} = opt_pattern(Pa0, Rs0),
+ {Pb,Rs} = opt_pattern(Pb0, Rs1),
+ {{match,Line,Pa,Pb},Rs};
+opt_pattern(P, Rs) -> {P,Rs}.
+
+opt_var({var,Line,_}=Var, Tag, Sz) ->
+ Rp = record_pattern(2, -1, ignore, Sz, Line, [{atom,Line,Tag}]),
+ {match,Line,{tuple,Line,Rp},Var}.
+
+opt_remove(Gs, Rs) ->
+ [opt_remove_1(G, Rs) || G <- Gs].
+
+opt_remove_1(Ts, Rs) ->
+ [opt_remove_2(T, Rs) || T <- Ts].
+
+opt_remove_2({op,L,'and'=Op,A1,A2}, Rs) ->
+ {op,L,Op,opt_remove_2(A1, Rs),opt_remove_2(A2, Rs)};
+opt_remove_2({op,L,'andalso'=Op,A1,A2}, Rs) ->
+ {op,L,Op,opt_remove_2(A1, Rs),opt_remove_2(A2, Rs)};
+opt_remove_2({op,L,'orelse',A1,A2}, Rs) ->
+ {op,L,'orelse',opt_remove_2(A1, Rs),A2};
+opt_remove_2({call,Line,{remote,_,{atom,_,erlang},{atom,_,is_record}},
+ [{var,_,V},{atom,_,Tag},{integer,_,Sz}]}=A, Rs) ->
+ case orddict:find(V, Rs) of
+ {ok,{remove,Tag,Sz}} ->
+ {atom,Line,true};
+ _ ->
+ A
+ end;
+opt_remove_2({call,Line,{atom,_,is_record},
+ [{var,_,V},{atom,_,Tag},{integer,_,Sz}]}=A, Rs) ->
+ case orddict:find(V, Rs) of
+ {ok,{remove,Tag,Sz}} ->
+ {atom,Line,true};
+ _ ->
+ A
+ end;
+opt_remove_2(A, _) -> A.
+
neg_line(L) ->
erl_parse:set_line(L, fun(Line) -> -abs(Line) end).
diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl
index 16173d8210..b30b02a96f 100644
--- a/lib/stdlib/src/erl_internal.erl
+++ b/lib/stdlib/src/erl_internal.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(erl_internal).
@@ -48,7 +48,7 @@
%%
-export([bif/2,bif/3,guard_bif/2,
- type_test/2,new_type_test/2,old_type_test/2]).
+ type_test/2,new_type_test/2,old_type_test/2,old_bif/2]).
-export([arith_op/2,bool_op/2,comp_op/2,list_op/2,send_op/2,op_type/2]).
%%---------------------------------------------------------------------------
@@ -87,6 +87,8 @@ guard_bif(is_reference, 1) -> true;
guard_bif(is_tuple, 1) -> true;
guard_bif(is_record, 2) -> true;
guard_bif(is_record, 3) -> true;
+guard_bif(binary_part, 2) -> true;
+guard_bif(binary_part, 3) -> true;
guard_bif(Name, A) when is_atom(Name), is_integer(A) -> false.
%% Erlang type tests.
@@ -229,11 +231,14 @@ bif(apply, 2) -> true;
bif(apply, 3) -> 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, 2) -> true;
bif(binary_to_existing_atom, 2) -> true;
bif(binary_to_list, 1) -> true;
bif(binary_to_list, 3) -> true;
bif(binary_to_term, 1) -> true;
+bif(binary_to_term, 2) -> true;
bif(bitsize, 1) -> true;
bif(bit_size, 1) -> true;
bif(bitstring_to_list, 1) -> true;
@@ -242,10 +247,14 @@ bif(check_process_code, 2) -> true;
bif(concat_binary, 1) -> true;
bif(date, 0) -> true;
bif(delete_module, 1) -> true;
+bif(demonitor, 1) -> true;
+bif(demonitor, 2) -> true;
bif(disconnect_node, 1) -> true;
bif(element, 2) -> true;
bif(erase, 0) -> true;
bif(erase, 1) -> true;
+bif(error, 1) -> true;
+bif(error, 2) -> true;
bif(exit, 1) -> true;
bif(exit, 2) -> true;
bif(float, 1) -> true;
@@ -261,6 +270,7 @@ bif(halt, 0) -> true;
bif(halt, 1) -> true;
bif(hd, 1) -> true;
bif(integer_to_list, 1) -> true;
+bif(integer_to_list, 2) -> true;
bif(iolist_size, 1) -> true;
bif(iolist_to_binary, 1) -> true;
bif(is_alive, 0) -> true;
@@ -290,11 +300,16 @@ bif(list_to_bitstring, 1) -> true;
bif(list_to_existing_atom, 1) -> true;
bif(list_to_float, 1) -> true;
bif(list_to_integer, 1) -> true;
+bif(list_to_integer, 2) -> true;
bif(list_to_pid, 1) -> true;
bif(list_to_tuple, 1) -> true;
bif(load_module, 2) -> true;
bif(make_ref, 0) -> true;
+bif(max,2) -> true;
+bif(min,2) -> true;
bif(module_loaded, 1) -> true;
+bif(monitor, 2) -> true;
+bif(monitor, 3) -> true;
bif(monitor_node, 2) -> true;
bif(node, 0) -> true;
bif(node, 1) -> true;
@@ -305,6 +320,7 @@ bif(open_port, 2) -> true;
bif(pid_to_list, 1) -> true;
bif(port_close, 1) -> true;
bif(port_command, 2) -> true;
+bif(port_command, 3) -> true;
bif(port_connect, 2) -> true;
bif(port_control, 3) -> true;
bif(pre_loaded, 0) -> true;
@@ -349,3 +365,134 @@ bif(unlink, 1) -> true;
bif(unregister, 1) -> true;
bif(whereis, 1) -> true;
bif(Name, A) when is_atom(Name), is_integer(A) -> false.
+
+-spec old_bif(Name::atom(), Arity::arity()) -> boolean().
+%% Returns true if erlang:Name/Arity is an old (pre R14) auto-imported BIF, false otherwise.
+%% Use erlang:is_bultin(Mod, Name, Arity) to find whether a function is a BIF
+%% (meaning implemented in C) or not.
+
+old_bif(abs, 1) -> true;
+old_bif(apply, 2) -> true;
+old_bif(apply, 3) -> true;
+old_bif(atom_to_binary, 2) -> true;
+old_bif(atom_to_list, 1) -> true;
+old_bif(binary_to_atom, 2) -> true;
+old_bif(binary_to_existing_atom, 2) -> true;
+old_bif(binary_to_list, 1) -> true;
+old_bif(binary_to_list, 3) -> true;
+old_bif(binary_to_term, 1) -> true;
+old_bif(bitsize, 1) -> true;
+old_bif(bit_size, 1) -> true;
+old_bif(bitstring_to_list, 1) -> true;
+old_bif(byte_size, 1) -> true;
+old_bif(check_process_code, 2) -> true;
+old_bif(concat_binary, 1) -> true;
+old_bif(date, 0) -> true;
+old_bif(delete_module, 1) -> true;
+old_bif(disconnect_node, 1) -> true;
+old_bif(element, 2) -> true;
+old_bif(erase, 0) -> true;
+old_bif(erase, 1) -> true;
+old_bif(exit, 1) -> true;
+old_bif(exit, 2) -> true;
+old_bif(float, 1) -> true;
+old_bif(float_to_list, 1) -> true;
+old_bif(garbage_collect, 0) -> true;
+old_bif(garbage_collect, 1) -> true;
+old_bif(get, 0) -> true;
+old_bif(get, 1) -> true;
+old_bif(get_keys, 1) -> true;
+old_bif(group_leader, 0) -> true;
+old_bif(group_leader, 2) -> true;
+old_bif(halt, 0) -> true;
+old_bif(halt, 1) -> true;
+old_bif(hd, 1) -> true;
+old_bif(integer_to_list, 1) -> true;
+old_bif(iolist_size, 1) -> true;
+old_bif(iolist_to_binary, 1) -> true;
+old_bif(is_alive, 0) -> true;
+old_bif(is_process_alive, 1) -> true;
+old_bif(is_atom, 1) -> true;
+old_bif(is_boolean, 1) -> true;
+old_bif(is_binary, 1) -> true;
+old_bif(is_bitstr, 1) -> true;
+old_bif(is_bitstring, 1) -> true;
+old_bif(is_float, 1) -> true;
+old_bif(is_function, 1) -> true;
+old_bif(is_function, 2) -> true;
+old_bif(is_integer, 1) -> true;
+old_bif(is_list, 1) -> true;
+old_bif(is_number, 1) -> true;
+old_bif(is_pid, 1) -> true;
+old_bif(is_port, 1) -> true;
+old_bif(is_reference, 1) -> true;
+old_bif(is_tuple, 1) -> true;
+old_bif(is_record, 2) -> true;
+old_bif(is_record, 3) -> true;
+old_bif(length, 1) -> true;
+old_bif(link, 1) -> true;
+old_bif(list_to_atom, 1) -> true;
+old_bif(list_to_binary, 1) -> true;
+old_bif(list_to_bitstring, 1) -> true;
+old_bif(list_to_existing_atom, 1) -> true;
+old_bif(list_to_float, 1) -> true;
+old_bif(list_to_integer, 1) -> true;
+old_bif(list_to_pid, 1) -> true;
+old_bif(list_to_tuple, 1) -> true;
+old_bif(load_module, 2) -> true;
+old_bif(make_ref, 0) -> true;
+old_bif(module_loaded, 1) -> true;
+old_bif(monitor_node, 2) -> true;
+old_bif(node, 0) -> true;
+old_bif(node, 1) -> true;
+old_bif(nodes, 0) -> true;
+old_bif(nodes, 1) -> true;
+old_bif(now, 0) -> true;
+old_bif(open_port, 2) -> true;
+old_bif(pid_to_list, 1) -> true;
+old_bif(port_close, 1) -> true;
+old_bif(port_command, 2) -> true;
+old_bif(port_connect, 2) -> true;
+old_bif(port_control, 3) -> true;
+old_bif(pre_loaded, 0) -> true;
+old_bif(process_flag, 2) -> true;
+old_bif(process_flag, 3) -> true;
+old_bif(process_info, 1) -> true;
+old_bif(process_info, 2) -> true;
+old_bif(processes, 0) -> true;
+old_bif(purge_module, 1) -> true;
+old_bif(put, 2) -> true;
+old_bif(register, 2) -> true;
+old_bif(registered, 0) -> true;
+old_bif(round, 1) -> true;
+old_bif(self, 0) -> true;
+old_bif(setelement, 3) -> true;
+old_bif(size, 1) -> true;
+old_bif(spawn, 1) -> true;
+old_bif(spawn, 2) -> true;
+old_bif(spawn, 3) -> true;
+old_bif(spawn, 4) -> true;
+old_bif(spawn_link, 1) -> true;
+old_bif(spawn_link, 2) -> true;
+old_bif(spawn_link, 3) -> true;
+old_bif(spawn_link, 4) -> true;
+old_bif(spawn_monitor, 1) -> true;
+old_bif(spawn_monitor, 3) -> true;
+old_bif(spawn_opt, 2) -> true;
+old_bif(spawn_opt, 3) -> true;
+old_bif(spawn_opt, 4) -> true;
+old_bif(spawn_opt, 5) -> true;
+old_bif(split_binary, 2) -> true;
+old_bif(statistics, 1) -> true;
+old_bif(term_to_binary, 1) -> true;
+old_bif(term_to_binary, 2) -> true;
+old_bif(throw, 1) -> true;
+old_bif(time, 0) -> true;
+old_bif(tl, 1) -> true;
+old_bif(trunc, 1) -> true;
+old_bif(tuple_size, 1) -> true;
+old_bif(tuple_to_list, 1) -> true;
+old_bif(unlink, 1) -> true;
+old_bif(unregister, 1) -> true;
+old_bif(whereis, 1) -> true;
+old_bif(Name, A) when is_atom(Name), is_integer(A) -> false.
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 91f7641af7..0c2d3db8ec 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -40,7 +40,7 @@
%% Value.
%% The option handling functions.
--spec bool_option(atom(), atom(), boolean(), [_]) -> boolean().
+-spec bool_option(atom(), atom(), boolean(), [compile:option()]) -> boolean().
bool_option(On, Off, Default, Opts) ->
foldl(fun (Opt, _Def) when Opt =:= On -> true;
@@ -72,6 +72,10 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
%%-define(DEBUGF(X,Y), io:format(X, Y)).
-define(DEBUGF(X,Y), void).
+-type line() :: erl_scan:line(). % a convenient alias
+-type fa() :: {atom(), arity()}. % function+arity
+-type ta() :: {atom(), arity()}. % type+arity
+
%% Usage of records, functions, and imports. The variable table, which
%% is passed on as an argument, holds the usage of variables.
-record(usage, {
@@ -94,9 +98,11 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
mod_imports=dict:new() :: dict(), %Module Imports
compile=[], %Compile flags
records=dict:new() :: dict(), %Record definitions
+ locals=gb_sets:empty() :: gb_set(), %All defined functions (prescanned)
+ no_auto=gb_sets:empty() :: gb_set(), %Functions explicitly not autoimported
defined=gb_sets:empty() :: gb_set(), %Defined fuctions
- on_load=[] :: [{atom(),integer()}], %On-load function
- on_load_line=0 :: integer(), %Line for on_load
+ on_load=[] :: [fa()], %On-load function
+ on_load_line=0 :: line(), %Line for on_load
clashes=[], %Exported functions named as BIFs
not_deprecated=[], %Not considered deprecated
func=[], %Current function
@@ -110,10 +116,11 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
%outside any fun or lc
xqlc= false :: boolean(), %true if qlc.hrl included
new = false :: boolean(), %Has user-defined 'new/N'
- called= [], %Called functions
+ called= [] :: [{fa(),line()}], %Called functions
usage = #usage{} :: #usage{},
specs = dict:new() :: dict(), %Type specifications
- types = dict:new() :: dict() %Type definitions
+ types = dict:new() :: dict(), %Type definitions
+ exp_types=gb_sets:empty():: gb_set() %Exported types
}).
-type lint_state() :: #lint{}.
@@ -161,6 +168,9 @@ format_error({bad_nowarn_unused_function,{F,A}}) ->
io_lib:format("function ~w/~w undefined", [F,A]);
format_error({bad_nowarn_bif_clash,{F,A}}) ->
io_lib:format("function ~w/~w undefined", [F,A]);
+format_error(disallowed_nowarn_bif_clash) ->
+ io_lib:format("compile directive nowarn_bif_clash is no longer allowed,~n"
+ " - use explicit module names or -compile({no_auto_import, [F/A]})", []);
format_error({bad_nowarn_deprecated_function,{M,F,A}}) ->
io_lib:format("~w:~w/~w is not a deprecated function", [M,F,A]);
format_error({bad_on_load,Term}) ->
@@ -186,13 +196,21 @@ format_error({define_import,{F,A}}) ->
io_lib:format("defining imported function ~w/~w", [F,A]);
format_error({unused_function,{F,A}}) ->
io_lib:format("function ~w/~w is unused", [F,A]);
-format_error({redefine_bif,{F,A}}) ->
- io_lib:format("defining BIF ~w/~w", [F,A]);
format_error({call_to_redefined_bif,{F,A}}) ->
- io_lib:format("call to ~w/~w will call erlang:~w/~w; "
- "not ~w/~w in this module \n"
- " (add an explicit module name to the call to avoid this error)",
- [F,A,F,A,F,A]);
+ io_lib:format("ambiguous call of overridden auto-imported BIF ~w/~w~n"
+ " - use erlang:~w/~w or \"-compile({no_auto_import,[~w/~w]}).\" "
+ "to resolve name clash", [F,A,F,A,F,A]);
+format_error({call_to_redefined_old_bif,{F,A}}) ->
+ io_lib:format("ambiguous call of overridden pre R14 auto-imported BIF ~w/~w~n"
+ " - use erlang:~w/~w or \"-compile({no_auto_import,[~w/~w]}).\" "
+ "to resolve name clash", [F,A,F,A,F,A]);
+format_error({redefine_old_bif_import,{F,A}}) ->
+ io_lib:format("import directive overrides pre R14 auto-imported BIF ~w/~w~n"
+ " - use \"-compile({no_auto_import,[~w/~w]}).\" "
+ "to resolve name clash", [F,A,F,A]);
+format_error({redefine_bif_import,{F,A}}) ->
+ io_lib:format("import directive overrides auto-imported BIF ~w/~w~n"
+ " - use \"-compile({no_auto_import,[~w/~w]}).\" to resolve name clash", [F,A,F,A]);
format_error({deprecated, MFA, ReplacementMFA, Rel}) ->
io_lib:format("~s is deprecated and will be removed in ~s; use ~s",
@@ -213,6 +231,9 @@ format_error(illegal_pattern) -> "illegal pattern";
format_error(illegal_bin_pattern) ->
"binary patterns cannot be matched in parallel using '='";
format_error(illegal_expr) -> "illegal expression";
+format_error({illegal_guard_local_call, {F,A}}) ->
+ io_lib:format("call to local/imported function ~w/~w is illegal in guard",
+ [F,A]);
format_error(illegal_guard_expr) -> "illegal guard expression";
%% --- exports ---
format_error({explicit_export,F,A}) ->
@@ -242,10 +263,10 @@ format_error({untyped_record,T}) ->
format_error({unbound_var,V}) ->
io_lib:format("variable ~w is unbound", [V]);
format_error({unsafe_var,V,{What,Where}}) ->
- io_lib:format("variable ~w unsafe in ~w ~s",
+ io_lib:format("variable ~w unsafe in ~w ~s",
[V,What,format_where(Where)]);
format_error({exported_var,V,{What,Where}}) ->
- io_lib:format("variable ~w exported from ~w ~s",
+ io_lib:format("variable ~w exported from ~w ~s",
[V,What,format_where(Where)]);
format_error({shadowed_var,V,In}) ->
io_lib:format("variable ~w shadowed in ~w", [V,In]);
@@ -290,22 +311,26 @@ format_error({ill_defined_behaviour_callbacks,Behaviour}) ->
%% --- types and specs ---
format_error({singleton_typevar, Name}) ->
io_lib:format("type variable ~w is only used once (is unbound)", [Name]);
+format_error({bad_export_type, _ETs}) ->
+ io_lib:format("bad export_type declaration", []);
+format_error({duplicated_export_type, {T, A}}) ->
+ io_lib:format("type ~w/~w already exported", [T, A]);
format_error({undefined_type, {TypeName, Arity}}) ->
io_lib:format("type ~w~s undefined", [TypeName, gen_type_paren(Arity)]);
format_error({unused_type, {TypeName, Arity}}) ->
io_lib:format("type ~w~s is unused", [TypeName, gen_type_paren(Arity)]);
format_error({new_builtin_type, {TypeName, Arity}}) ->
io_lib:format("type ~w~s is a new builtin type; "
- "its (re)definition is allowed only until the next release",
+ "its (re)definition is allowed only until the next release",
[TypeName, gen_type_paren(Arity)]);
format_error({builtin_type, {TypeName, Arity}}) ->
- io_lib:format("type ~w~s is a builtin type; it cannot be redefined",
+ io_lib:format("type ~w~s is a builtin type; it cannot be redefined",
[TypeName, gen_type_paren(Arity)]);
format_error({renamed_type, OldName, NewName}) ->
io_lib:format("type ~w() is now called ~w(); "
"please use the new name instead", [OldName, NewName]);
format_error({redefine_type, {TypeName, Arity}}) ->
- io_lib:format("type ~w~s already defined",
+ io_lib:format("type ~w~s already defined",
[TypeName, gen_type_paren(Arity)]);
format_error({type_syntax, Constr}) ->
io_lib:format("bad ~w type", [Constr]);
@@ -354,7 +379,7 @@ pseudolocals() ->
%%
%% Used by erl_eval.erl to check commands.
-%%
+%%
exprs(Exprs, BindingsList) ->
exprs_opt(Exprs, BindingsList, []).
@@ -362,7 +387,7 @@ exprs_opt(Exprs, BindingsList, Opts) ->
{St0,Vs} = foldl(fun({{record,_SequenceNumber,_Name},Attr0}, {St1,Vs1}) ->
Attr = zip_file_and_line(Attr0, "none"),
{attribute_state(Attr, St1),Vs1};
- ({V,_}, {St1,Vs1}) ->
+ ({V,_}, {St1,Vs1}) ->
{St1,[{V,{bound,unused,[]}} | Vs1]}
end, {start("nofile",Opts),[]}, BindingsList),
Vt = orddict:from_list(Vs),
@@ -391,7 +416,7 @@ module(Forms) ->
Opts = compiler_options(Forms),
St = forms(Forms, start("nofile", Opts)),
return_status(St).
-
+
module(Forms, FileName) ->
Opts = compiler_options(Forms),
St = forms(Forms, start(FileName, Opts)),
@@ -506,7 +531,7 @@ pack_errors(Es) ->
%% Sort on line number.
pack_warnings(Ws) ->
- [{File,lists:sort([W || {F,W} <- Ws, F =:= File])} ||
+ [{File,lists:sort([W || {F,W} <- Ws, F =:= File])} ||
File <- lists:usort([F || {F,_} <- Ws])].
%% add_error(ErrorDescriptor, State) -> State'
@@ -516,13 +541,13 @@ pack_warnings(Ws) ->
add_error(E, St) -> St#lint{errors=[{St#lint.file,E}|St#lint.errors]}.
-add_error(FileLine, E, St) ->
+add_error(FileLine, E, St) ->
{File,Location} = loc(FileLine),
add_error({Location,erl_lint,E}, St#lint{file = File}).
add_warning(W, St) -> St#lint{warnings=[{St#lint.file,W}|St#lint.warnings]}.
-add_warning(FileLine, W, St) ->
+add_warning(FileLine, W, St) ->
{File,Location} = loc(FileLine),
add_warning({Location,erl_lint,W}, St#lint{file = File}).
@@ -538,8 +563,12 @@ loc(L) ->
forms(Forms0, St0) ->
Forms = eval_file_attribute(Forms0, St0),
+ Locals = local_functions(Forms),
+ AutoImportSuppressed = auto_import_suppressed(St0#lint.compile),
+ StDeprecated = disallowed_compile_flags(Forms,St0),
%% Line numbers are from now on pairs {File,Line}.
- St1 = includes_qlc_hrl(Forms, St0),
+ St1 = includes_qlc_hrl(Forms, StDeprecated#lint{locals = Locals,
+ no_auto = AutoImportSuppressed}),
St2 = bif_clashes(Forms, St1),
St3 = not_deprecated(Forms, St2),
St4 = foldl(fun form/2, pre_scan(Forms, St3), Forms),
@@ -561,7 +590,7 @@ pre_scan([_ | Fs], St) ->
pre_scan(Fs, St);
pre_scan([], St) ->
St.
-
+
includes_qlc_hrl(Forms, St) ->
%% QLC calls erl_lint several times, sometimes with the compile
%% attribute removed. The file attribute, however, is left as is.
@@ -667,6 +696,8 @@ attribute_state({attribute,L,extends,_M}, St) ->
add_error(L, invalid_extends, St);
attribute_state({attribute,L,export,Es}, St) ->
export(L, Es, St);
+attribute_state({attribute,L,export_type,Es}, St) ->
+ export_type(L, Es, St);
attribute_state({attribute,L,import,Is}, St) ->
import(L, Is, St);
attribute_state({attribute,L,record,{Name,Fields}}, St) ->
@@ -724,27 +755,38 @@ bif_clashes(Forms, St) ->
Clashes = ordsets:subtract(ordsets:from_list(Clashes0), Nowarn),
St#lint{clashes=Clashes}.
--spec is_bif_clash(atom(), byte(), lint_state()) -> boolean().
-
-is_bif_clash(_Name, _Arity, #lint{clashes=[]}) ->
- false;
-is_bif_clash(Name, Arity, #lint{clashes=Clashes}) ->
- ordsets:is_element({Name,Arity}, Clashes).
-
%% not_deprecated(Forms, State0) -> State
not_deprecated(Forms, St0) ->
%% There are no line numbers in St0#lint.compile.
- MFAsL = [{MFA,L} ||
+ MFAsL = [{MFA,L} ||
{attribute, L, compile, Args} <- Forms,
{nowarn_deprecated_function, MFAs0} <- lists:flatten([Args]),
MFA <- lists:flatten([MFAs0])],
Nowarn = [MFA || {MFA,_L} <- MFAsL],
- Bad = [MFAL || {{M,F,A},_L}=MFAL <- MFAsL,
+ Bad = [MFAL || {{M,F,A},_L}=MFAL <- MFAsL,
otp_internal:obsolete(M, F, A) =:= no],
St1 = func_line_warning(bad_nowarn_deprecated_function, Bad, St0),
St1#lint{not_deprecated = ordsets:from_list(Nowarn)}.
+%% The nowarn_bif_clash directive is not only deprecated, it's actually an error from R14A
+disallowed_compile_flags(Forms, St0) ->
+ %% There are (still) no line numbers in St0#lint.compile.
+ Errors0 = [ {St0#lint.file,{L,erl_lint,disallowed_nowarn_bif_clash}} ||
+ {attribute,[{line,{_,L}}],compile,nowarn_bif_clash} <- Forms ],
+ Errors1 = [ {St0#lint.file,{L,erl_lint,disallowed_nowarn_bif_clash}} ||
+ {attribute,[{line,{_,L}}],compile,{nowarn_bif_clash, {_,_}}} <- Forms ],
+ Disabled = (not is_warn_enabled(bif_clash, St0)),
+ Errors = if
+ Disabled andalso Errors0 =:= [] ->
+ [{St0#lint.file,{erl_lint,disallowed_nowarn_bif_clash}} | St0#lint.errors];
+ Disabled ->
+ Errors0 ++ Errors1 ++ St0#lint.errors;
+ true ->
+ Errors1 ++ St0#lint.errors
+ end,
+ St0#lint{errors=Errors}.
+
%% post_traversal_check(Forms, State0) -> State.
%% Do some further checking after the forms have been traversed and
%% data about calls etc. have been collected.
@@ -862,7 +904,7 @@ check_deprecated(Forms, St0) ->
Bad = [{E,L} || {attribute, L, deprecated, Depr} <- Forms,
D <- lists:flatten([Depr]),
E <- depr_cat(D, X, Mod)],
- foldl(fun ({E,L}, St1) ->
+ foldl(fun ({E,L}, St1) ->
add_error(L, E, St1)
end, St0, Bad).
@@ -912,7 +954,7 @@ check_imports(Forms, St0) ->
true ->
Usage = St0#lint.usage,
Unused = ordsets:subtract(St0#lint.imports, Usage#usage.imported),
- Imports = [{{FA,list_to_atom(package_to_string(Mod))},L}
+ Imports = [{{FA,list_to_atom(package_to_string(Mod))},L}
|| {attribute,L,import,{Mod,Fs}} <- Forms,
FA <- lists:usort(Fs)],
Bad = [{FM,L} || FM <- Unused, {FM2,L} <- Imports, FM =:= FM2],
@@ -932,7 +974,7 @@ check_unused_functions(Forms, St0) ->
Opts = St1#lint.compile,
case member(export_all, Opts) orelse
not is_warn_enabled(unused_function, St1) of
- true ->
+ true ->
St1;
false ->
Nowarn = nowarn_function(nowarn_unused_function, Opts),
@@ -1003,12 +1045,13 @@ check_option_functions(Forms, Tag0, Type, St0) ->
{Tag, FAs0} <- lists:flatten([Args]),
Tag0 =:= Tag,
FA <- lists:flatten([FAs0])],
- DefFunctions = gb_sets:to_list(St0#lint.defined) -- pseudolocals(),
+ DefFunctions = (gb_sets:to_list(St0#lint.defined) -- pseudolocals()) ++
+ [{F,A} || {{F,A},_} <- orddict:to_list(St0#lint.imports)],
Bad = [{FA,L} || {FA,L} <- FAsL, not member(FA, DefFunctions)],
func_line_error(Type, Bad, St0).
nowarn_function(Tag, Opts) ->
- ordsets:from_list([FA || {Tag1,FAs} <- Opts,
+ ordsets:from_list([FA || {Tag1,FAs} <- Opts,
Tag1 =:= Tag,
FA <- lists:flatten([FAs])]).
@@ -1021,11 +1064,8 @@ func_line_error(Type, Fs, St) ->
check_untyped_records(Forms, St0) ->
case is_warn_enabled(untyped_record, St0) of
true ->
- %% One possibility is to use the names of all records
- %% RecNames = dict:fetch_keys(St0#lint.records),
- %% but I think it's better to keep those that are used by the file
- Usage = St0#lint.usage,
- UsedRecNames = sets:to_list(Usage#usage.used_records),
+ %% Use the names of all records *defined* in the module (not used)
+ RecNames = dict:fetch_keys(St0#lint.records),
%% these are the records with field(s) containing type info
TRecNames = [Name ||
{attribute,_,type,{{record,Name},Fields,_}} <- Forms,
@@ -1038,7 +1078,7 @@ check_untyped_records(Forms, St0) ->
[] -> St; % exclude records with no fields
[_|_] -> add_warning(L, {untyped_record, N}, St)
end
- end, St0, UsedRecNames -- TRecNames);
+ end, St0, RecNames -- TRecNames);
false ->
St0
end.
@@ -1051,10 +1091,10 @@ check_unused_records(Forms, St0) ->
%% functions count.
Usage = St0#lint.usage,
UsedRecords = sets:to_list(Usage#usage.used_records),
- URecs = foldl(fun (Used, Recs) ->
- dict:erase(Used, Recs)
+ URecs = foldl(fun (Used, Recs) ->
+ dict:erase(Used, Recs)
end, St0#lint.records, UsedRecords),
- Unused = [{Name,FileLine} ||
+ Unused = [{Name,FileLine} ||
{Name,{FileLine,_Fields}} <- dict:to_list(URecs),
element(1, loc(FileLine)) =:= FirstFile],
foldl(fun ({N,L}, St) ->
@@ -1064,18 +1104,19 @@ check_unused_records(Forms, St0) ->
St0
end.
-%% For storing the import list we use the orddict module.
+%% For storing the import list we use the orddict module.
%% We know an empty set is [].
-%% export(Line, Exports, State) -> State.
+-spec export(line(), [fa()], lint_state()) -> lint_state().
%% Mark functions as exported, also as called from the export line.
export(Line, Es, #lint{exports = Es0, called = Called} = St0) ->
- {Es1,C1,St1} =
+ {Es1,C1,St1} =
foldl(fun (NA, {E,C,St2}) ->
St = case gb_sets:is_element(NA, E) of
true ->
- add_warning(Line, {duplicated_export, NA}, St2);
+ Warn = {duplicated_export,NA},
+ add_warning(Line, Warn, St2);
false ->
St2
end,
@@ -1084,8 +1125,31 @@ export(Line, Es, #lint{exports = Es0, called = Called} = St0) ->
{Es0,Called,St0}, Es),
St1#lint{exports = Es1, called = C1}.
-%% import(Line, Imports, State) -> State.
-%% imported(Name, Arity, State) -> {yes,Module} | no.
+-spec export_type(line(), [ta()], lint_state()) -> lint_state().
+%% Mark types as exported; also mark them as used from the export line.
+
+export_type(Line, ETs, #lint{usage = Usage, exp_types = ETs0} = St0) ->
+ UTs0 = Usage#usage.used_types,
+ try foldl(fun ({T,A}=TA, {E,U,St2}) when is_atom(T), is_integer(A) ->
+ St = case gb_sets:is_element(TA, E) of
+ true ->
+ Warn = {duplicated_export_type,TA},
+ add_warning(Line, Warn, St2);
+ false ->
+ St2
+ end,
+ {gb_sets:add_element(TA, E), dict:store(TA, Line, U), St}
+ end,
+ {ETs0,UTs0,St0}, ETs) of
+ {ETs1,UTs1,St1} ->
+ St1#lint{usage = Usage#usage{used_types = UTs1}, exp_types = ETs1}
+ catch
+ error:_ ->
+ add_error(Line, {bad_export_type, ETs}, St0)
+ end.
+
+-type import() :: {module(), [fa()]} | module().
+-spec import(line(), import(), lint_state()) -> lint_state().
import(Line, {Mod,Fs}, St) ->
Mod1 = package_to_string(Mod),
@@ -1097,11 +1161,41 @@ import(Line, {Mod,Fs}, St) ->
St#lint{imports=add_imports(list_to_atom(Mod1), Mfs,
St#lint.imports)};
Efs ->
- foldl(fun (Ef, St0) ->
- add_error(Line, {redefine_import,Ef},
- St0)
+ {Err, St1} =
+ foldl(fun ({bif,{F,A},_}, {Err,St0}) ->
+ %% BifClash - import directive
+ Warn = is_warn_enabled(bif_clash, St0)
+ and (not bif_clash_specifically_disabled(St0,{F,A})),
+ AutoImpSup = is_autoimport_suppressed(St0#lint.no_auto,{F,A}),
+ OldBif = erl_internal:old_bif(F,A),
+ {Err,if
+ Warn and (not AutoImpSup) and OldBif ->
+ add_error
+ (Line,
+ {redefine_old_bif_import, {F,A}},
+ St0);
+ Warn and (not AutoImpSup) ->
+ add_warning
+ (Line,
+ {redefine_bif_import, {F,A}},
+ St0);
+ true ->
+ St0
+ end};
+ (Ef, {_Err,St0}) ->
+ {true,add_error(Line,
+ {redefine_import,Ef},
+ St0)}
end,
- St, Efs)
+ {false,St}, Efs),
+ if
+ not Err ->
+ St1#lint{imports=
+ add_imports(list_to_atom(Mod1), Mfs,
+ St#lint.imports)};
+ true ->
+ St1
+ end
end;
false ->
add_error(Line, {bad_module_name, Mod1}, St)
@@ -1144,13 +1238,15 @@ check_imports(_Line, Fs, Is) ->
add_imports(Mod, Fs, Is) ->
foldl(fun (F, Is0) -> orddict:store(F, Mod, Is0) end, Is, Fs).
+-spec imported(atom(), arity(), lint_state()) -> {'yes',module()} | 'no'.
+
imported(F, A, St) ->
case orddict:find({F,A}, St#lint.imports) of
{ok,Mod} -> {yes,Mod};
error -> no
end.
-%% on_load(Line, Val, State) -> State.
+-spec on_load(line(), fa(), lint_state()) -> lint_state().
%% Check an on_load directive and remember it.
on_load(Line, {Name,Arity}=Fa, #lint{on_load=OnLoad0}=St0)
@@ -1182,7 +1278,7 @@ check_on_load(#lint{defined=Defined,on_load=[{_,0}=Fa],
end;
check_on_load(St) -> St.
-%% call_function(Line, Name, Arity, State) -> State.
+-spec call_function(line(), atom(), arity(), lint_state()) -> lint_state().
%% Add to both called and calls.
call_function(Line, F, A, #lint{usage=Usage0,called=Cd,func=Func}=St) ->
@@ -1194,12 +1290,6 @@ call_function(Line, F, A, #lint{usage=Usage0,called=Cd,func=Func}=St) ->
end,
St#lint{called=[{NA,Line}|Cd], usage=Usage}.
-%% is_function_exported(Name, Arity, State) -> false|true.
-
-is_function_exported(Name, Arity, #lint{exports=Exports,compile=Compile}) ->
- gb_sets:is_element({Name,Arity}, Exports) orelse
- member(export_all, Compile).
-
%% function(Line, Name, Arity, Clauses, State) -> State.
function(Line, instance, _Arity, _Cs, St) when St#lint.global_vt =/= [] ->
@@ -1208,7 +1298,7 @@ function(Line, Name, Arity, Cs, St0) ->
St1 = define_function(Line, Name, Arity, St0#lint{func={Name,Arity}}),
clauses(Cs, St1#lint.global_vt, St1).
-%% define_function(Line, Name, Arity, State) -> State.
+-spec define_function(line(), atom(), arity(), lint_state()) -> lint_state().
define_function(Line, Name, Arity, St0) ->
St1 = keyword_warning(Line, Name, St0),
@@ -1218,14 +1308,9 @@ define_function(Line, Name, Arity, St0) ->
add_error(Line, {redefine_function,NA}, St1);
false ->
St2 = St1#lint{defined=gb_sets:add_element(NA, St1#lint.defined)},
- St = case erl_internal:bif(Name, Arity) andalso
- not is_function_exported(Name, Arity, St2) of
- true -> add_warning(Line, {redefine_bif,NA}, St2);
- false -> St2
- end,
- case imported(Name, Arity, St) of
- {yes,_M} -> add_error(Line, {define_import,NA}, St);
- no -> St
+ case imported(Name, Arity, St2) of
+ {yes,_M} -> add_error(Line, {define_import,NA}, St2);
+ no -> St2
end
end.
@@ -1261,7 +1346,7 @@ head([P|Ps], Vt, Old, St0) ->
{vtmerge_pat(Pvt, Psvt),vtmerge_pat(Bvt1,Bvt2),St2};
head([], _Vt, _Env, St) -> {[],[],St}.
-%% pattern(Pattern, VarTable, Old, BinVarTable, State) ->
+%% pattern(Pattern, VarTable, Old, BinVarTable, State) ->
%% {UpdVarTable,BinVarTable,State}.
%% Check pattern return variables. Old is the set of variables used for
%% deciding whether an occurrence is a binding occurrence or a use, and
@@ -1279,7 +1364,7 @@ pattern(P, Vt, St) ->
pattern({var,_Line,'_'}, _Vt, _Old, _Bvt, St) ->
{[],[],St}; %Ignore anonymous variable
-pattern({var,Line,V}, _Vt, Old, Bvt, St) ->
+pattern({var,Line,V}, _Vt, Old, Bvt, St) ->
pat_var(V, Line, Old, Bvt, St);
pattern({char,_Line,_C}, _Vt, _Old, _Bvt, St) -> {[],[],St};
pattern({integer,_Line,_I}, _Vt, _Old, _Bvt, St) -> {[],[],St};
@@ -1297,7 +1382,7 @@ pattern({tuple,_Line,Ps}, Vt, Old, Bvt, St) ->
%%pattern({struct,_Line,_Tag,Ps}, Vt, Old, Bvt, St) ->
%% pattern_list(Ps, Vt, Old, Bvt, St);
pattern({record_index,Line,Name,Field}, _Vt, _Old, _Bvt, St) ->
- {Vt1,St1} =
+ {Vt1,St1} =
check_record(Line, Name, St,
fun (Dfs, St1) ->
pattern_field(Field, Name, Dfs, St1)
@@ -1312,7 +1397,7 @@ pattern({record_field,Line,_,_}=M, _Vt, _Old, _Bvt, St0) ->
end;
pattern({record,Line,Name,Pfs}, Vt, Old, Bvt, St) ->
case dict:find(Name, St#lint.records) of
- {ok,{_Line,Fields}} ->
+ {ok,{_Line,Fields}} ->
St1 = used_record(Name, St),
pattern_fields(Pfs, Name, Fields, Vt, Old, Bvt, St1);
error -> {[],[],add_error(Line, {undefined_record,Name}, St)}
@@ -1372,7 +1457,7 @@ reject_bin_alias({cons,_,H1,T1}, {cons,_,H2,T2}, St0) ->
reject_bin_alias(T1, T2, St);
reject_bin_alias({tuple,_,Es1}, {tuple,_,Es2}, St) ->
reject_bin_alias_list(Es1, Es2, St);
-reject_bin_alias({record,_,Name1,Pfs1}, {record,_,Name2,Pfs2},
+reject_bin_alias({record,_,Name1,Pfs1}, {record,_,Name2,Pfs2},
#lint{records=Recs}=St) ->
case {dict:find(Name1, Recs),dict:find(Name2, Recs)} of
{{ok,{_Line1,Fields1}},{ok,{_Line2,Fields2}}} ->
@@ -1454,7 +1539,7 @@ is_pattern_expr_1({op,_Line,Op,A1,A2}) ->
erl_internal:arith_op(Op, 2) andalso all(fun is_pattern_expr/1, [A1,A2]);
is_pattern_expr_1(_Other) -> false.
-%% pattern_bin([Element], VarTable, Old, BinVarTable, State) ->
+%% pattern_bin([Element], VarTable, Old, BinVarTable, State) ->
%% {UpdVarTable,UpdBinVarTable,State}.
%% Check a pattern group. BinVarTable are used binsize variables.
@@ -1501,7 +1586,7 @@ good_string_size_type(default, Ts) ->
end, Ts);
good_string_size_type(_, _) -> false.
-%% pat_bit_expr(Pattern, OldVarTable, BinVarTable,State) ->
+%% pat_bit_expr(Pattern, OldVarTable, BinVarTable,State) ->
%% {UpdVarTable,UpdBinVarTable,State}.
%% Check pattern bit expression, only allow really valid patterns!
@@ -1516,7 +1601,7 @@ pat_bit_expr(P, _Old, _Bvt, St) ->
false -> {[],[],add_error(element(2, P), illegal_pattern, St)}
end.
-%% pat_bit_size(Size, VarTable, BinVarTable, State) ->
+%% pat_bit_size(Size, VarTable, BinVarTable, State) ->
%% {Value,UpdVarTable,UpdBinVarTable,State}.
%% Check pattern size expression, only allow really valid sizes!
@@ -1599,7 +1684,7 @@ bit_size_check(Line, Size, #bittype{type=Type,unit=Unit}, St) ->
Sz = Unit * Size, %Total number of bits!
St2 = elemtype_check(Line, Type, Sz, St),
{Sz,St2}.
-
+
elemtype_check(_Line, float, 32, St) -> St;
elemtype_check(_Line, float, 64, St) -> St;
elemtype_check(Line, float, _Size, St) ->
@@ -1681,8 +1766,6 @@ gexpr({cons,_Line,H,T}, Vt, St) ->
gexpr_list([H,T], Vt, St);
gexpr({tuple,_Line,Es}, Vt, St) ->
gexpr_list(Es, Vt, St);
-%%gexpr({struct,_Line,_Tag,Es}, Vt, St) ->
-%% gexpr_list(Es, Vt, St);
gexpr({record_index,Line,Name,Field}, _Vt, St) ->
check_record(Line, Name, St,
fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end );
@@ -1713,7 +1796,7 @@ gexpr({call,_Line,{atom,_Lr,is_record},[E,{atom,Ln,Name}]}, Vt, St0) ->
gexpr({call,Line,{atom,_Lr,is_record},[E,R]}, Vt, St0) ->
{Asvt,St1} = gexpr_list([E,R], Vt, St0),
{Asvt,add_error(Line, illegal_guard_expr, St1)};
-gexpr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,Lf,is_record}},[E,A]},
+gexpr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,Lf,is_record}},[E,A]},
Vt, St0) ->
gexpr({call,Line,{atom,Lf,is_record},[E,A]}, Vt, St0);
gexpr({call,_Line,{atom,_Lr,is_record},[E,{atom,_,_Name},{integer,_,_}]},
@@ -1728,14 +1811,22 @@ gexpr({call,Line,{remote,_,{atom,_,erlang},{atom,_,is_record}=Isr},[_,_,_]=Args}
gexpr({call,Line,{atom,_La,F},As}, Vt, St0) ->
{Asvt,St1} = gexpr_list(As, Vt, St0),
A = length(As),
- case erl_internal:guard_bif(F, A) of
+ %% BifClash - Function called in guard
+ case erl_internal:guard_bif(F, A) andalso no_guard_bif_clash(St1,{F,A}) of
true ->
%% Also check that it is auto-imported.
case erl_internal:bif(F, A) of
true -> {Asvt,St1};
false -> {Asvt,add_error(Line, {explicit_export,F,A}, St1)}
end;
- false -> {Asvt,add_error(Line, illegal_guard_expr, St1)}
+ false ->
+ case is_local_function(St1#lint.locals,{F,A}) orelse
+ is_imported_function(St1#lint.imports,{F,A}) of
+ true ->
+ {Asvt,add_error(Line, {illegal_guard_local_call,{F,A}}, St1)};
+ _ ->
+ {Asvt,add_error(Line, illegal_guard_expr, St1)}
+ end
end;
gexpr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,_Lf,F}},As}, Vt, St0) ->
{Asvt,St1} = gexpr_list(As, Vt, St0),
@@ -1780,7 +1871,7 @@ is_guard_test(E) ->
%% is_guard_test(Expression, Forms) -> boolean().
is_guard_test(Expression, Forms) ->
RecordAttributes = [A || A = {attribute, _, record, _D} <- Forms],
- St0 = foldl(fun(Attr0, St1) ->
+ St0 = foldl(fun(Attr0, St1) ->
Attr = zip_file_and_line(Attr0, "none"),
attribute_state(Attr, St1)
end, start(), RecordAttributes),
@@ -1801,7 +1892,7 @@ is_guard_test2(G, RDs) ->
%% 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, []).
is_gexpr({var,_L,_V}, _RDs) -> true;
is_gexpr({char,_L,_C}, _RDs) -> true;
@@ -1823,7 +1914,7 @@ is_gexpr({record_field,_L,Rec,_Name,Field}, RDs) ->
is_gexpr({record,L,Name,Inits}, RDs) ->
is_gexpr_fields(Inits, L, Name, RDs);
is_gexpr({bin,_L,Fs}, RDs) ->
- all(fun ({bin_element,_Line,E,Sz,_Ts}) ->
+ all(fun ({bin_element,_Line,E,Sz,_Ts}) ->
is_gexpr(E, RDs) and (Sz =:= default orelse is_gexpr(Sz, RDs))
end, Fs);
is_gexpr({call,_L,{atom,_Lf,F},As}, RDs) ->
@@ -1898,15 +1989,13 @@ expr({bc,_Line,E,Qs}, Vt0, St0) ->
{vtold(Vt,Vt0),St}; %Don't export local variables
expr({tuple,_Line,Es}, Vt, St) ->
expr_list(Es, Vt, St);
-%%expr({struct,Line,Tag,Es}, Vt, St) ->
-%% expr_list(Es, Vt, St);
expr({record_index,Line,Name,Field}, _Vt, St) ->
check_record(Line, Name, St,
fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end);
expr({record,Line,Name,Inits}, Vt, St) ->
check_record(Line, Name, St,
- fun (Dfs, St1) ->
- init_fields(Inits, Line, Name, Dfs, Vt, St1)
+ fun (Dfs, St1) ->
+ init_fields(Inits, Line, Name, Dfs, Vt, St1)
end);
expr({record_field,Line,_,_}=M, _Vt, St0) ->
case expand_package(M, St0) of
@@ -1943,8 +2032,6 @@ expr({'case',Line,E,Cs}, Vt, St0) ->
{Evt,St1} = expr(E, Vt, St0),
{Cvt,St2} = icrt_clauses(Cs, {'case',Line}, vtupdate(Evt, Vt), St1),
{vtmerge(Evt, Cvt),St2};
-expr({'cond',Line,Cs}, Vt, St) ->
- cond_clauses(Cs,{'cond',Line}, Vt, St);
expr({'receive',Line,Cs}, Vt, St) ->
icrt_clauses(Cs, {'receive',Line}, Vt, St);
expr({'receive',Line,Cs,To,ToEs}, Vt, St0) ->
@@ -1963,8 +2050,11 @@ expr({'fun',Line,Body}, Vt, St) ->
{Bvt, St1} = fun_clauses(Cs, Vt, St),
{vtupdate(Bvt, Vt), St1};
{function,F,A} ->
+ %% BifClash - Fun expression
%% N.B. Only allows BIFs here as well, NO IMPORTS!!
- case erl_internal:bif(F, A) of
+ case ((not is_local_function(St#lint.locals,{F,A})) andalso
+ (erl_internal:bif(F, A) andalso
+ (not is_autoimport_suppressed(St#lint.no_auto,{F,A})))) of
true -> {[],St};
false -> {[],call_function(Line, F, A, St)}
end;
@@ -1974,7 +2064,7 @@ expr({'fun',Line,Body}, Vt, St) ->
expr({call,_Line,{atom,_Lr,is_record},[E,{atom,Ln,Name}]}, Vt, St0) ->
{Rvt,St1} = expr(E, Vt, St0),
{Rvt,exist_record(Ln, Name, St1)};
-expr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,Lf,is_record}},[E,A]},
+expr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,Lf,is_record}},[E,A]},
Vt, St0) ->
expr({call,Line,{atom,Lf,is_record},[E,A]}, Vt, St0);
expr({call,L,{tuple,Lt,[{atom,Lm,erlang},{atom,Lf,is_record}]},As}, Vt, St) ->
@@ -1997,29 +2087,54 @@ expr({call,Line,{atom,La,F},As}, Vt, St0) ->
St1 = keyword_warning(La, F, St0),
{Asvt,St2} = expr_list(As, Vt, St1),
A = length(As),
- case erl_internal:bif(F, A) of
+ IsLocal = is_local_function(St2#lint.locals,{F,A}),
+ IsAutoBif = erl_internal:bif(F, A),
+ AutoSuppressed = is_autoimport_suppressed(St2#lint.no_auto,{F,A}),
+ Warn = is_warn_enabled(bif_clash, St2) and (not bif_clash_specifically_disabled(St2,{F,A})),
+ Imported = imported(F, A, St2),
+ case ((not IsLocal) andalso (Imported =:= no) andalso
+ IsAutoBif andalso (not AutoSuppressed)) of
true ->
St3 = deprecated_function(Line, erlang, F, As, St2),
- {Asvt,case is_warn_enabled(bif_clash, St3) andalso
- is_bif_clash(F, A, St3) of
- false ->
- St3;
- true ->
- add_error(Line, {call_to_redefined_bif,{F,A}}, St3)
- end};
+ {Asvt,St3};
false ->
- {Asvt,case imported(F, A, St2) of
+ {Asvt,case Imported of
{yes,M} ->
St3 = check_remote_function(Line, M, F, As, St2),
U0 = St3#lint.usage,
Imp = ordsets:add_element({{F,A},M},U0#usage.imported),
St3#lint{usage=U0#usage{imported = Imp}};
no ->
- case {F,A} of
- {record_info,2} ->
+ case {F,A} of
+ {record_info,2} ->
check_record_info_call(Line,La,As,St2);
- N when N =:= St2#lint.func -> St2;
- _ -> call_function(Line, F, A, St2)
+ N ->
+ %% BifClash - function call
+ %% Issue these warnings/errors even if it's a recursive call
+ St3 = if
+ (not AutoSuppressed) andalso IsAutoBif andalso Warn ->
+ case erl_internal:old_bif(F,A) of
+ true ->
+ add_error
+ (Line,
+ {call_to_redefined_old_bif, {F,A}},
+ St2);
+ false ->
+ add_warning
+ (Line,
+ {call_to_redefined_bif, {F,A}},
+ St2)
+ end;
+ true ->
+ St2
+ end,
+ %% ...but don't lint recursive calls
+ if
+ N =:= St3#lint.func ->
+ St3;
+ true ->
+ call_function(Line, F, A, St3)
+ end
end
end}
end;
@@ -2160,7 +2275,7 @@ def_fields(Fs0, Name, St0) ->
foldl(fun ({record_field,Lf,{atom,La,F},V}, {Fs,St}) ->
case exist_field(F, Fs) of
true -> {Fs,add_error(Lf, {redefine_field,Name,F}, St)};
- false ->
+ false ->
St1 = St#lint{recdef_top = true},
{_,St2} = expr(V, [], St1),
%% Warnings and errors found are kept, but
@@ -2311,7 +2426,7 @@ init_fields(Ifs, Line, Name, Dfs, Vt0, St0) ->
Defs = init_fields(Ifs, Line, Dfs),
{_,St2} = check_fields(Defs, Name, Dfs, Vt1, St1, fun expr/3),
{Vt1,St1#lint{usage = St2#lint.usage}}.
-
+
ginit_fields(Ifs, Line, Name, Dfs, Vt0, St0) ->
{Vt1,St1} = check_fields(Ifs, Name, Dfs, Vt0, St0, fun gexpr/3),
Defs = init_fields(Ifs, Line, Dfs),
@@ -2321,7 +2436,7 @@ ginit_fields(Ifs, Line, Name, Dfs, Vt0, St0) ->
IllErrs = [E || {_File,{_Line,erl_lint,illegal_guard_expr}}=E <- Errors],
St4 = St1#lint{usage = Usage, errors = IllErrs ++ St1#lint.errors},
{Vt1,St4}.
-
+
%% Default initializations to be carried out
init_fields(Ifs, Line, Dfs) ->
[ {record_field,Lf,{atom,La,F},copy_expr(Di, Line)} ||
@@ -2399,7 +2514,7 @@ check_type({ann_type, _L, [_Var, Type]}, SeenVars, St) ->
check_type(Type, SeenVars, St);
check_type({paren_type, _L, [Type]}, SeenVars, St) ->
check_type(Type, SeenVars, St);
-check_type({remote_type, L, [{atom, _, Mod}, {atom, _, Name}, Args]},
+check_type({remote_type, L, [{atom, _, Mod}, {atom, _, Name}, Args]},
SeenVars, #lint{module=CurrentMod} = St) ->
St1 =
case (dict:is_key({Name, length(Args)}, default_types())
@@ -2437,7 +2552,7 @@ check_type({type, L, 'fun', [Dom, Range]}, SeenVars, St) ->
check_type({type, -1, product, [Dom, Range]}, SeenVars, St1);
check_type({type, L, range, [From, To]}, SeenVars, St) ->
St1 =
- case {From, To} of
+ case {erl_eval:partial_eval(From), erl_eval:partial_eval(To)} of
{{integer, _, X}, {integer, _, Y}} when X < Y -> St;
_ -> add_error(L, {type_syntax, range}, St)
end,
@@ -2446,8 +2561,8 @@ check_type({type, _L, tuple, any}, SeenVars, St) -> {SeenVars, St};
check_type({type, _L, any}, SeenVars, St) -> {SeenVars, St};
check_type({type, L, binary, [Base, Unit]}, SeenVars, St) ->
St1 =
- case {Base, Unit} of
- {{integer, _, BaseVal},
+ case {erl_eval:partial_eval(Base), erl_eval:partial_eval(Unit)} of
+ {{integer, _, BaseVal},
{integer, _, UnitVal}} when BaseVal >= 0, UnitVal >= 0 -> St;
_ -> add_error(L, {type_syntax, binary}, St)
end,
@@ -2472,7 +2587,13 @@ check_type({type, La, TypeName, Args}, SeenVars, #lint{usage=Usage} = St) ->
UsedTypes = dict:store({TypeName, Arity}, La, OldUsed),
St#lint{usage=Usage#usage{used_types=UsedTypes}}
end,
- check_type({type, -1, product, Args}, SeenVars, St1).
+ check_type({type, -1, product, Args}, SeenVars, St1);
+check_type(I, SeenVars, St) ->
+ case erl_eval:partial_eval(I) of
+ {integer,_ILn,_Integer} -> {SeenVars, St};
+ _Other ->
+ {SeenVars, add_error(element(2, I), {type_syntax, integer}, St)}
+ end.
check_record_types(Line, Name, Fields, SeenVars, St) ->
case dict:find(Name, St#lint.records) of
@@ -2480,12 +2601,12 @@ check_record_types(Line, Name, Fields, SeenVars, St) ->
case lists:all(fun({type, _, field_type, _}) -> true;
(_) -> false
end, Fields) of
- true ->
+ true ->
check_record_types(Fields, Name, DefFields, SeenVars, St, []);
false ->
{SeenVars, add_error(Line, {type_syntax, record}, St)}
end;
- error ->
+ error ->
{SeenVars, add_error(Line, {undefined_record, Name}, St)}
end.
@@ -2568,7 +2689,6 @@ default_types() ->
{set, 0},
{string, 0},
{term, 0},
- {tid, 0},
{timeout, 0},
{var, 1}],
dict:from_list([{T, -1} || T <- DefTypes]).
@@ -2590,7 +2710,6 @@ is_newly_introduced_builtin_type({gb_tree, 0}) -> true; % opaque
is_newly_introduced_builtin_type({iodata, 0}) -> true;
is_newly_introduced_builtin_type({queue, 0}) -> true; % opaque
is_newly_introduced_builtin_type({set, 0}) -> true; % opaque
-is_newly_introduced_builtin_type({tid, 0}) -> true; % opaque
%% R13B01
is_newly_introduced_builtin_type({boolean, 0}) -> true;
is_newly_introduced_builtin_type({Name, _}) when is_atom(Name) -> false.
@@ -2611,7 +2730,7 @@ spec_decl(Line, MFA0, TypeSpecs, St0 = #lint{specs = Specs, module = Mod}) ->
check_specs([FunType|Left], Arity, St0) ->
{FunType1, CTypes} =
case FunType of
- {type, _, bounded_fun, [FT = {type, _, 'fun', _}, Cs]} ->
+ {type, _, bounded_fun, [FT = {type, _, 'fun', _}, Cs]} ->
Types0 = [T || {type, _, constraint, [_, T]} <- Cs],
{FT, lists:append(Types0)};
{type, _, 'fun', _} = FT -> {FT, []}
@@ -2671,10 +2790,12 @@ add_missing_spec_warnings(Forms, St0, Type) ->
add_warning(L, {missing_spec,FA}, St)
end, St0, Warns).
-check_unused_types(Forms, St = #lint{usage=Usage, types=Types}) ->
+check_unused_types(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) ->
case [File || {attribute,_L,file,{File,_Line}} <- Forms] of
[FirstFile|_] ->
- UsedTypes = Usage#usage.used_types,
+ D = Usage#usage.used_types,
+ L = gb_sets:to_list(ExpTs) ++ dict:fetch_keys(D),
+ UsedTypes = gb_sets:from_list(L),
FoldFun =
fun(_Type, -1, AccSt) ->
%% Default type
@@ -2682,19 +2803,18 @@ check_unused_types(Forms, St = #lint{usage=Usage, types=Types}) ->
(Type, FileLine, AccSt) ->
case loc(FileLine) of
{FirstFile, _} ->
- case dict:is_key(Type, UsedTypes) of
+ case gb_sets:is_member(Type, UsedTypes) of
true -> AccSt;
- false ->
- add_warning(FileLine,
- {unused_type, Type},
- AccSt)
+ false ->
+ Warn = {unused_type,Type},
+ add_warning(FileLine, Warn, AccSt)
end;
_ ->
- %% Don't warn about unused types in include file
+ %% No warns about unused types in include files
AccSt
end
end,
- dict:fold(FoldFun, St, Types);
+ dict:fold(FoldFun, St, Ts);
[] ->
St
end.
@@ -2720,45 +2840,6 @@ icrt_clause({clause,_Line,H,G,B}, Vt0, St0) ->
{Bvt,St3} = exprs(B, Vt2, St2),
{vtupdate(Bvt, Vt2),St3}.
-%% The tests of 'cond' clauses are normal expressions - not guards.
-%% Variables bound in a test is visible both in the corresponding body
-%% and in the tests and bodies of subsequent clauses: a 'cond' is
-%% *equivalent* to nested case-switches on boolean expressions.
-
-cond_clauses([C], In, Vt, St) ->
- last_cond_clause(C, In, Vt, St);
-cond_clauses([C | Cs], In, Vt, St) ->
- cond_clause(C, Cs, In, Vt, St).
-
-%% see expr/3 for 'case'
-cond_clause({clause,_L,[],[[E]],B}, Cs, In, Vt, St0) ->
- {Evt,St1} = expr(E, Vt, St0),
- {Cvt, St2} = cond_cases(B, Cs, In, vtupdate(Evt, Vt), St1),
- Mvt = vtmerge(Evt, Cvt),
- {Mvt,St2}.
-
-%% see icrt_clauses/4
-cond_cases(B, Cs, In, Vt, St0) ->
- %% note that Vt is used for both cases
- {Bvt,St1} = exprs(B, Vt, St0), % true case
- Vt1 = vtupdate(Bvt, Vt),
- {Cvt, St2} = cond_clauses(Cs, In, Vt, St1), % false case
- Vt2 = vtupdate(Cvt, Vt),
- %% and this also uses Vt
- icrt_export([Vt1,Vt2], Vt, In, St2).
-
-%% last case must call icrt_export/4 with only one vartable
-last_cond_clause({clause,_L,[],[[E]],B}, In, Vt, St0) ->
- {Evt,St1} = expr(E, Vt, St0),
- {Cvt, St2} = last_cond_case(B, In, vtupdate(Evt, Vt), St1),
- Mvt = vtmerge(Evt, Cvt),
- {Mvt,St2}.
-
-last_cond_case(B, In, Vt, St0) ->
- {Bvt,St1} = exprs(B, Vt, St0),
- Vt1 = vtupdate(Bvt, Vt),
- icrt_export([Vt1], Vt, In, St1).
-
icrt_export(Csvt, Vt, In, St) ->
Vt1 = vtmerge(Csvt),
All = ordsets:subtract(vintersection(Csvt), vtnames(Vt)),
@@ -2878,7 +2959,7 @@ fun_clause({clause,_Line,H,G,B}, Vt0, St0) ->
%%
%% used variable has been used
%% unused variable has been bound but not used
-%%
+%%
%% Lines is a list of line numbers where the variable was bound.
%%
%% Report variable errors/warnings as soon as possible and then change
@@ -2908,9 +2989,9 @@ pat_var(V, Line, Vt, Bvt, St) ->
case orddict:find(V, Bvt) of
{ok, {bound,_Usage,Ls}} ->
{[],[{V,{bound,used,Ls}}],St};
- error ->
+ error ->
case orddict:find(V, Vt) of
- {ok,{bound,_Usage,Ls}} ->
+ {ok,{bound,_Usage,Ls}} ->
{[{V,{bound,used,Ls}}],[],St};
{ok,{{unsafe,In},_Usage,Ls}} ->
{[{V,{bound,used,Ls}}],[],
@@ -2963,7 +3044,7 @@ pat_binsize_var(V, Line, Vt, Bvt, St) ->
expr_var(V, Line, Vt, St0) ->
case orddict:find(V, Vt) of
- {ok,{bound,_Usage,Ls}} ->
+ {ok,{bound,_Usage,Ls}} ->
{[{V,{bound,used,Ls}}],St0};
{ok,{{unsafe,In},_Usage,Ls}} ->
{[{V,{bound,used,Ls}}],
@@ -3001,7 +3082,7 @@ check_old_unused_vars(Vt, Vt0, St0) ->
warn_unused_vars(U, Vt, St0).
unused_vars(Vt, Vt0, _St0) ->
- U0 = orddict:filter(fun (V, {_State,unused,_Ls}) ->
+ U0 = orddict:filter(fun (V, {_State,unused,_Ls}) ->
case atom_to_list(V) of
"_"++_ -> false;
_ -> true
@@ -3017,7 +3098,7 @@ warn_unused_vars(U, Vt, St0) ->
false -> St0;
true ->
foldl(fun ({V,{_,unused,Ls}}, St) ->
- foldl(fun (L, St2) ->
+ foldl(fun (L, St2) ->
add_warning(L, {unused_var,V},
St2)
end, St, Ls)
@@ -3117,7 +3198,7 @@ vt_no_unsafe(Vt) -> [V || {_,{S,_U,_L}}=V <- Vt,
-ifdef(NOTUSED).
vunion(Vs1, Vs2) -> ordsets:union(vtnames(Vs1), vtnames(Vs2)).
-vunion(Vss) -> foldl(fun (Vs, Uvs) ->
+vunion(Vss) -> foldl(fun (Vs, Uvs) ->
ordsets:union(vtnames(Vs), Uvs)
end, [], Vss).
@@ -3147,7 +3228,7 @@ modify_line(T, F0) ->
%% Forms.
modify_line1({function,F,A}, _Mf) -> {function,F,A};
modify_line1({function,M,F,A}, _Mf) -> {function,M,F,A};
-modify_line1({attribute,L,record,{Name,Fields}}, Mf) ->
+modify_line1({attribute,L,record,{Name,Fields}}, Mf) ->
{attribute,Mf(L),record,{Name,modify_line1(Fields, Mf)}};
modify_line1({attribute,L,spec,{Fun,Types}}, Mf) ->
{attribute,Mf(L),spec,{Fun,modify_line1(Types, Mf)}};
@@ -3162,7 +3243,7 @@ modify_line1({warning,W}, _Mf) -> {warning,W};
modify_line1({error,W}, _Mf) -> {error,W};
%% Expressions.
modify_line1({clauses,Cs}, Mf) -> {clauses,modify_line1(Cs, Mf)};
-modify_line1({typed_record_field,Field,Type}, Mf) ->
+modify_line1({typed_record_field,Field,Type}, Mf) ->
{typed_record_field,modify_line1(Field, Mf),modify_line1(Type, Mf)};
modify_line1({Tag,L}, Mf) -> {Tag,Mf(L)};
modify_line1({Tag,L,E1}, Mf) ->
@@ -3198,7 +3279,7 @@ check_record_info_call(Line,_La,_As,St) ->
has_wildcard_field([{record_field,_Lf,{var,_La,'_'},_Val}|_Fs]) -> true;
has_wildcard_field([_|Fs]) -> has_wildcard_field(Fs);
has_wildcard_field([]) -> false.
-
+
%% check_remote_function(Line, ModuleName, FuncName, [Arg], State) -> State.
%% Perform checks on known remote calls.
@@ -3214,7 +3295,7 @@ check_remote_function(Line, M, F, As, St0) ->
check_qlc_hrl(Line, M, F, As, St) ->
Arity = length(As),
case As of
- [{lc,_L,_E,_Qs}|_] when M =:= qlc, F =:= q,
+ [{lc,_L,_E,_Qs}|_] when M =:= qlc, F =:= q,
Arity < 3, not St#lint.xqlc ->
add_warning(Line, {missing_qlc_hrl, Arity}, St);
_ ->
@@ -3399,11 +3480,11 @@ extract_sequence(3, [$.,_|Fmt], Need) ->
extract_sequence(4, Fmt, Need);
extract_sequence(3, Fmt, Need) ->
extract_sequence(4, Fmt, Need);
-extract_sequence(4, [$t, $c | Fmt], Need) ->
- extract_sequence(5, [$c|Fmt], Need);
-extract_sequence(4, [$t, $s | Fmt], Need) ->
- extract_sequence(5, [$s|Fmt], Need);
-extract_sequence(4, [$t, C | _Fmt], _Need) ->
+extract_sequence(4, [$t, $c | Fmt], Need) ->
+ extract_sequence(5, [$c|Fmt], Need);
+extract_sequence(4, [$t, $s | Fmt], Need) ->
+ extract_sequence(5, [$s|Fmt], Need);
+extract_sequence(4, [$t, C | _Fmt], _Need) ->
{error,"invalid control ~t" ++ [C]};
extract_sequence(4, Fmt, Need) ->
extract_sequence(5, Fmt, Need);
@@ -3481,3 +3562,56 @@ expand_package(M, St0) ->
{error, St1}
end
end.
+
+
+%% Prebuild set of local functions (to override auto-import)
+local_functions(Forms) ->
+ gb_sets:from_list([ {Func,Arity} || {function,_,Func,Arity,_} <- Forms ]).
+%% Predicate to find out if the function is locally defined
+is_local_function(LocalSet,{Func,Arity}) ->
+ gb_sets:is_element({Func,Arity},LocalSet).
+%% Predicate to see if a function is explicitly imported
+is_imported_function(ImportSet,{Func,Arity}) ->
+ case orddict:find({Func,Arity}, ImportSet) of
+ {ok,_Mod} -> true;
+ error -> false
+ end.
+%% Predicate to see if a function is explicitly imported from the erlang module
+is_imported_from_erlang(ImportSet,{Func,Arity}) ->
+ case orddict:find({Func,Arity}, ImportSet) of
+ {ok,erlang} -> true;
+ _ -> false
+ end.
+%% Build set of functions where auto-import is explicitly supressed
+auto_import_suppressed(CompileFlags) ->
+ L0 = [ X || {no_auto_import,X} <- CompileFlags ],
+ L1 = [ {Y,Z} || {Y,Z} <- lists:flatten(L0), is_atom(Y), is_integer(Z) ],
+ gb_sets:from_list(L1).
+%% Predicate to find out if autoimport is explicitly supressed for a function
+is_autoimport_suppressed(NoAutoSet,{Func,Arity}) ->
+ gb_sets:is_element({Func,Arity},NoAutoSet).
+%% Predicate to find out if a function specific bif-clash supression (old deprecated) is present
+bif_clash_specifically_disabled(St,{F,A}) ->
+ Nowarn = nowarn_function(nowarn_bif_clash, St#lint.compile),
+ lists:member({F,A},Nowarn).
+
+%% Predicate to find out if an autoimported guard_bif is not overriden in some way
+%% Guard Bif without module name is disallowed if
+%% * It is overridden by local function
+%% * It is overridden by -import and that import is not of itself (i.e. from module erlang)
+%% * The autoimport is suppressed or it's not reimported by -import directive
+%% Otherwise it's OK (given that it's actually a guard bif and actually is autoimported)
+no_guard_bif_clash(St,{F,A}) ->
+ (
+ (not is_local_function(St#lint.locals,{F,A}))
+ andalso
+ (
+ (not is_imported_function(St#lint.imports,{F,A})) orelse
+ is_imported_from_erlang(St#lint.imports,{F,A})
+ )
+ andalso
+ (
+ (not is_autoimport_suppressed(St#lint.no_auto, {F,A})) orelse
+ is_imported_from_erlang(St#lint.imports,{F,A})
+ )
+ ).
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index fd5d905797..bb4b18cf9b 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -1,20 +1,20 @@
%% -*- erlang -*-
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -30,14 +30,12 @@ expr_600 expr_700 expr_800 expr_900
expr_max
list tail
list_comprehension lc_expr lc_exprs
-binary_comprehension
+binary_comprehension
tuple
-atom1
%struct
record_expr record_tuple record_field record_fields
if_expr if_clause if_clauses case_expr cr_clause cr_clauses receive_expr
fun_expr fun_clause fun_clauses
-%% cond_expr cond_clause cond_clauses
try_expr try_catch try_clause try_clauses query_expr
function_call argument_list
exprs guard
@@ -49,22 +47,22 @@ opt_bit_size_expr bit_size_expr opt_bit_type_list bit_type_list bit_type
top_type top_type_100 top_types type typed_expr typed_attr_val
type_sig type_sigs type_guard type_guards fun_type fun_type_100 binary_type
type_spec spec_fun typed_exprs typed_record_fields field_types field_type
-bin_base_type bin_unit_type int_type.
+bin_base_type bin_unit_type type_200 type_300 type_400 type_500.
Terminals
char integer float atom string var
'(' ')' ',' '->' ':-' '{' '}' '[' ']' '|' '||' '<-' ';' ':' '#' '.'
'after' 'begin' 'case' 'try' 'catch' 'end' 'fun' 'if' 'of' 'receive' 'when'
-'andalso' 'orelse' 'query' 'spec'
-%% 'cond'
+'andalso' 'orelse' 'query'
'bnot' 'not'
'*' '/' 'div' 'rem' 'band' 'and'
'+' '-' 'bor' 'bxor' 'bsl' 'bsr' 'or' 'xor'
'++' '--'
'==' '/=' '=<' '<' '>=' '>' '=:=' '=/=' '<='
'<<' '>>'
-'!' '=' '::'
+'!' '=' '::' '..' '...'
+'spec' % helper
dot.
Expect 2.
@@ -79,19 +77,16 @@ attribute -> '-' atom attr_val : build_attribute('$2', '$3').
attribute -> '-' atom typed_attr_val : build_typed_attribute('$2','$3').
attribute -> '-' atom '(' typed_attr_val ')' : build_typed_attribute('$2','$4').
attribute -> '-' 'spec' type_spec : build_type_spec('$2', '$3').
-
-atom1 -> 'spec' : {atom, ?line('$1'), 'spec'}.
-atom1 -> atom : '$1'.
type_spec -> spec_fun type_sigs : {'$1', '$2'}.
type_spec -> '(' spec_fun type_sigs ')' : {'$2', '$3'}.
-spec_fun -> atom1 : '$1'.
-spec_fun -> atom1 ':' atom1 : {'$1', '$3'}.
+spec_fun -> atom : '$1'.
+spec_fun -> atom ':' atom : {'$1', '$3'}.
%% The following two are retained only for backwards compatibility;
%% they are not part of the EEP syntax and should be removed.
-spec_fun -> atom1 '/' integer '::' : {'$1', '$3'}.
-spec_fun -> atom1 ':' atom1 '/' integer '::' : {'$1', '$3', '$5'}.
+spec_fun -> atom '/' integer '::' : {'$1', '$3'}.
+spec_fun -> atom ':' atom '/' integer '::' : {'$1', '$3', '$5'}.
typed_attr_val -> expr ',' typed_record_fields : {typed_record, '$1', '$3'}.
typed_attr_val -> expr '::' top_type : {type_def, '$1', '$3'}.
@@ -109,14 +104,15 @@ type_sigs -> type_sig : ['$1'].
type_sigs -> type_sig ';' type_sigs : ['$1'|'$3'].
type_sig -> fun_type : '$1'.
-type_sig -> fun_type 'when' type_guards : {type, ?line('$1'), bounded_fun,
+type_sig -> fun_type 'when' type_guards : {type, ?line('$1'), bounded_fun,
['$1','$3']}.
type_guards -> type_guard : ['$1'].
type_guards -> type_guard ',' type_guards : ['$1'|'$3'].
-type_guard -> atom1 '(' top_types ')' : {type, ?line('$1'), constraint,
+type_guard -> atom '(' top_types ')' : {type, ?line('$1'), constraint,
['$1', '$3']}.
+type_guard -> var '::' top_type : build_def('$1', '$3').
top_types -> top_type : ['$1'].
top_types -> top_type ',' top_types : ['$1'|'$3'].
@@ -124,58 +120,68 @@ top_types -> top_type ',' top_types : ['$1'|'$3'].
top_type -> var '::' top_type_100 : {ann_type, ?line('$1'), ['$1','$3']}.
top_type -> top_type_100 : '$1'.
-top_type_100 -> type : '$1'.
-top_type_100 -> type '|' top_type_100 : lift_unions('$1','$3').
+top_type_100 -> type_200 : '$1'.
+top_type_100 -> type_200 '|' top_type_100 : lift_unions('$1','$3').
+
+type_200 -> type_300 '..' type_300 : {type, ?line('$1'), range,
+ [skip_paren('$1'),
+ skip_paren('$3')]}.
+type_200 -> type_300 : '$1'.
+
+type_300 -> type_300 add_op type_400 : ?mkop2(skip_paren('$1'),
+ '$2', skip_paren('$3')).
+type_300 -> type_400 : '$1'.
+
+type_400 -> type_400 mult_op type_500 : ?mkop2(skip_paren('$1'),
+ '$2', skip_paren('$3')).
+type_400 -> type_500 : '$1'.
+
+type_500 -> prefix_op type : ?mkop1('$1', skip_paren('$2')).
+type_500 -> type : '$1'.
type -> '(' top_type ')' : {paren_type, ?line('$2'), ['$2']}.
type -> var : '$1'.
-type -> atom1 : '$1'.
-type -> atom1 '(' ')' : build_gen_type('$1').
-type -> atom1 '(' top_types ')' : {type, ?line('$1'),
+type -> atom : '$1'.
+type -> atom '(' ')' : build_gen_type('$1').
+type -> atom '(' top_types ')' : {type, ?line('$1'),
normalise('$1'), '$3'}.
-type -> atom1 ':' atom1 '(' ')' : {remote_type, ?line('$1'),
+type -> atom ':' atom '(' ')' : {remote_type, ?line('$1'),
['$1', '$3', []]}.
-type -> atom1 ':' atom1 '(' top_types ')' : {remote_type, ?line('$1'),
+type -> atom ':' atom '(' top_types ')' : {remote_type, ?line('$1'),
['$1', '$3', '$5']}.
type -> '[' ']' : {type, ?line('$1'), nil, []}.
type -> '[' top_type ']' : {type, ?line('$1'), list, ['$2']}.
-type -> '[' top_type ',' '.' '.' '.' ']' : {type, ?line('$1'),
+type -> '[' top_type ',' '...' ']' : {type, ?line('$1'),
nonempty_list, ['$2']}.
type -> '{' '}' : {type, ?line('$1'), tuple, []}.
type -> '{' top_types '}' : {type, ?line('$1'), tuple, '$2'}.
-type -> '#' atom1 '{' '}' : {type, ?line('$1'), record, ['$2']}.
-type -> '#' atom1 '{' field_types '}' : {type, ?line('$1'),
+type -> '#' atom '{' '}' : {type, ?line('$1'), record, ['$2']}.
+type -> '#' atom '{' field_types '}' : {type, ?line('$1'),
record, ['$2'|'$4']}.
type -> binary_type : '$1'.
-type -> int_type : '$1'.
-type -> int_type '.' '.' int_type : {type, ?line('$1'), range,
- ['$1', '$4']}.
+type -> integer : '$1'.
type -> 'fun' '(' ')' : {type, ?line('$1'), 'fun', []}.
type -> 'fun' '(' fun_type_100 ')' : '$3'.
-int_type -> integer : '$1'.
-int_type -> '-' integer : abstract(-normalise('$2'),
- ?line('$2')).
-
-fun_type_100 -> '(' '.' '.' '.' ')' '->' top_type
+fun_type_100 -> '(' '...' ')' '->' top_type
: {type, ?line('$1'), 'fun',
- [{type, ?line('$1'), any}, '$7']}.
+ [{type, ?line('$1'), any}, '$5']}.
fun_type_100 -> fun_type : '$1'.
fun_type -> '(' ')' '->' top_type : {type, ?line('$1'), 'fun',
[{type, ?line('$1'), product, []}, '$4']}.
-fun_type -> '(' top_types ')' '->' top_type
+fun_type -> '(' top_types ')' '->' top_type
: {type, ?line('$1'), 'fun',
[{type, ?line('$1'), product, '$2'},'$5']}.
field_types -> field_type : ['$1'].
field_types -> field_type ',' field_types : ['$1'|'$3'].
-field_type -> atom1 '::' top_type : {type, ?line('$1'), field_type,
+field_type -> atom '::' top_type : {type, ?line('$1'), field_type,
['$1', '$3']}.
-binary_type -> '<<' '>>' : {type, ?line('$1'),binary,
- [abstract(0, ?line('$1')),
+binary_type -> '<<' '>>' : {type, ?line('$1'),binary,
+ [abstract(0, ?line('$1')),
abstract(0, ?line('$1'))]}.
binary_type -> '<<' bin_base_type '>>' : {type, ?line('$1'),binary,
['$2', abstract(0, ?line('$1'))]}.
@@ -184,9 +190,9 @@ binary_type -> '<<' bin_unit_type '>>' : {type, ?line('$1'),binary,
binary_type -> '<<' bin_base_type ',' bin_unit_type '>>'
: {type, ?line('$1'), binary, ['$2', '$4']}.
-bin_base_type -> var ':' integer : build_bin_type(['$1'], '$3').
+bin_base_type -> var ':' type : build_bin_type(['$1'], '$3').
-bin_unit_type -> var ':' var '*' integer : build_bin_type(['$1', '$3'], '$5').
+bin_unit_type -> var ':' var '*' type : build_bin_type(['$1', '$3'], '$5').
attr_val -> expr : ['$1'].
attr_val -> expr ',' exprs : ['$1' | '$3'].
@@ -197,7 +203,7 @@ function -> function_clauses : build_function('$1').
function_clauses -> function_clause : ['$1'].
function_clauses -> function_clause ';' function_clauses : ['$1'|'$3'].
-function_clause -> atom1 clause_args clause_guard clause_body :
+function_clause -> atom clause_args clause_guard clause_body :
{clause,?line('$1'),element(3, '$1'),'$2','$3','$4'}.
@@ -250,9 +256,9 @@ expr_800 -> expr_900 ':' expr_max :
{remote,?line('$2'),'$1','$3'}.
expr_800 -> expr_900 : '$1'.
-expr_900 -> '.' atom1 :
+expr_900 -> '.' atom :
{record_field,?line('$1'),{atom,?line('$1'),''},'$2'}.
-expr_900 -> expr_900 '.' atom1 :
+expr_900 -> expr_900 '.' atom :
{record_field,?line('$2'),'$1','$3'}.
expr_900 -> expr_max : '$1'.
@@ -270,7 +276,6 @@ expr_max -> if_expr : '$1'.
expr_max -> case_expr : '$1'.
expr_max -> receive_expr : '$1'.
expr_max -> fun_expr : '$1'.
-%%expr_max -> cond_expr : '$1'.
expr_max -> try_expr : '$1'.
expr_max -> query_expr : '$1'.
@@ -304,8 +309,8 @@ opt_bit_type_list -> '$empty' : default.
bit_type_list -> bit_type '-' bit_type_list : ['$1' | '$3'].
bit_type_list -> bit_type : ['$1'].
-bit_type -> atom1 : element(3,'$1').
-bit_type -> atom1 ':' integer : { element(3,'$1'), element(3,'$3') }.
+bit_type -> atom : element(3,'$1').
+bit_type -> atom ':' integer : { element(3,'$1'), element(3,'$3') }.
bit_size_expr -> expr_max : '$1'.
@@ -325,7 +330,7 @@ tuple -> '{' '}' : {tuple,?line('$1'),[]}.
tuple -> '{' exprs '}' : {tuple,?line('$1'),'$2'}.
-%%struct -> atom1 tuple :
+%%struct -> atom tuple :
%% {struct,?line('$1'),element(3, '$1'),element(3, '$2')}.
@@ -333,13 +338,17 @@ tuple -> '{' exprs '}' : {tuple,?line('$1'),'$2'}.
%% N.B. Field names are returned as the complete object, even if they are
%% always atoms for the moment, this might change in the future.
-record_expr -> '#' atom1 '.' atom1 :
+record_expr -> '#' atom '.' atom :
{record_index,?line('$1'),element(3, '$2'),'$4'}.
-record_expr -> '#' atom1 record_tuple :
+record_expr -> '#' atom record_tuple :
{record,?line('$1'),element(3, '$2'),'$3'}.
-record_expr -> expr_max '#' atom1 '.' atom1 :
+record_expr -> expr_max '#' atom '.' atom :
{record_field,?line('$2'),'$1',element(3, '$3'),'$5'}.
-record_expr -> expr_max '#' atom1 record_tuple :
+record_expr -> expr_max '#' atom record_tuple :
+ {record,?line('$2'),'$1',element(3, '$3'),'$4'}.
+record_expr -> record_expr '#' atom '.' atom :
+ {record_field,?line('$2'),'$1',element(3, '$3'),'$5'}.
+record_expr -> record_expr '#' atom record_tuple :
{record,?line('$2'),'$1',element(3, '$3'),'$4'}.
record_tuple -> '{' '}' : [].
@@ -349,7 +358,7 @@ record_fields -> record_field : ['$1'].
record_fields -> record_field ',' record_fields : ['$1' | '$3'].
record_field -> var '=' expr : {record_field,?line('$1'),'$1','$3'}.
-record_field -> atom1 '=' expr : {record_field,?line('$1'),'$1','$3'}.
+record_field -> atom '=' expr : {record_field,?line('$1'),'$1','$3'}.
%% N.B. This is called from expr_700.
@@ -383,9 +392,9 @@ receive_expr -> 'receive' cr_clauses 'after' expr clause_body 'end' :
{'receive',?line('$1'),'$2','$4','$5'}.
-fun_expr -> 'fun' atom1 '/' integer :
+fun_expr -> 'fun' atom '/' integer :
{'fun',?line('$1'),{function,element(3, '$2'),element(3, '$4')}}.
-fun_expr -> 'fun' atom1 ':' atom1 '/' integer :
+fun_expr -> 'fun' atom ':' atom '/' integer :
{'fun',?line('$1'),{function,element(3, '$2'),element(3, '$4'),element(3,'$6')}}.
fun_expr -> 'fun' fun_clauses 'end' :
build_fun(?line('$1'), '$2').
@@ -415,21 +424,13 @@ try_clauses -> try_clause ';' try_clauses : ['$1' | '$3'].
try_clause -> expr clause_guard clause_body :
L = ?line('$1'),
{clause,L,[{tuple,L,[{atom,L,throw},'$1',{var,L,'_'}]}],'$2','$3'}.
-try_clause -> atom1 ':' expr clause_guard clause_body :
+try_clause -> atom ':' expr clause_guard clause_body :
L = ?line('$1'),
{clause,L,[{tuple,L,['$1','$3',{var,L,'_'}]}],'$4','$5'}.
try_clause -> var ':' expr clause_guard clause_body :
L = ?line('$1'),
{clause,L,[{tuple,L,['$1','$3',{var,L,'_'}]}],'$4','$5'}.
-%%cond_expr -> 'cond' cond_clauses 'end' : {'cond',?line('$1'),'$2'}.
-
-%%cond_clauses -> cond_clause : ['$1'].
-%%cond_clauses -> cond_clause ';' cond_clauses : ['$1' | '$3'].
-
-%%cond_clause -> expr clause_body :
-%% {clause,?line('$1'),[],[['$1']],'$2'}.
-
query_expr -> 'query' list_comprehension 'end' :
{'query',?line('$1'),'$2'}.
@@ -447,7 +448,7 @@ guard -> exprs ';' guard : ['$1'|'$3'].
atomic -> char : '$1'.
atomic -> integer : '$1'.
atomic -> float : '$1'.
-atomic -> atom1 : '$1'.
+atomic -> atom : '$1'.
atomic -> strings : '$1'.
strings -> string : '$1'.
@@ -492,7 +493,7 @@ rule -> rule_clauses : build_rule('$1').
rule_clauses -> rule_clause : ['$1'].
rule_clauses -> rule_clause ';' rule_clauses : ['$1'|'$3'].
-rule_clause -> atom1 clause_args clause_guard rule_body :
+rule_clause -> atom clause_args clause_guard rule_body :
{clause,?line('$1'),element(3, '$1'),'$2','$3','$4'}.
rule_body -> ':-' lc_exprs: '$2'.
@@ -514,8 +515,8 @@ Erlang code.
%% mkop(Op, Arg) -> {op,Line,Op,Arg}.
%% mkop(Left, Op, Right) -> {op,Line,Op,Left,Right}.
--define(mkop2(L, OpPos, R),
- begin
+-define(mkop2(L, OpPos, R),
+ begin
{Op,Pos} = OpPos,
{op,Pos,Op,L,R}
end).
@@ -533,6 +534,8 @@ Erlang code.
%% These really suck and are only here until Calle gets multiple
%% entry points working.
+parse_form([{'-',L1},{atom,L2,spec}|Tokens]) ->
+ parse([{'-',L1},{'spec',L2}|Tokens]);
parse_form(Tokens) ->
parse(Tokens).
@@ -559,7 +562,7 @@ parse_term(Tokens) ->
-type attributes() :: 'export' | 'file' | 'import' | 'module'
| 'opaque' | 'record' | 'type'.
-build_typed_attribute({atom,La,record},
+build_typed_attribute({atom,La,record},
{typed_record, {atom,_Ln,RecordName}, RecTuple}) ->
{attribute,La,record,{RecordName,record_tuple(RecTuple)}};
build_typed_attribute({atom,La,Attr},
@@ -582,7 +585,7 @@ build_typed_attribute({atom,La,Attr},_) ->
build_type_spec({spec,La}, {SpecFun, TypeSpecs}) ->
NewSpecFun =
case SpecFun of
- {atom, _, Fun} ->
+ {atom, _, Fun} ->
{Fun, find_arity_from_specs(TypeSpecs)};
{{atom,_, Mod}, {atom,_, Fun}} ->
{Mod,Fun,find_arity_from_specs(TypeSpecs)};
@@ -605,11 +608,20 @@ find_arity_from_specs([Spec|_]) ->
{type, _, 'fun', [{type, _, product, Args},_]} = Fun,
length(Args).
+build_def(LHS, Types) ->
+ IsSubType = {atom, ?line(LHS), is_subtype},
+ {type, ?line(LHS), constraint, [IsSubType, [LHS, Types]]}.
+
lift_unions(T1, {type, _La, union, List}) ->
{type, ?line(T1), union, [T1|List]};
lift_unions(T1, T2) ->
{type, ?line(T1), union, [T1, T2]}.
+skip_paren({paren_type,_L,[Type]}) ->
+ skip_paren(Type);
+skip_paren(Type) ->
+ Type.
+
build_gen_type({atom, La, tuple}) ->
{type, La, tuple, any};
build_gen_type({atom, La, Name}) ->
@@ -618,7 +630,7 @@ build_gen_type({atom, La, Name}) ->
build_bin_type([{var, _, '_'}|Left], Int) ->
build_bin_type(Left, Int);
build_bin_type([], Int) ->
- Int;
+ skip_paren(Int);
build_bin_type([{var, La, _}|_], _) ->
ret_err(La, "Bad binary type").
@@ -716,7 +728,7 @@ attribute_farity(Other) -> Other.
attribute_farity_list(Args) ->
[attribute_farity(A) || A <- Args].
-
+
-spec error_bad_decl(integer(), attributes()) -> no_return().
error_bad_decl(L, S) ->
@@ -739,17 +751,33 @@ record_fields([{match,_Lm,{atom,La,A},Expr}|Fields]) ->
[{record_field,La,{atom,La,A},Expr}|record_fields(Fields)];
record_fields([{typed,Expr,TypeInfo}|Fields]) ->
[Field] = record_fields([Expr]),
- TypeInfo1 =
+ TypeInfo1 =
case Expr of
{match, _, _, _} -> TypeInfo; %% If we have an initializer.
- {atom, La, _} ->
- lift_unions(abstract(undefined, La), TypeInfo)
- end,
+ {atom, La, _} ->
+ case has_undefined(TypeInfo) of
+ false ->
+ lift_unions(abstract(undefined, La), TypeInfo);
+ true ->
+ TypeInfo
+ end
+ end,
[{typed_record_field,Field,TypeInfo1}|record_fields(Fields)];
record_fields([Other|_Fields]) ->
ret_err(?line(Other), "bad record field");
record_fields([]) -> [].
+has_undefined({atom,_,undefined}) ->
+ true;
+has_undefined({ann_type,_,[_,T]}) ->
+ has_undefined(T);
+has_undefined({paren_type,_,[T]}) ->
+ has_undefined(T);
+has_undefined({type,_,union,Ts}) ->
+ lists:any(fun has_undefined/1, Ts);
+has_undefined(_) ->
+ false.
+
term(Expr) ->
try normalise(Expr)
catch _:_R -> ret_err(?line(Expr), "bad attribute")
@@ -989,7 +1017,7 @@ inop_prec('#') -> {800,700,800};
inop_prec(':') -> {900,800,900};
inop_prec('.') -> {900,900,1000}.
--type pre_op() :: 'catch' | '+' | '-' | 'bnot' | '#'.
+-type pre_op() :: 'catch' | '+' | '-' | 'bnot' | 'not' | '#'.
-spec preop_prec(pre_op()) -> {0 | 600 | 700, 100 | 700 | 800}.
diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl
index b1b5bad294..df4a20b833 100644
--- a/lib/stdlib/src/erl_pp.erl
+++ b/lib/stdlib/src/erl_pp.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(erl_pp).
@@ -115,7 +115,7 @@ lattribute({attribute,_Line,Name,Arg}, Hook) ->
lattribute(module, {M,Vs}, _Hook) ->
attr("module",[{var,0,pname(M)},
- foldr(fun(V, C) -> {cons,0,{var,0,V},C}
+ foldr(fun(V, C) -> {cons,0,{var,0,V},C}
end, {nil,0}, Vs)]);
lattribute(module, M, _Hook) ->
attr("module", [{var,0,pname(M)}]);
@@ -140,7 +140,7 @@ typeattr(Tag, {TypeName,Type,Args}, _Hook) ->
ltype({ann_type,_Line,[V,T]}) ->
typed(lexpr(V, none), T);
ltype({paren_type,_Line,[T]}) ->
- [$(,ltype(T),$)];
+ [$(,ltype(T),$)];
ltype({type,_Line,union,Ts}) ->
{seq,[],[],[' |'],ltypes(Ts)};
ltype({type,_Line,list,[T]}) ->
@@ -153,7 +153,7 @@ ltype({type,Line,tuple,any}) ->
simple_type({atom,Line,tuple}, []);
ltype({type,_Line,tuple,Ts}) ->
tuple_type(Ts, fun ltype/1);
-ltype({type,_Line,record,[N|Fs]}) ->
+ltype({type,_Line,record,[{atom,_,N}|Fs]}) ->
record_type(N, Fs);
ltype({type,_Line,range,[_I1,_I2]=Es}) ->
expr_list(Es, '..', fun lexpr/2, none);
@@ -161,24 +161,28 @@ ltype({type,_Line,binary,[I1,I2]}) ->
binary_type(I1, I2); % except binary()
ltype({type,_Line,'fun',[]}) ->
leaf("fun()");
-ltype({type,_Line,'fun',_}=FunType) ->
+ltype({type,_,'fun',[{type,_,any},_]}=FunType) ->
+ [fun_type(['fun',$(], FunType),$)];
+ltype({type,_Line,'fun',[{type,_,product,_},_]}=FunType) ->
[fun_type(['fun',$(], FunType),$)];
ltype({type,Line,T,Ts}) ->
simple_type({atom,Line,T}, Ts);
ltype({remote_type,Line,[M,F,Ts]}) ->
simple_type({remote,Line,M,F}, Ts);
ltype({atom,_,T}) ->
- %% Follow the convention to always quote atoms (in types):
- leaf([$',atom_to_list(T),$']);
+ leaf(write(T));
ltype(E) ->
lexpr(E, 0, none).
-binary_type({integer,_,Int1}=I1, {integer,_,Int2}=I2) ->
- E1 = [[leaf("_:"),lexpr(I1, 0, none)] || Int1 =/= 0],
- E2 = [[leaf("_:_*"),lexpr(I2, 0, none)] || Int2 =/= 0],
+binary_type(I1, I2) ->
+ B = [[] || {integer,_,0} <- [I1]] =:= [],
+ U = [[] || {integer,_,0} <- [I2]] =:= [],
+ P = max_prec(),
+ E1 = [[leaf("_:"),lexpr(I1, P, none)] || B],
+ E2 = [[leaf("_:_*"),lexpr(I2, P, none)] || U],
{seq,'<<','>>',[$,],E1++E2}.
-record_type({atom,_,Name}, Fields) ->
+record_type(Name, Fields) ->
{first,[record_name(Name)],field_types(Fields)}.
field_types(Fs) ->
@@ -442,7 +446,7 @@ lexpr({op,_,Op,Arg}, Prec, Hook) ->
Ol = leaf(format("~s ", [Op])),
El = [Ol,lexpr(Arg, R, Hook)],
maybe_paren(P, Prec, El);
-lexpr({op,_,Op,Larg,Rarg}, Prec, Hook) when Op =:= 'orelse';
+lexpr({op,_,Op,Larg,Rarg}, Prec, Hook) when Op =:= 'orelse';
Op =:= 'andalso' ->
%% Breaks lines since R12B.
{L,P,R} = inop_prec(Op),
@@ -726,15 +730,15 @@ frmt(Item, I) ->
%%% and indentation are inserted between IPs.
%%% - {first,I,IP2}: IP2 follows after I, and is output with an indentation
%%% updated with the width of I.
-%%% - {seq,Before,After,Separator,IPs}: a sequence of Is separated by
-%%% Separator. Before is output before IPs, and the indentation of IPs
+%%% - {seq,Before,After,Separator,IPs}: a sequence of Is separated by
+%%% Separator. Before is output before IPs, and the indentation of IPs
%%% is updated with the width of Before. After follows after IPs.
%%% - {force_nl,ExtraInfo,I}: fun-info (a comment) forces linebreak before I.
%%% - {prefer_nl,Sep,IPs}: forces linebreak between Is unlesss negative
%%% indentation.
%%% - {string,S}: a string.
%%% - {hook,...}, {ehook,...}: hook expressions.
-%%%
+%%%
%%% list, first, seq, force_nl, and prefer_nl all accept IPs, where each
%%% element is either an item or a tuple {step|cstep,I1,I2}. step means
%%% that I2 is output after linebreak and an incremented indentation.
@@ -760,7 +764,7 @@ f({seq,Before,After,Sep,LItems}, I0, ST, WT) ->
{CharsL,SizeL} = unz(CharsSizeL),
{BCharsL,BSizeL} = unz1([BCharsSize]),
Sizes = BSizeL ++ SizeL,
- NSepChars = if
+ NSepChars = if
is_list(Sep), Sep =/= [] ->
erlang:max(0, length(CharsL)-1);
true ->
@@ -875,7 +879,7 @@ nl_indent(I, T) when I > 0 ->
[$\n|spaces(I, T)].
same_line(I0, SizeL, NSepChars) ->
- try
+ try
Size = lists:sum(SizeL) + NSepChars,
true = incr(I0, Size) =< ?MAXLINE,
{yes,Size}
@@ -955,9 +959,9 @@ write_a_string(S, N, Len) ->
-define(N_SPACES, 30).
spacetab() ->
- {[_|L],_} = mapfoldl(fun(_, A) -> {A,[$\s|A]}
+ {[_|L],_} = mapfoldl(fun(_, A) -> {A,[$\s|A]}
end, [], lists:seq(0, ?N_SPACES)),
- list_to_tuple(L).
+ list_to_tuple(L).
spaces(N, T) when N =< ?N_SPACES ->
element(N, T);
@@ -965,7 +969,7 @@ spaces(N, T) ->
[element(?N_SPACES, T)|spaces(N-?N_SPACES, T)].
wordtable() ->
- L = [begin {leaf,Sz,S} = leaf(W), {S,Sz} end ||
+ L = [begin {leaf,Sz,S} = leaf(W), {S,Sz} end ||
W <- [" ->"," =","<<",">>","[]","after","begin","case","catch",
"end","fun","if","of","receive","try","when"," ::","..",
" |"]],
diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl
index 52ec81a78b..18f64c46d0 100644
--- a/lib/stdlib/src/erl_scan.erl
+++ b/lib/stdlib/src/erl_scan.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -48,25 +48,20 @@
-module(erl_scan).
-%%% External exports
+%%% External exports
-export([string/1,string/2,string/3,tokens/3,tokens/4,
format_error/1,reserved_word/1,
token_info/1,token_info/2,
attributes_info/1,attributes_info/2,set_attribute/3]).
-%%% Local record.
--record(erl_scan,
- {resword_fun=fun reserved_word/1,
- ws=false,
- comment=false,
- text=false}).
+-export_type([error_info/0, line/0, tokens_result/0]).
%%%
-%%% Exported functions
+%%% Defines and type definitions
%%%
--define(COLUMN(C), is_integer(C), C >= 1).
+-define(COLUMN(C), (is_integer(C) andalso C >= 1)).
%% Line numbers less than zero have always been allowed:
-define(ALINE(L), is_integer(L)).
-define(STRING(S), is_list(S)).
@@ -95,44 +90,53 @@
-type error_description() :: term().
-type error_info() :: {location(), module(), error_description()}.
+%%% Local record.
+-record(erl_scan,
+ {resword_fun = fun reserved_word/1 :: resword_fun(),
+ ws = false :: boolean(),
+ comment = false :: boolean(),
+ text = false :: boolean()}).
+
+%%----------------------------------------------------------------------------
+
-spec format_error(Error :: term()) -> string().
format_error({string,Quote,Head}) ->
lists:flatten(["unterminated " ++ string_thing(Quote) ++
- " starting with " ++
+ " starting with " ++
io_lib:write_unicode_string(Head, Quote)]);
-format_error({illegal,Type}) ->
+format_error({illegal,Type}) ->
lists:flatten(io_lib:fwrite("illegal ~w", [Type]));
format_error(char) -> "unterminated character";
-format_error({base,Base}) ->
+format_error({base,Base}) ->
lists:flatten(io_lib:fwrite("illegal base '~w'", [Base]));
-format_error(Other) ->
+format_error(Other) ->
lists:flatten(io_lib:write(Other)).
--type string_return() :: {'ok', tokens(), location()}
+-type string_return() :: {'ok', tokens(), location()}
| {'error', error_info(), location()}.
-spec string(String :: string()) -> string_return().
string(String) ->
string(String, 1, []).
--spec string(String :: string(), StartLocation :: location()) ->
+-spec string(String :: string(), StartLocation :: location()) ->
string_return().
string(String, StartLocation) ->
string(String, StartLocation, []).
--spec string(String :: string(), StartLocation :: location(),
+-spec string(String :: string(), StartLocation :: location(),
Options :: options()) -> string_return().
string(String, Line, Options) when ?STRING(String), ?ALINE(Line) ->
string1(String, options(Options), Line, no_col, []);
string(String, {Line,Column}, Options) when ?STRING(String),
- ?ALINE(Line),
+ ?ALINE(Line),
?COLUMN(Column) ->
string1(String, options(Options), Line, Column, []).
-type char_spec() :: string() | 'eof'.
-type cont_fun() :: fun((char_spec(), #erl_scan{}, line(), column(),
tokens(), any()) -> any()).
--opaque return_cont() :: {string(), column(), tokens(), line(),
+-opaque return_cont() :: {string(), column(), tokens(), line(),
#erl_scan{}, cont_fun(), any()}.
-type cont() :: return_cont() | [].
-type tokens_result() :: {'ok', tokens(), location()}
@@ -141,13 +145,13 @@ string(String, {Line,Column}, Options) when ?STRING(String),
-type tokens_return() :: {'done', tokens_result(), char_spec()}
| {'more', return_cont()}.
--spec tokens(Cont :: cont(), CharSpec :: char_spec(),
+-spec tokens(Cont :: cont(), CharSpec :: char_spec(),
StartLocation :: location()) -> tokens_return().
tokens(Cont, CharSpec, StartLocation) ->
tokens(Cont, CharSpec, StartLocation, []).
--spec tokens(Cont :: cont(), CharSpec :: char_spec(),
- StartLocation :: location(), Options :: options()) ->
+-spec tokens(Cont :: cont(), CharSpec :: char_spec(),
+ StartLocation :: location(), Options :: options()) ->
tokens_return().
tokens([], CharSpec, Line, Options) when ?ALINE(Line) ->
tokens1(CharSpec, options(Options), Line, no_col, [], fun scan/6, []);
@@ -157,15 +161,15 @@ tokens([], CharSpec, {Line,Column}, Options) when ?ALINE(Line),
tokens({Cs,Col,Toks,Line,St,Any,Fun}, CharSpec, _Loc, _Opts) ->
tokens1(Cs++CharSpec, St, Line, Col, Toks, Fun, Any).
--type attribute_item() :: 'column' | 'length' | 'line'
+-type attribute_item() :: 'column' | 'length' | 'line'
| 'location' | 'text'.
-type info_location() :: location() | term().
--type attribute_info() :: {'column', column()}| {'length', pos_integer()}
- | {'line', info_line()}
+-type attribute_info() :: {'column', column()}| {'length', pos_integer()}
+ | {'line', info_line()}
| {'location', info_location()}
| {'text', string()}.
-type token_item() :: 'category' | 'symbol' | attribute_item().
--type token_info() :: {'category', category()} | {'symbol', symbol()}
+-type token_info() :: {'category', category()} | {'symbol', symbol()}
| attribute_info().
-spec token_info(token()) -> [token_info()].
@@ -214,7 +218,7 @@ attributes_info(Attrs, [A|As]) when is_atom(A) ->
AttributeInfo when is_tuple(AttributeInfo) ->
[AttributeInfo|attributes_info(Attrs, As)]
end;
-attributes_info({Line,Column}, column=Item) when ?ALINE(Line),
+attributes_info({Line,Column}, column=Item) when ?ALINE(Line),
?COLUMN(Column) ->
{Item,Column};
attributes_info(Line, column) when ?ALINE(Line) ->
@@ -230,12 +234,12 @@ attributes_info(Attrs, length=Item) ->
end;
attributes_info(Line, line=Item) when ?ALINE(Line) ->
{Item,Line};
-attributes_info({Line,Column}, line=Item) when ?ALINE(Line),
+attributes_info({Line,Column}, line=Item) when ?ALINE(Line),
?COLUMN(Column) ->
{Item,Line};
attributes_info(Attrs, line=Item) ->
attr_info(Attrs, Item);
-attributes_info({Line,Column}=Location, location=Item) when ?ALINE(Line),
+attributes_info({Line,Column}=Location, location=Item) when ?ALINE(Line),
?COLUMN(Column) ->
{Item,Location};
attributes_info(Line, location=Item) when ?ALINE(Line) ->
@@ -289,11 +293,11 @@ string_thing(_) -> "string".
options(Opts0) when is_list(Opts0) ->
Opts = lists:foldr(fun expand_opt/2, [], Opts0),
- [RW_fun] =
+ [RW_fun] =
case opts(Opts, [reserved_word_fun], []) of
badarg ->
erlang:error(badarg, [Opts0]);
- R ->
+ R ->
R
end,
Comment = proplists:get_bool(return_comments, Opts),
@@ -307,10 +311,10 @@ options(Opt) ->
options([Opt]).
opts(Options, [Key|Keys], L) ->
- V = case lists:keysearch(Key, 1, Options) of
- {value,{reserved_word_fun,F}} when ?RESWORDFUN(F) ->
+ V = case lists:keyfind(Key, 1, Options) of
+ {reserved_word_fun,F} when ?RESWORDFUN(F) ->
{ok,F};
- {value,{Key,_}} ->
+ {Key,_} ->
badarg;
false ->
{ok,default_option(Key)}
@@ -333,12 +337,13 @@ expand_opt(O, Os) ->
[O|Os].
attr_info(Attrs, Item) ->
- case catch lists:keysearch(Item, 1, Attrs) of
- {value,{Item,Value}} ->
- {Item,Value};
- false ->
- undefined;
- _ ->
+ try lists:keyfind(Item, 1, Attrs) of
+ {_Item, _Value} = T ->
+ T;
+ false ->
+ undefined
+ catch
+ _:_ ->
erlang:error(badarg, [Attrs, Item])
end.
@@ -442,6 +447,14 @@ 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]);
+scan1("..."++Cs, St, Line, Col, Toks) ->
+ tok2(Cs, St, Line, Col, Toks, "...", '...', 3);
+scan1(".."=Cs, _St, Line, Col, Toks) ->
+ {more,{Cs,Col,Toks,Line,[],fun scan/6}};
+scan1(".."++Cs, St, Line, Col, Toks) ->
+ tok2(Cs, St, Line, Col, Toks, "..", '..', 2);
+scan1("."=Cs, _St, Line, Col, Toks) ->
+ {more,{Cs,Col,Toks,Line,[],fun scan/6}};
scan1([$.=C|Cs], St, Line, Col, Toks) ->
scan_dot(Cs, St, Line, Col, Toks, [C]);
scan1([$"|Cs], St, Line, Col, Toks) -> %" Emacs
@@ -591,12 +604,12 @@ scan_atom(Cs0, St, Line, Col, Toks, Ncs0) ->
case catch list_to_atom(Wcs) of
Name when is_atom(Name) ->
case (St#erl_scan.resword_fun)(Name) of
- true ->
+ true ->
tok2(Cs, St, Line, Col, Toks, Wcs, Name);
- false ->
+ false ->
tok3(Cs, St, Line, Col, Toks, atom, Wcs, Name)
end;
- _Error ->
+ _Error ->
Ncol = incr_column(Col, length(Wcs)),
scan_error({illegal,atom}, Line, Col, Line, Ncol, Cs)
end
@@ -610,7 +623,7 @@ scan_variable(Cs0, St, Line, Col, Toks, Ncs0) ->
case catch list_to_atom(Wcs) of
Name when is_atom(Name) ->
tok3(Cs, St, Line, Col, Toks, var, Wcs, Name);
- _Error ->
+ _Error ->
Ncol = incr_column(Col, length(Wcs)),
scan_error({illegal,var}, Line, Col, Line, Ncol, Cs)
end
@@ -644,8 +657,6 @@ scan_dot([$\n=C|Cs], St, Line, Col, Toks, Ncs) ->
scan_dot([C|Cs], St, Line, Col, Toks, Ncs) when ?WHITE_SPACE(C) ->
Attrs = attributes(Line, Col, St, Ncs++[C]),
{ok,[{dot,Attrs}|Toks],Cs,Line,incr_column(Col, 2)};
-scan_dot([]=Cs, _St, Line, Col, Toks, Ncs) ->
- {more,{Cs,Col,Toks,Line,Ncs,fun scan_dot/6}};
scan_dot(eof=Cs, St, Line, Col, Toks, Ncs) ->
Attrs = attributes(Line, Col, St, Ncs),
{ok,[{dot,Attrs}|Toks],Cs,Line,incr_column(Col, 1)};
@@ -690,7 +701,7 @@ scan_nl_spcs([]=Cs, _St, Line, Col, Toks, N) ->
{more,{Cs,Col,Toks,Line,N,fun scan_nl_spcs/6}};
scan_nl_spcs(Cs, St, Line, Col, Toks, N) ->
newline_end(Cs, St, Line, Col, Toks, N, nl_spcs(N)).
-
+
scan_nl_tabs([$\t|Cs], St, Line, Col, Toks, N) when N < 11 ->
scan_nl_tabs(Cs, St, Line, Col, Toks, N+1);
scan_nl_tabs([]=Cs, _St, Line, Col, Toks, N) ->
@@ -701,7 +712,7 @@ scan_nl_tabs(Cs, St, Line, Col, Toks, N) ->
%% Note: returning {more,Cont} is meaningless here; one could just as
%% well return several tokens. But since tokens() scans up to a full
%% stop anyway, nothing is gained by not collecting all white spaces.
-scan_nl_white_space([$\n|Cs], #erl_scan{text = false}=St, Line, no_col=Col,
+scan_nl_white_space([$\n|Cs], #erl_scan{text = false}=St, Line, no_col=Col,
Toks0, Ncs) ->
Toks = [{white_space,Line,lists:reverse(Ncs)}|Toks0],
scan_newline(Cs, St, Line+1, Col, Toks);
@@ -714,7 +725,7 @@ scan_nl_white_space([C|Cs], St, Line, Col, Toks, Ncs) when ?WHITE_SPACE(C) ->
scan_nl_white_space(Cs, St, Line, Col, Toks, [C|Ncs]);
scan_nl_white_space([]=Cs, _St, Line, Col, Toks, Ncs) ->
{more,{Cs,Col,Toks,Line,Ncs,fun scan_nl_white_space/6}};
-scan_nl_white_space(Cs, #erl_scan{text = false}=St, Line, no_col=Col,
+scan_nl_white_space(Cs, #erl_scan{text = false}=St, Line, no_col=Col,
Toks, Ncs) ->
scan1(Cs, St, Line+1, Col, [{white_space,Line,lists:reverse(Ncs)}|Toks]);
scan_nl_white_space(Cs, St, Line, Col, Toks, Ncs0) ->
@@ -723,7 +734,7 @@ scan_nl_white_space(Cs, St, Line, Col, Toks, Ncs0) ->
Token = {white_space,Attrs,Ncs},
scan1(Cs, St, Line+1, new_column(Col, length(Ncs)), [Token|Toks]).
-newline_end(Cs, #erl_scan{text = false}=St, Line, no_col=Col,
+newline_end(Cs, #erl_scan{text = false}=St, Line, no_col=Col,
Toks, _N, Ncs) ->
scan1(Cs, St, Line+1, Col, [{white_space,Line,Ncs}|Toks]);
newline_end(Cs, St, Line, Col, Toks, N, Ncs) ->
@@ -789,7 +800,7 @@ scan_char([$\\|Cs]=Cs0, St, Line, Col, Toks) ->
Ntoks = [{char,Attrs,Val}|Toks],
scan1(Ncs, St, Line, Ncol, Ntoks)
end;
-scan_char([$\n=C|Cs], St, Line, Col, Toks) ->
+scan_char([$\n=C|Cs], St, Line, Col, Toks) ->
Attrs = attributes(Line, Col, St, [$$,C]),
scan1(Cs, St, Line+1, new_column(Col, 1), [{char,Attrs,C}|Toks]);
scan_char([C|Cs], St, Line, Col, Toks) when ?CHAR(C) ->
@@ -896,7 +907,7 @@ scan_string_no_col([Q|Cs], Line, Col, Q, Wcs, Uni) ->
{Cs,Line,Col,_DontCare=[],lists:reverse(Wcs),Uni};
scan_string_no_col([$\n=C|Cs], Line, Col, Q, Wcs, Uni) ->
scan_string_no_col(Cs, Line+1, Col, Q, [C|Wcs], Uni);
-scan_string_no_col([C|Cs], Line, Col, Q, Wcs, Uni) when C =/= $\\,
+scan_string_no_col([C|Cs], Line, Col, Q, Wcs, Uni) when C =/= $\\,
?CHAR(C), ?UNI255(C) ->
scan_string_no_col(Cs, Line, Col, Q, [C|Wcs], Uni);
scan_string_no_col(Cs, Line, Col, Q, Wcs, Uni) ->
@@ -909,7 +920,7 @@ scan_string_col([Q|Cs], Line, Col, Q, Wcs0, Uni) ->
{Cs,Line,Col+1,Str,Wcs,Uni};
scan_string_col([$\n=C|Cs], Line, _xCol, Q, Wcs, Uni) ->
scan_string_col(Cs, Line+1, 1, Q, [C|Wcs], Uni);
-scan_string_col([C|Cs], Line, Col, Q, Wcs, Uni) when C =/= $\\,
+scan_string_col([C|Cs], Line, Col, Q, Wcs, Uni) when C =/= $\\,
?CHAR(C), ?UNI255(C) ->
scan_string_col(Cs, Line, Col+1, Q, [C|Wcs], Uni);
scan_string_col(Cs, Line, Col, Q, Wcs, Uni) ->
@@ -970,8 +981,8 @@ scan_string1(eof, Line, Col, _Q, _Str, Wcs, _Uni) ->
{error,Line,Col,lists:reverse(Wcs),eof}.
-define(OCT(C), C >= $0, C =< $7).
--define(HEX(C), C >= $0 andalso C =< $9 orelse
- C >= $A andalso C =< $F orelse
+-define(HEX(C), C >= $0 andalso C =< $9 orelse
+ C >= $A andalso C =< $F orelse
C >= $a andalso C =< $f).
%% \<1-3> octal digits
@@ -1086,7 +1097,7 @@ scan_number(Cs, St, Line, Col, Toks, Ncs0) ->
Ncol = incr_column(Col, length(Ncs)),
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});
@@ -1262,7 +1273,7 @@ nl_tabs(8) -> "\n\t\t\t\t\t\t\t";
nl_tabs(9) -> "\n\t\t\t\t\t\t\t\t";
nl_tabs(10) -> "\n\t\t\t\t\t\t\t\t\t";
nl_tabs(11) -> "\n\t\t\t\t\t\t\t\t\t\t".
-
+
tabs(1) -> "\t";
tabs(2) -> "\t\t";
tabs(3) -> "\t\t\t";
@@ -1303,5 +1314,4 @@ reserved_word('bsl') -> true;
reserved_word('bsr') -> true;
reserved_word('or') -> true;
reserved_word('xor') -> true;
-reserved_word('spec') -> true;
reserved_word(_) -> false.
diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl
index 5958a58d7c..99e454f593 100644
--- a/lib/stdlib/src/escript.erl
+++ b/lib/stdlib/src/escript.erl
@@ -19,102 +19,254 @@
-module(escript).
%% Useful functions that can be called from scripts.
--export([script_name/0, foldl/3]).
+-export([script_name/0, create/2, extract/2]).
%% Internal API.
-export([start/0, start/1]).
--record(state, {file,
- module,
+%%-----------------------------------------------------------------------
+
+-define(SHEBANG, "/usr/bin/env escript").
+-define(COMMENT, "This is an -*- erlang -*- file").
+
+%%-----------------------------------------------------------------------
+
+-type mode() :: 'compile' | 'debug' | 'interpret' | 'run'.
+-type source() :: 'archive' | 'beam' | 'text'.
+
+-record(state, {file :: file:filename(),
+ module :: module(),
forms_or_bin,
- source,
- n_errors,
- mode,
- exports_main,
- has_records}).
-
+ source :: source(),
+ n_errors :: non_neg_integer(),
+ mode :: mode(),
+ exports_main :: boolean(),
+ has_records :: boolean()}).
+
+-type shebang() :: string().
+-type comment() :: string().
+-type emu_args() :: string().
+
+-record(sections, {type,
+ shebang :: shebang(),
+ comment :: comment(),
+ emu_args :: emu_args(),
+ body}).
+
+-record(extract_options, {compile_source}).
+
+-type zip_file() ::
+ file:filename()
+ | {file:filename(), binary()}
+ | {file:filename(), binary(), file:file_info()}.
+-type zip_create_option() :: term().
+-type section() ::
+ shebang
+ | {shebang, shebang()}
+ | comment
+ | {comment, comment()}
+ | {emu_args, emu_args()}
+ | {source, file:filename() | binary()}
+ | {beam, file:filename() | binary()}
+ | {archive, file:filename() | binary()}
+ | {archive, [zip_file()], [zip_create_option()]}.
+
+%%-----------------------------------------------------------------------
+
+%% Create a complete escript file with both header and body
+-spec create(file:filename() | binary, [section()]) ->
+ ok | {ok, binary()} | {error, term()}.
+
+create(File, Options) when is_list(Options) ->
+ try
+ S = prepare(Options, #sections{}),
+ BinList =
+ [Section || Section <- [S#sections.shebang,
+ S#sections.comment,
+ S#sections.emu_args,
+ S#sections.body],
+ Section =/= undefined],
+ case File of
+ binary ->
+ {ok, list_to_binary(BinList)};
+ _ ->
+ case file:write_file(File, BinList) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ {error, {Reason, File}}
+ end
+ end
+ catch
+ throw:PrepareReason ->
+ {error, PrepareReason}
+ end.
+
+prepare([H | T], S) ->
+ case H of
+ {shebang, undefined} ->
+ prepare(T, S);
+ shebang ->
+ prepare(T, S#sections{shebang = "#!" ++ ?SHEBANG ++ "\n"});
+ {shebang, default} ->
+ prepare(T, S#sections{shebang = "#!" ++ ?SHEBANG ++ "\n"});
+ {shebang, Shebang} when is_list(Shebang) ->
+ prepare(T, S#sections{shebang = "#!" ++ Shebang ++ "\n"});
+ {comment, undefined} ->
+ prepare(T, S);
+ comment ->
+ prepare(T, S#sections{comment = "%% " ++ ?COMMENT ++ "\n"});
+ {comment, default} ->
+ prepare(T, S#sections{comment = "%% " ++ ?COMMENT ++ "\n"});
+ {comment, Comment} when is_list(Comment) ->
+ prepare(T, S#sections{comment = "%% " ++ Comment ++ "\n"});
+ {emu_args, undefined} ->
+ prepare(T, S);
+ {emu_args, Args} when is_list(Args) ->
+ prepare(T, S#sections{emu_args = "%%!" ++ Args ++ "\n"});
+ {Type, File} when is_list(File) ->
+ case file:read_file(File) of
+ {ok, Bin} ->
+ prepare(T, S#sections{type = Type, body = Bin});
+ {error, Reason} ->
+ throw({Reason, H})
+ end;
+ {Type, Bin} when is_binary(Bin) ->
+ prepare(T, S#sections{type = Type, body = Bin});
+ {archive = Type, ZipFiles, ZipOptions}
+ when is_list(ZipFiles), is_list(ZipOptions) ->
+ File = "dummy.zip",
+ case zip:create(File, ZipFiles, ZipOptions ++ [memory]) of
+ {ok, {File, ZipBin}} ->
+ prepare(T, S#sections{type = Type, body = ZipBin});
+ {error, Reason} ->
+ throw({Reason, H})
+ end;
+ _ ->
+ throw({badarg, H})
+ end;
+prepare([], #sections{body = undefined}) ->
+ throw(missing_body);
+prepare([], #sections{type = Type} = S)
+ when Type =:= source; Type =:= beam; Type =:= archive ->
+ S;
+prepare([], #sections{type = Type}) ->
+ throw({illegal_type, Type});
+prepare(BadOptions, _) ->
+ throw({badarg, BadOptions}).
+
+-type section_name() :: shebang | comment | emu_args | body .
+-type extract_option() :: compile_source | {section, [section_name()]}.
+-spec extract(file:filename(), [extract_option()]) ->
+ {ok, [section()]} | {error, term()}.
+
+extract(File, Options) when is_list(File), is_list(Options) ->
+ try
+ EO = parse_extract_options(Options,
+ #extract_options{compile_source = false}),
+ {HeaderSz, NextLineNo, Fd, Sections} =
+ parse_header(File, not EO#extract_options.compile_source),
+ Type = Sections#sections.type,
+ case {Type, EO#extract_options.compile_source} of
+ {source, true} ->
+ Bin = compile_source(Type, File, Fd, NextLineNo, HeaderSz);
+ {_, _} ->
+ ok = file:close(Fd),
+ case file:read_file(File) of
+ {ok, <<_Header:HeaderSz/binary, Bin/binary>>} ->
+ ok;
+ {error, ReadReason} ->
+ Bin = get_rid_of_compiler_warning,
+ throw(ReadReason)
+ end
+ end,
+ return_sections(Sections, Bin)
+ catch
+ throw:Reason ->
+ {error, Reason}
+ end.
+
+parse_extract_options([H | T], EO) ->
+ case H of
+ compile_source ->
+ EO2 = EO#extract_options{compile_source = true},
+ parse_extract_options(T, EO2);
+ _ ->
+ throw({badarg, H})
+ end;
+parse_extract_options([], EO) ->
+ EO.
+
+compile_source(Type, File, Fd, NextLineNo, HeaderSz) ->
+ {text, _Module, Forms, _HasRecs, _Mode} =
+ do_parse_file(Type, File, Fd, NextLineNo, HeaderSz, false),
+ ok = file:close(Fd),
+ case compile:forms(Forms, [return_errors, debug_info]) of
+ {ok, _, BeamBin} ->
+ BeamBin;
+ {error, Errors, Warnings} ->
+ throw({compile, [{errors, format_errors(Errors)},
+ {warnings, format_errors(Warnings)}]})
+ end.
+
+format_errors(CompileErrors) ->
+ [lists:flatten([File, ":", integer_to_list(LineNo), ": ",
+ Mod:format_error(Error)]) ||
+ {File, FileErrors} <- CompileErrors,
+ {LineNo, Mod, Error} <- FileErrors].
+
+return_sections(S, Bin) ->
+ {ok, [normalize_section(shebang, S#sections.shebang),
+ normalize_section(comment, S#sections.comment),
+ normalize_section(emu_args, S#sections.emu_args),
+ normalize_section(S#sections.type, Bin)]}.
+
+normalize_section(Name, undefined) ->
+ {Name, undefined};
+normalize_section(shebang, "#!" ++ Chars) ->
+ Chopped = string:strip(Chars, right, $\n),
+ Stripped = string:strip(Chopped, both),
+ if
+ Stripped =:= ?SHEBANG ->
+ {shebang, default};
+ true ->
+ {shebang, Stripped}
+ end;
+normalize_section(comment, Chars) ->
+ Chopped = string:strip(Chars, right, $\n),
+ Stripped = string:strip(string:strip(Chopped, left, $%), both),
+ if
+ Stripped =:= ?COMMENT ->
+ {comment, default};
+ true ->
+ {comment, Stripped}
+ end;
+normalize_section(emu_args, "%%!" ++ Chars) ->
+ Chopped = string:strip(Chars, right, $\n),
+ Stripped = string:strip(Chopped, both),
+ {emu_args, Stripped};
+normalize_section(Name, Chars) ->
+ {Name, Chars}.
+
+-spec script_name() -> string().
+
script_name() ->
[ScriptName|_] = init:get_plain_arguments(),
ScriptName.
-%% Apply Fun(Name, GetInfo, GetBin, Acc) for each file in the escript.
-%%
-%% Fun/2 must return a new accumulator which is passed to the next call.
-%% The function returns the final value of the accumulator. Acc0 is
-%% returned if the escript contain an empty archive.
-%%
-%% GetInfo/0 is a fun that returns a #file_info{} record for the file.
-%% GetBin/0 is a fun that returns a the contents of the file as a binary.
-%%
-%% An escript may contain erlang code, beam code or an archive:
-%%
-%% archive - the Fun/2 will be applied for each file in the archive
-%% beam - the Fun/2 will be applied once and GetInfo/0 returns the file
-%% info for the (entire) escript file
-%% erl - the Fun/2 will be applied once, GetInfo/0 returns the file
-%% info for the (entire) escript file and the GetBin returns
-%% the compiled beam code
-
-%%-spec foldl(fun((string(),
-%% fun(() -> #file_info()),
-%% fun(() -> binary() -> term()),
-%% term()) -> term()),
-%% term(),
-%% string()).
-foldl(Fun, Acc0, File) when is_function(Fun, 4) ->
- case parse_file(File, false) of
- {text, _, Forms, _HasRecs, _Mode} when is_list(Forms) ->
- GetInfo = fun() -> file:read_file_info(File) end,
- GetBin =
- fun() ->
- case compile:forms(Forms, [return_errors, debug_info]) of
- {ok, _, BeamBin} ->
- BeamBin;
- {error, _Errors, _Warnings} ->
- fatal("There were compilation errors.")
- end
- end,
- try
- {ok, Fun(".", GetInfo, GetBin, Acc0)}
- catch
- throw:Reason ->
- {error, Reason}
- end;
- {beam, _, BeamBin, _HasRecs, _Mode} when is_binary(BeamBin) ->
- GetInfo = fun() -> file:read_file_info(File) end,
- GetBin = fun() -> BeamBin end,
- try
- {ok, Fun(".", GetInfo, GetBin, Acc0)}
- catch
- throw:Reason ->
- {error, Reason}
- end;
- {archive, _, ArchiveBin, _HasRecs, _Mode} when is_binary(ArchiveBin) ->
- ZipFun =
- fun({Name, GetInfo, GetBin}, A) ->
- A2 = Fun(Name, GetInfo, GetBin, A),
- {true, false, A2}
- end,
- case prim_zip:open(ZipFun, Acc0, {File, ArchiveBin}) of
- {ok, PrimZip, Res} ->
- ok = prim_zip:close(PrimZip),
- {ok, Res};
- {error, bad_eocd} ->
- {error, "Not an archive file"};
- {error, Reason} ->
- {error, Reason}
- end
- end.
-
%%
%% Internal API.
%%
+-spec start() -> no_return().
+
start() ->
start([]).
+-spec start([string()]) -> no_return().
+
start(EscriptOptions) ->
- try
+ try
%% Commands run using -run or -s are run in a process
%% trap_exit set to false. Because this behaviour is
%% surprising for users of escript, make sure to reset
@@ -143,11 +295,11 @@ parse_and_run(File, Args, Options) ->
parse_file(File, CheckOnly),
Mode2 =
case lists:member("d", Options) of
- true ->
+ true ->
debug;
false ->
case lists:member("c", Options) of
- true ->
+ true ->
compile;
false ->
case lists:member("i", Options) of
@@ -177,7 +329,7 @@ parse_and_run(File, Args, Options) ->
_Other ->
fatal("There were compilation errors.")
end
- end;
+ end;
is_binary(FormsOrBin) ->
case Source of
archive ->
@@ -190,11 +342,13 @@ parse_and_run(File, Args, Options) ->
true ->
my_halt(0);
false ->
- Text = lists:concat(["Function ", Module, ":main/1 is not exported"]),
+ Text = lists:concat(["Function ", Module,
+ ":main/1 is not exported"]),
fatal(Text)
end;
_ ->
- Text = lists:concat(["Cannot load module ", Module, " from archive"]),
+ Text = lists:concat(["Cannot load module ", Module,
+ " from archive"]),
fatal(Text)
end;
ok ->
@@ -212,7 +366,7 @@ parse_and_run(File, Args, Options) ->
run ->
{module, Module} = code:load_binary(Module, File, FormsOrBin),
run(Module, Args);
- debug ->
+ debug ->
[Base | Rest] = lists:reverse(filename:split(File)),
Base2 = filename:basename(Base, code:objfile_extension()),
Rest2 =
@@ -222,8 +376,8 @@ parse_and_run(File, Args, Options) ->
end,
SrcFile = filename:join(lists:reverse([Base2 ++ ".erl" | Rest2])),
debug(Module, {Module, SrcFile, File, FormsOrBin}, Args)
- end
- end
+ end
+ end
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -231,25 +385,19 @@ parse_and_run(File, Args, Options) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
parse_file(File, CheckOnly) ->
- S = #state{file = File,
- n_errors = 0,
- mode = interpret,
- exports_main = false,
- has_records = false},
- {ok, Fd} =
- case file:open(File, [read]) of
- {ok, Fd0} ->
- {ok, Fd0};
- {error, R} ->
- fatal(lists:concat([file:format_error(R), ": '", File, "'"]))
- end,
- {HeaderSz, StartLine, ScriptType} = skip_header(Fd, 1),
+ {HeaderSz, NextLineNo, Fd, Sections} =
+ parse_header(File, false),
+ do_parse_file(Sections#sections.type,
+ File, Fd, NextLineNo, HeaderSz, CheckOnly).
+
+do_parse_file(Type, File, Fd, NextLineNo, HeaderSz, CheckOnly) ->
+ S = initial_state(File),
#state{mode = Mode,
source = Source,
module = Module,
forms_or_bin = FormsOrBin,
has_records = HasRecs} =
- case ScriptType of
+ case Type of
archive ->
%% Archive file
ok = file:close(Fd),
@@ -260,63 +408,101 @@ parse_file(File, CheckOnly) ->
parse_beam(S, File, HeaderSz, CheckOnly);
source ->
%% Source code
- parse_source(S, File, Fd, StartLine, HeaderSz, CheckOnly)
+ parse_source(S, File, Fd, NextLineNo, HeaderSz, CheckOnly)
end,
{Source, Module, FormsOrBin, HasRecs, Mode}.
+initial_state(File) ->
+ #state{file = File,
+ n_errors = 0,
+ mode = interpret,
+ exports_main = false,
+ has_records = false}.
+
%% Skip header and make a heuristic guess about the script type
-skip_header(P, LineNo) ->
+parse_header(File, KeepFirst) ->
+ LineNo = 1,
+ {ok, Fd} =
+ case file:open(File, [read]) of
+ {ok, Fd0} ->
+ {ok, Fd0};
+ {error, R} ->
+ fatal(lists:concat([file:format_error(R), ": '", File, "'"]))
+ end,
+
%% Skip shebang on first line
- {ok, HeaderSz0} = file:position(P, cur),
- Line1 = get_line(P),
+ {ok, HeaderSz0} = file:position(Fd, cur),
+ Line1 = get_line(Fd),
case classify_line(Line1) of
shebang ->
- find_first_body_line(P, LineNo);
+ find_first_body_line(Fd, HeaderSz0, LineNo, KeepFirst,
+ #sections{shebang = Line1});
archive ->
- {HeaderSz0, LineNo, archive};
+ {HeaderSz0, LineNo, Fd,
+ #sections{type = archive}};
beam ->
- {HeaderSz0, LineNo, beam};
+ {HeaderSz0, LineNo, Fd,
+ #sections{type = beam}};
_ ->
- find_first_body_line(P, LineNo)
+ find_first_body_line(Fd, HeaderSz0, LineNo, KeepFirst,
+ #sections{})
end.
-find_first_body_line(P, LineNo) ->
- {ok, HeaderSz1} = file:position(P, cur),
+find_first_body_line(Fd, HeaderSz0, LineNo, KeepFirst, Sections) ->
+ {ok, HeaderSz1} = file:position(Fd, cur),
%% Look for special comment on second line
- Line2 = get_line(P),
- {ok, HeaderSz2} = file:position(P, cur),
+ Line2 = get_line(Fd),
+ {ok, HeaderSz2} = file:position(Fd, cur),
case classify_line(Line2) of
emu_args ->
%% Skip special comment on second line
- Line3 = get_line(P),
- {HeaderSz2, LineNo + 2, guess_type(Line3)};
- _ ->
+ Line3 = get_line(Fd),
+ {HeaderSz2, LineNo + 2, Fd,
+ Sections#sections{type = guess_type(Line3),
+ comment = undefined,
+ emu_args = Line2}};
+ Line2Type ->
%% Look for special comment on third line
- Line3 = get_line(P),
- {ok, HeaderSz3} = file:position(P, cur),
- case classify_line(Line3) of
- emu_args ->
+ Line3 = get_line(Fd),
+ {ok, HeaderSz3} = file:position(Fd, cur),
+ Line3Type = classify_line(Line3),
+ if
+ Line3Type =:= emu_args ->
%% Skip special comment on third line
- Line4 = get_line(P),
- {HeaderSz3, LineNo + 3, guess_type(Line4)};
- _ ->
+ Line4 = get_line(Fd),
+ {HeaderSz3, LineNo + 3, Fd,
+ Sections#sections{type = guess_type(Line4),
+ comment = Line2,
+ emu_args = Line3}};
+ Sections#sections.shebang =:= undefined,
+ KeepFirst =:= true ->
+ %% No shebang. Use the entire file
+ {HeaderSz0, LineNo, Fd,
+ Sections#sections{type = guess_type(Line2)}};
+ Sections#sections.shebang =:= undefined ->
+ %% No shebang. Skip the first line
+ {HeaderSz1, LineNo, Fd,
+ Sections#sections{type = guess_type(Line2)}};
+ Line2Type =:= comment ->
+ %% Skip shebang on first line and comment on second
+ {HeaderSz2, LineNo + 2, Fd,
+ Sections#sections{type = guess_type(Line3),
+ comment = Line2}};
+ true ->
%% Just skip shebang on first line
- {HeaderSz1, LineNo + 1, guess_type(Line2)}
+ {HeaderSz1, LineNo + 1, Fd,
+ Sections#sections{type = guess_type(Line2)}}
end
end.
-
+
classify_line(Line) ->
case Line of
- [$\#, $\! | _] ->
- shebang;
- [$P, $K | _] ->
- archive;
- [$F, $O, $R, $1 | _] ->
- beam;
- [$\%, $\%, $\! | _] ->
- emu_args;
- _ ->
- undefined
+ "#!" ++ _ -> shebang;
+ "PK" ++ _ -> archive;
+ "FOR1" ++ _ -> beam;
+ "%%!" ++ _ -> emu_args;
+ "%" ++ _ -> comment;
+ _ -> undefined
end.
guess_type(Line) ->
@@ -336,8 +522,8 @@ get_line(P) ->
parse_archive(S, File, HeaderSz) ->
case file:read_file(File) of
- {ok, <<_FirstLine:HeaderSz/binary, Bin/binary>>} ->
- Mod =
+ {ok, <<_Header:HeaderSz/binary, Bin/binary>>} ->
+ Mod =
case init:get_argument(escript) of
{ok, [["main", M]]} ->
%% Use explicit module name
@@ -345,14 +531,13 @@ parse_archive(S, File, HeaderSz) ->
_ ->
%% Use escript name without extension as module name
RevBase = lists:reverse(filename:basename(File)),
- RevBase2 =
+ RevBase2 =
case lists:dropwhile(fun(X) -> X =/= $. end, RevBase) of
[$. | Rest] -> Rest;
[] -> RevBase
end,
list_to_atom(lists:reverse(RevBase2))
end,
-
S#state{source = archive,
mode = run,
module = Mod,
@@ -365,7 +550,7 @@ parse_archive(S, File, HeaderSz) ->
parse_beam(S, File, HeaderSz, CheckOnly) ->
- {ok, <<_FirstLine:HeaderSz/binary, Bin/binary>>} =
+ {ok, <<_Header:HeaderSz/binary, Bin/binary>>} =
file:read_file(File),
case beam_lib:chunks(Bin, [exports]) of
{ok, {Module, [{exports, Exports}]}} ->
@@ -399,7 +584,7 @@ parse_source(S, File, Fd, StartLine, HeaderSz, CheckOnly) ->
{ok, FileForm} = epp:parse_erl_form(Epp),
OptModRes = epp:parse_erl_form(Epp),
S2 = S#state{source = text, module = Module},
- S3 =
+ S3 =
case OptModRes of
{ok, {attribute,_, module, M} = Form} ->
epp_parse_file(Epp, S2#state{module = M}, [Form, FileForm]);
@@ -408,8 +593,8 @@ parse_source(S, File, Fd, StartLine, HeaderSz, CheckOnly) ->
epp_parse_file2(Epp, S2, [ModForm, FileForm], OptModRes);
{error, _} ->
epp_parse_file2(Epp, S2, [FileForm], OptModRes);
- {eof,LastLine} ->
- S#state{forms_or_bin = [FileForm, {eof,LastLine}]}
+ {eof, _LastLine} = Eof ->
+ S#state{forms_or_bin = [FileForm, Eof]}
end,
ok = epp:close(Epp),
ok = file:close(Fd),
@@ -448,12 +633,12 @@ check_source(S, CheckOnly) ->
pre_def_macros(File) ->
{MegaSecs, Secs, MicroSecs} = erlang:now(),
- Replace = fun(Char) ->
+ Replace = fun(Char) ->
case Char of
$\. -> $\_;
_ -> Char
end
- end,
+ end,
CleanBase = lists:map(Replace, filename:basename(File)),
ModuleStr =
CleanBase ++ "__" ++
@@ -504,8 +689,8 @@ epp_parse_file2(Epp, S, Forms, Parsed) ->
io:format("~s:~w: ~s\n",
[S#state.file,Ln,Mod:format_error(Args)]),
epp_parse_file(Epp, S#state{n_errors = S#state.n_errors + 1}, [Form | Forms]);
- {eof,LastLine} ->
- S#state{forms_or_bin = lists:reverse([{eof, LastLine} | Forms])}
+ {eof, _LastLine} = Eof ->
+ S#state{forms_or_bin = lists:reverse([Eof | Forms])}
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -642,8 +827,8 @@ eval_exprs([E|Es], Bs0, Lf, Ef, RBs) ->
eval_exprs(Es, Bs, Lf, Ef, RBs).
format_exception(Class, Reason) ->
- PF = fun(Term, I) ->
- io_lib:format("~." ++ integer_to_list(I) ++ "P", [Term, 50])
+ PF = fun(Term, I) ->
+ io_lib:format("~." ++ integer_to_list(I) ++ "P", [Term, 50])
end,
StackTrace = erlang:get_stacktrace(),
StackFun = fun(M, _F, _A) -> (M =:= erl_eval) or (M =:= ?MODULE) end,
@@ -651,7 +836,7 @@ format_exception(Class, Reason) ->
fatal(Str) ->
throw(Str).
-
+
my_halt(Reason) ->
case process_info(group_leader(), status) of
{_,waiting} ->
@@ -675,7 +860,7 @@ hidden_apply(App, M, F, Args) ->
Arity = length(Args),
Text = io_lib:format("Call to ~w:~w/~w in application ~w failed.\n",
[M, F, Arity, App]),
- fatal(Text);
+ fatal(Text);
Stk ->
erlang:raise(error, undef, Stk)
end
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index 9f84e3639f..6e6e949e2c 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(ets).
@@ -42,10 +42,15 @@
-export([i/0, i/1, i/2, i/3]).
-%%------------------------------------------------------------------------------
+-export_type([tab/0, tid/0]).
+
+%%-----------------------------------------------------------------------------
-type tab() :: atom() | tid().
+%% a similar definition is also in erl_types
+-opaque tid() :: integer().
+
-type ext_info() :: 'md5sum' | 'object_count'.
-type protection() :: 'private' | 'protected' | 'public'.
-type type() :: 'bag' | 'duplicate_bag' | 'ordered_set' | 'set'.
@@ -63,7 +68,7 @@
-type match_pattern() :: atom() | tuple().
-type match_specs() :: [{match_pattern(), [_], [_]}].
-%%------------------------------------------------------------------------------
+%%-----------------------------------------------------------------------------
%% The following functions used to be found in this module, but
%% are now BIFs (i.e. implemented in C).
@@ -230,7 +235,7 @@ from_dets(EtsTable, DetsTable) ->
erlang:error(Unexpected,[EtsTable,DetsTable])
end.
--spec to_dets(tab(), dets:tab_name()) -> tab().
+-spec to_dets(tab(), dets:tab_name()) -> dets:tab_name().
to_dets(EtsTable, DetsTable) ->
case (catch dets:from_ets(DetsTable, EtsTable)) of
@@ -507,7 +512,7 @@ file2tab(File) ->
file2tab(File, Opts) ->
try
- {ok,Verify} = parse_f2t_opts(Opts,false),
+ {ok,Verify,TabArg} = parse_f2t_opts(Opts,false,[]),
Name = make_ref(),
{ok, Major, Minor, FtOptions, MD5State, FullHeader, DLContext} =
case disk_log:open([{name, Name},
@@ -535,7 +540,7 @@ file2tab(File, Opts) ->
true ->
ok
end,
- {ok, Tab, HeadCount} = create_tab(FullHeader),
+ {ok, Tab, HeadCount} = create_tab(FullHeader, TabArg),
StrippedOptions =
case Verify of
true ->
@@ -622,14 +627,14 @@ do_read_and_verify(ReadFun,InitState,Tab,FtOptions,HeadCount,Verify) ->
end,
{ok,Tab};
{ok,{FinalMD5State,FinalCount,['$end_of_table',LastInfo],_}} ->
- ECount = case lists:keysearch(count,1,LastInfo) of
- {value,{count,N}} ->
+ ECount = case lists:keyfind(count,1,LastInfo) of
+ {count,N} ->
N;
_ ->
false
end,
- EMD5 = case lists:keysearch(md5,1,LastInfo) of
- {value,{md5,M}} ->
+ EMD5 = case lists:keyfind(md5,1,LastInfo) of
+ {md5,M} ->
M;
_ ->
false
@@ -671,15 +676,17 @@ do_read_and_verify(ReadFun,InitState,Tab,FtOptions,HeadCount,Verify) ->
{ok,Tab}
end.
-parse_f2t_opts([],Verify) ->
- {ok,Verify};
-parse_f2t_opts([{verify, true}|T],_OV) ->
- parse_f2t_opts(T,true);
-parse_f2t_opts([{verify,false}|T],OV) ->
- parse_f2t_opts(T,OV);
-parse_f2t_opts([Unexpected|_],_) ->
+parse_f2t_opts([],Verify,Tab) ->
+ {ok,Verify,Tab};
+parse_f2t_opts([{verify, true}|T],_OV,Tab) ->
+ parse_f2t_opts(T,true,Tab);
+parse_f2t_opts([{verify,false}|T],OV,Tab) ->
+ parse_f2t_opts(T,OV,Tab);
+parse_f2t_opts([{table,Tab}|T],OV,[]) ->
+ parse_f2t_opts(T,OV,Tab);
+parse_f2t_opts([Unexpected|_],_,_) ->
throw({unknown_option,Unexpected});
-parse_f2t_opts(Malformed,_) ->
+parse_f2t_opts(Malformed,_,_) ->
throw({malformed_option,Malformed}).
count_mandatory([]) ->
@@ -742,22 +749,21 @@ get_header_data(Name,true) ->
false ->
throw(badfile);
true ->
- Major = case lists:keysearch(major,1,L) of
- {value,{major,Maj}} ->
+ Major = case lists:keyfind(major,1,L) of
+ {major,Maj} ->
Maj;
_ ->
0
end,
- Minor = case lists:keysearch(minor,1,L) of
- {value,{minor,Min}} ->
+ Minor = case lists:keyfind(minor,1,L) of
+ {minor,Min} ->
Min;
_ ->
0
end,
FtOptions =
- case lists:keysearch(extended_info,1,L) of
- {value,{extended_info,I}}
- when is_list(I) ->
+ case lists:keyfind(extended_info,1,L) of
+ {extended_info,I} when is_list(I) ->
#filetab_options
{
object_count =
@@ -786,29 +792,28 @@ get_header_data(Name,true) ->
end;
get_header_data(Name, false) ->
- case wrap_chunk(Name,start,1,false) of
+ case wrap_chunk(Name, start, 1, false) of
{C,[Tup]} when is_tuple(Tup) ->
L = tuple_to_list(Tup),
case verify_header_mandatory(L) of
false ->
throw(badfile);
true ->
- Major = case lists:keysearch(major_version,1,L) of
- {value,{major_version,Maj}} ->
+ Major = case lists:keyfind(major_version, 1, L) of
+ {major_version, Maj} ->
Maj;
_ ->
0
end,
- Minor = case lists:keysearch(minor_version,1,L) of
- {value,{minor_version,Min}} ->
+ Minor = case lists:keyfind(minor_version, 1, L) of
+ {minor_version, Min} ->
Min;
_ ->
0
end,
FtOptions =
- case lists:keysearch(extended_info,1,L) of
- {value,{extended_info,I}}
- when is_list(I) ->
+ case lists:keyfind(extended_info, 1, L) of
+ {extended_info, I} when is_list(I) ->
#filetab_options
{
object_count =
@@ -825,25 +830,26 @@ get_header_data(Name, false) ->
throw(badfile)
end.
-md5_and_convert([],MD5State,Count) ->
+md5_and_convert([], MD5State, Count) ->
{[],MD5State,Count,[]};
-md5_and_convert([H|T],MD5State,Count) when is_binary(H) ->
+md5_and_convert([H|T], MD5State, Count) when is_binary(H) ->
case (catch binary_to_term(H)) of
{'EXIT', _} ->
md5_and_convert(T,MD5State,Count);
- ['$end_of_table',Dat] ->
- {[],MD5State,Count,['$end_of_table',Dat]};
+ ['$end_of_table',_Dat] = L ->
+ {[],MD5State,Count,L};
Term ->
- X = erlang:md5_update(MD5State,H),
- {Rest,NewMD5,NewCount,NewLast} = md5_and_convert(T,X,Count+1),
+ X = erlang:md5_update(MD5State, H),
+ {Rest,NewMD5,NewCount,NewLast} = md5_and_convert(T, X, Count+1),
{[Term | Rest],NewMD5,NewCount,NewLast}
end.
-scan_for_endinfo([],Count) ->
+
+scan_for_endinfo([], Count) ->
{[],Count,[]};
-scan_for_endinfo([['$end_of_table',Dat]],Count) ->
+scan_for_endinfo([['$end_of_table',Dat]], Count) ->
{['$end_of_table',Dat],Count,[]};
-scan_for_endinfo([Term|T],Count) ->
- {NewLast,NCount,Rest} = scan_for_endinfo(T,Count+1),
+scan_for_endinfo([Term|T], Count) ->
+ {NewLast,NCount,Rest} = scan_for_endinfo(T, Count+1),
{NewLast,NCount,[Term | Rest]}.
load_table(ReadFun, State, Tab) ->
@@ -852,23 +858,32 @@ load_table(ReadFun, State, Tab) ->
[] ->
{ok,NewState};
List ->
- ets:insert(Tab,List),
- load_table(ReadFun,NewState,Tab)
+ ets:insert(Tab, List),
+ load_table(ReadFun, NewState, Tab)
end.
-create_tab(I) ->
- {value, {name, Name}} = lists:keysearch(name, 1, I),
- {value, {type, Type}} = lists:keysearch(type, 1, I),
- {value, {protection, P}} = lists:keysearch(protection, 1, I),
- {value, {named_table, Val}} = lists:keysearch(named_table, 1, I),
- {value, {keypos, Kp}} = lists:keysearch(keypos, 1, I),
- {value, {size, Sz}} = lists:keysearch(size, 1, I),
- try
- Tab = ets:new(Name, [Type, P, {keypos, Kp} | named_table(Val)]),
- {ok, Tab, Sz}
- catch
- _:_ ->
- throw(cannot_create_table)
+create_tab(I, TabArg) ->
+ {name, Name} = lists:keyfind(name, 1, I),
+ {type, Type} = lists:keyfind(type, 1, I),
+ {protection, P} = lists:keyfind(protection, 1, I),
+ {named_table, Val} = lists:keyfind(named_table, 1, I),
+ {keypos, _Kp} = Keypos = lists:keyfind(keypos, 1, I),
+ {size, Sz} = lists:keyfind(size, 1, I),
+ Comp = case lists:keyfind(compressed, 1, I) of
+ {compressed, true} -> [compressed];
+ {compressed, false} -> [];
+ false -> []
+ end,
+ case TabArg of
+ [] ->
+ try
+ Tab = ets:new(Name, [Type, P, Keypos] ++ named_table(Val) ++ Comp),
+ {ok, Tab, Sz}
+ catch _:_ ->
+ throw(cannot_create_table)
+ end;
+ _ ->
+ {ok, TabArg, Sz}
end.
named_table(true) -> [named_table];
@@ -905,9 +920,9 @@ tabfile_info(File) when is_list(File) ; is_atom(File) ->
{value, Val} = lists:keysearch(named_table, 1, FullHeader),
{value, Kp} = lists:keysearch(keypos, 1, FullHeader),
{value, Sz} = lists:keysearch(size, 1, FullHeader),
- Ei = case lists:keysearch(extended_info, 1, FullHeader) of
- {value, Ei0} -> Ei0;
- _ -> {extended_info, []}
+ Ei = case lists:keyfind(extended_info, 1, FullHeader) of
+ false -> {extended_info, []};
+ Ei0 -> Ei0
end,
{ok, [N,Type,P,Val,Kp,Sz,Ei,{version,{Major,Minor}}]}
catch
@@ -1021,21 +1036,20 @@ options(Option, Keys) ->
options([Option], Keys, []).
options(Options, [Key | Keys], L) when is_list(Options) ->
- V = case lists:keysearch(Key, 1, Options) of
- {value, {n_objects, default}} ->
+ V = case lists:keyfind(Key, 1, Options) of
+ {n_objects, default} ->
{ok, default_option(Key)};
- {value, {n_objects, NObjs}} when is_integer(NObjs),
- NObjs >= 1 ->
+ {n_objects, NObjs} when is_integer(NObjs), NObjs >= 1 ->
{ok, NObjs};
- {value, {traverse, select}} ->
+ {traverse, select} ->
{ok, select};
- {value, {traverse, {select, MS}}} ->
- {ok, {select, MS}};
- {value, {traverse, first_next}} ->
+ {traverse, {select, _MS} = Select} ->
+ {ok, Select};
+ {traverse, first_next} ->
{ok, first_next};
- {value, {traverse, last_prev}} ->
+ {traverse, last_prev} ->
{ok, last_prev};
- {value, {Key, _}} ->
+ {Key, _} ->
badarg;
false ->
Default = default_option(Key),
diff --git a/lib/stdlib/src/eval_bits.erl b/lib/stdlib/src/eval_bits.erl
index 3671aecdcb..2cbd6cdae7 100644
--- a/lib/stdlib/src/eval_bits.erl
+++ b/lib/stdlib/src/eval_bits.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,6 +19,8 @@
%%
-module(eval_bits).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([expr_grp/3,expr_grp/5,match_bits/6,
match_bits/7,bin_gen/6]).
diff --git a/lib/stdlib/src/file_sorter.erl b/lib/stdlib/src/file_sorter.erl
index e21a0c88f3..2a5b08b581 100644
--- a/lib/stdlib/src/file_sorter.erl
+++ b/lib/stdlib/src/file_sorter.erl
@@ -18,6 +18,8 @@
%%
-module(file_sorter).
+%% Avoid warning for local function error/2 clashing with autoimported BIF.
+-compile({no_auto_import,[error/2]}).
-export([sort/1, sort/2, sort/3,
keysort/2, keysort/3, keysort/4,
merge/2, merge/3,
@@ -191,7 +193,7 @@ options([{format, Format} | L], Opts) when Format =:= binary;
options([{format, binary_term} | L], Opts) ->
options(L, Opts#opts{format = binary_term_fun()});
options([{size, Size} | L], Opts) when is_integer(Size), Size >= 0 ->
- options(L, Opts#opts{size = max(Size, 1)});
+ options(L, Opts#opts{size = erlang:max(Size, 1)});
options([{no_files, NoFiles} | L], Opts) when is_integer(NoFiles),
NoFiles > 1 ->
options(L, Opts#opts{no_files = NoFiles});
@@ -997,10 +999,10 @@ close_read_fun(Fd, FileName, fsort) ->
file:delete(FileName).
read_objs(Fd, FileName, I, L, Bin0, Size0, LSz, W) ->
- Max = max(Size0, ?CHUNKSIZE),
+ Max = erlang:max(Size0, ?CHUNKSIZE),
BSz0 = byte_size(Bin0),
Min = Size0 - BSz0 + W#w.hdlen, % Min > 0
- NoBytes = max(Min, Max),
+ NoBytes = erlang:max(Min, Max),
case read(Fd, FileName, NoBytes, W) of
{ok, Bin} ->
BSz = byte_size(Bin),
@@ -1180,9 +1182,6 @@ make_key2([Kp], T) ->
make_key2([Kp | Kps], T) ->
[element(Kp, T) | make_key2(Kps, T)].
-max(A, B) when A < B -> B;
-max(A, _) -> A.
-
infun(W) ->
W1 = W#w{in = undefined},
try (W#w.in)(read) of
diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl
index 74c5172137..c845b61204 100644
--- a/lib/stdlib/src/filelib.erl
+++ b/lib/stdlib/src/filelib.erl
@@ -20,6 +20,8 @@
%% File utilities.
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([wildcard/1, wildcard/2, is_dir/1, is_file/1, is_regular/1,
compile_wildcard/1]).
-export([fold_files/5, last_modified/1, file_size/1, ensure_dir/1]).
@@ -40,66 +42,66 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--spec wildcard(name()) -> [file:filename()].
+-spec wildcard(file:name()) -> [file:filename()].
wildcard(Pattern) when is_list(Pattern) ->
?HANDLE_ERROR(do_wildcard(Pattern, file)).
--spec wildcard(name(), name() | atom()) -> [file:filename()].
-wildcard(Pattern, Cwd) when is_list(Pattern), is_list(Cwd) ->
+-spec wildcard(file:name(), file:name() | atom()) -> [file:filename()].
+wildcard(Pattern, Cwd) when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)) ->
?HANDLE_ERROR(do_wildcard(Pattern, Cwd, file));
wildcard(Pattern, Mod) when is_list(Pattern), is_atom(Mod) ->
?HANDLE_ERROR(do_wildcard(Pattern, Mod)).
--spec wildcard(name(), name(), atom()) -> [file:filename()].
+-spec wildcard(file:name(), file:name(), atom()) -> [file:filename()].
wildcard(Pattern, Cwd, Mod)
- when is_list(Pattern), is_list(Cwd), is_atom(Mod) ->
+ when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)), is_atom(Mod) ->
?HANDLE_ERROR(do_wildcard(Pattern, Cwd, Mod)).
--spec is_dir(name()) -> boolean().
+-spec is_dir(file:name()) -> boolean().
is_dir(Dir) ->
do_is_dir(Dir, file).
--spec is_dir(name(), atom()) -> boolean().
+-spec is_dir(file:name(), atom()) -> boolean().
is_dir(Dir, Mod) when is_atom(Mod) ->
do_is_dir(Dir, Mod).
--spec is_file(name()) -> boolean().
+-spec is_file(file:name()) -> boolean().
is_file(File) ->
do_is_file(File, file).
--spec is_file(name(), atom()) -> boolean().
+-spec is_file(file:name(), atom()) -> boolean().
is_file(File, Mod) when is_atom(Mod) ->
do_is_file(File, Mod).
--spec is_regular(name()) -> boolean().
+-spec is_regular(file:name()) -> boolean().
is_regular(File) ->
do_is_regular(File, file).
--spec is_regular(name(), atom()) -> boolean().
+-spec is_regular(file:name(), atom()) -> boolean().
is_regular(File, Mod) when is_atom(Mod) ->
do_is_regular(File, Mod).
--spec fold_files(name(), string(), boolean(), fun((_,_) -> _), _) -> _.
+-spec fold_files(file:name(), string(), boolean(), fun((_,_) -> _), _) -> _.
fold_files(Dir, RegExp, Recursive, Fun, Acc) ->
do_fold_files(Dir, RegExp, Recursive, Fun, Acc, file).
--spec fold_files(name(), string(), boolean(), fun((_,_) -> _), _, atom()) -> _.
+-spec fold_files(file:name(), string(), boolean(), fun((_,_) -> _), _, atom()) -> _.
fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod) when is_atom(Mod) ->
do_fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod).
--spec last_modified(name()) -> date_time() | 0.
+-spec last_modified(file:name()) -> file:date_time() | 0.
last_modified(File) ->
do_last_modified(File, file).
--spec last_modified(name(), atom()) -> date_time() | 0.
+-spec last_modified(file:name(), atom()) -> file:date_time() | 0.
last_modified(File, Mod) when is_atom(Mod) ->
do_last_modified(File, Mod).
--spec file_size(name()) -> non_neg_integer().
+-spec file_size(file:name()) -> non_neg_integer().
file_size(File) ->
do_file_size(File, file).
--spec file_size(name(), atom()) -> non_neg_integer().
+-spec file_size(file:name(), atom()) -> non_neg_integer().
file_size(File, Mod) when is_atom(Mod) ->
do_file_size(File, Mod).
@@ -116,7 +118,7 @@ do_wildcard_comp({compiled_wildcard,{exists,File}}, Mod) ->
do_wildcard_comp({compiled_wildcard,[Base|Rest]}, Mod) ->
do_wildcard_1([Base], Rest, Mod).
-do_wildcard(Pattern, Cwd, Mod) when is_list(Pattern), is_list(Cwd) ->
+do_wildcard(Pattern, Cwd, Mod) when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)) ->
do_wildcard_comp(do_compile_wildcard(Pattern), Cwd, Mod).
do_wildcard_comp({compiled_wildcard,{exists,File}}, Cwd, Mod) ->
@@ -125,9 +127,18 @@ do_wildcard_comp({compiled_wildcard,{exists,File}}, Cwd, Mod) ->
_ -> []
end;
do_wildcard_comp({compiled_wildcard,[current|Rest]}, Cwd0, Mod) ->
- Cwd = filename:join([Cwd0]), %Slash away redundant slashes.
- PrefixLen = length(Cwd)+1,
- [lists:nthtail(PrefixLen, N) || N <- do_wildcard_1([Cwd], Rest, Mod)];
+ {Cwd,PrefixLen} = case filename:join([Cwd0]) of
+ Bin when is_binary(Bin) -> {Bin,byte_size(Bin)+1};
+ Other -> {Other,length(Other)+1}
+ end, %Slash away redundant slashes.
+ [
+ if
+ is_binary(N) ->
+ <<_:PrefixLen/binary,Res/binary>> = N,
+ Res;
+ true ->
+ lists:nthtail(PrefixLen, N)
+ end || N <- do_wildcard_1([Cwd], Rest, Mod)];
do_wildcard_comp({compiled_wildcard,[Base|Rest]}, _Cwd, Mod) ->
do_wildcard_1([Base], Rest, Mod).
@@ -164,36 +175,44 @@ do_is_regular(File, Mod) ->
%% If <Recursive> is true all sub-directories to <Dir> are processed
do_fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod) ->
- {ok, Re1} = re:compile(RegExp),
- do_fold_files1(Dir, Re1, Recursive, Fun, Acc, Mod).
+ {ok, Re1} = re:compile(RegExp,[unicode]),
+ do_fold_files1(Dir, Re1, RegExp, Recursive, Fun, Acc, Mod).
-do_fold_files1(Dir, RegExp, Recursive, Fun, Acc, Mod) ->
+do_fold_files1(Dir, RegExp, OrigRE, Recursive, Fun, Acc, Mod) ->
case eval_list_dir(Dir, Mod) of
- {ok, Files} -> do_fold_files2(Files, Dir, RegExp, Recursive, Fun, Acc, Mod);
+ {ok, Files} -> do_fold_files2(Files, Dir, RegExp, OrigRE,
+ Recursive, Fun, Acc, Mod);
{error, _} -> Acc
end.
-do_fold_files2([], _Dir, _RegExp, _Recursive, _Fun, Acc, _Mod) ->
+%% OrigRE is not to be compiled as it's for non conforming filenames,
+%% i.e. for filenames that does not comply to the current encoding, which should
+%% be very rare. We use it only in those cases and do not want to precompile.
+do_fold_files2([], _Dir, _RegExp, _OrigRE, _Recursive, _Fun, Acc, _Mod) ->
Acc;
-do_fold_files2([File|T], Dir, RegExp, Recursive, Fun, Acc0, Mod) ->
+do_fold_files2([File|T], Dir, RegExp, OrigRE, Recursive, Fun, Acc0, Mod) ->
FullName = filename:join(Dir, File),
case do_is_regular(FullName, Mod) of
true ->
- case re:run(File, RegExp, [{capture,none}]) of
+ case (catch re:run(File, if is_binary(File) -> OrigRE;
+ true -> RegExp end,
+ [{capture,none}])) of
match ->
Acc = Fun(FullName, Acc0),
- do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc, Mod);
+ do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc, Mod);
+ {'EXIT',_} ->
+ do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc0, Mod);
nomatch ->
- do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc0, Mod)
+ do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc0, Mod)
end;
false ->
case Recursive andalso do_is_dir(FullName, Mod) of
true ->
- Acc1 = do_fold_files1(FullName, RegExp, Recursive,
+ Acc1 = do_fold_files1(FullName, RegExp, OrigRE, Recursive,
Fun, Acc0, Mod),
- do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc1, Mod);
+ do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc1, Mod);
false ->
- do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc0, Mod)
+ do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc0, Mod)
end
end.
@@ -218,7 +237,7 @@ do_file_size(File, Mod) ->
%% +type X = filename() | dirname()
%% ensures that the directory name required to create D exists
--spec ensure_dir(name()) -> 'ok' | {'error', posix()}.
+-spec ensure_dir(file:name()) -> 'ok' | {'error', file:posix()}.
ensure_dir("/") ->
ok;
ensure_dir(F) ->
@@ -266,6 +285,13 @@ do_wildcard_3(Base, [Pattern|Rest], Result, Mod) ->
do_wildcard_3(Base, [], Result, _Mod) ->
[Base|Result].
+wildcard_4(Pattern, [File|Rest], Base, Result) when is_binary(File) ->
+ case wildcard_5(Pattern, binary_to_list(File)) of
+ true ->
+ wildcard_4(Pattern, Rest, Base, [join(Base, File)|Result]);
+ false ->
+ wildcard_4(Pattern, Rest, Base, Result)
+ end;
wildcard_4(Pattern, [File|Rest], Base, Result) ->
case wildcard_5(Pattern, File) of
true ->
diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl
index cd26b2e219..e38b8957f2 100644
--- a/lib/stdlib/src/filename.erl
+++ b/lib/stdlib/src/filename.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(filename).
@@ -41,6 +41,9 @@
-include_lib("kernel/include/file.hrl").
+-define(IS_DRIVELETTER(Letter),(((Letter >= $A) andalso (Letter =< $Z)) orelse
+ ((Letter >= $a) andalso (Letter =< $z)))).
+
%% Converts a relative filename to an absolute filename
%% or the filename itself if it already is an absolute filename
%% Note that no attempt is made to create the most beatiful
@@ -57,12 +60,18 @@
%% (for Unix) : absname("/") -> "/"
%% (for WIN32): absname("/") -> "D:/"
--spec absname(name()) -> string().
+
+-spec absname(file:name()) -> file:filename().
absname(Name) ->
{ok, Cwd} = file:get_cwd(),
absname(Name, Cwd).
--spec absname(name(), string()) -> string().
+-spec absname(file:name(), file:filename()) -> file:filename().
+absname(Name, AbsBase) when is_binary(Name), is_list(AbsBase) ->
+ absname(Name,filename_string_to_binary(AbsBase));
+absname(Name, AbsBase) when is_list(Name), is_binary(AbsBase) ->
+ absname(filename_string_to_binary(Name),AbsBase);
+
absname(Name, AbsBase) ->
case pathtype(Name) of
relative ->
@@ -77,6 +86,20 @@ absname(Name, AbsBase) ->
%% Handles volumerelative names (on Windows only).
+absname_vr([<<"/">>|Rest1], [Volume|_], _AbsBase) ->
+ %% Absolute path on current drive.
+ join([Volume|Rest1]);
+absname_vr([<<X, $:>>|Rest1], [<<X,_/binary>>|_], AbsBase) ->
+ %% Relative to current directory on current drive.
+ absname(join(Rest1), AbsBase);
+absname_vr([<<X, $:>>|Name], _, _AbsBase) ->
+ %% Relative to current directory on another drive.
+ Dcwd =
+ case file:get_cwd([X, $:]) of
+ {ok, Dir} -> filename_string_to_binary(Dir);
+ {error, _} -> <<X, $:, $/>>
+ end,
+ absname(join(Name), Dcwd);
absname_vr(["/"|Rest1], [Volume|_], _AbsBase) ->
%% Absolute path on current drive.
join([Volume|Rest1]);
@@ -92,41 +115,13 @@ absname_vr([[X, $:]|Name], _, _AbsBase) ->
end,
absname(join(Name), Dcwd).
-%% Joins a relative filename to an absolute base. For VxWorks the
-%% resulting name is fixed to minimize the length by collapsing
-%% ".." directories.
-%% For other systems this is just a join/2, but assumes that
+%% Joins a relative filename to an absolute base.
+%% This is just a join/2, but assumes that
%% AbsBase must be absolute and Name must be relative.
--spec absname_join(string(), name()) -> string().
+-spec absname_join(file:filename(), file:name()) -> file:filename().
absname_join(AbsBase, Name) ->
- case major_os_type() of
- vxworks ->
- absname_pretty(AbsBase, split(Name), lists:reverse(split(AbsBase)));
- _Else ->
- join(AbsBase, flatten(Name))
- end.
-
-%% Handles absolute filenames for VxWorks - these are 'pretty-printed',
-%% since a C function call chdir("/erlang/lib/../bin") really sets
-%% cwd to '/erlang/lib/../bin' which also works, but the long term
-%% effect is potentially not so good ...
-%%
-%% absname_pretty("../bin", "/erlang/lib") -> "/erlang/bin"
-%% absname_pretty("../../../..", "/erlang") -> "/erlang"
-
-absname_pretty(Abspath, Relpath, []) ->
- %% AbsBase _must_ begin with a vxworks device name
- {device, _Rest, Dev} = vxworks_first(Abspath),
- absname_pretty(Abspath, Relpath, [lists:reverse(Dev)]);
-absname_pretty(_Abspath, [], AbsBase) ->
- join(lists:reverse(AbsBase));
-absname_pretty(Abspath, [[$.]|Rest], AbsBase) ->
- absname_pretty(Abspath, Rest, AbsBase);
-absname_pretty(Abspath, [[$.,$.]|Rest], [_|AbsRest]) ->
- absname_pretty(Abspath, Rest, AbsRest);
-absname_pretty(Abspath, [First|Rest], AbsBase) ->
- absname_pretty(Abspath, Rest, [First|AbsBase]).
+ join(AbsBase, flatten(Name)).
%% Returns the part of the filename after the last directory separator,
%% or the filename itself if it has no separators.
@@ -136,12 +131,36 @@ absname_pretty(Abspath, [First|Rest], AbsBase) ->
%% basename("/usr/foo/") -> "foo" (trailing slashes ignored)
%% basename("/") -> []
--spec basename(name()) -> string().
+-spec basename(file:name()) -> file:filename().
+basename(Name) when is_binary(Name) ->
+ case os:type() of
+ {win32,_} ->
+ win_basenameb(Name);
+ _ ->
+ basenameb(Name,[<<"/">>])
+ end;
+
basename(Name0) ->
Name = flatten(Name0),
{DirSep2, DrvSep} = separators(),
basename1(skip_prefix(Name, DrvSep), [], DirSep2).
+win_basenameb(<<Letter,$:,Rest/binary>>) when ?IS_DRIVELETTER(Letter) ->
+ basenameb(Rest,[<<"/">>,<<"\\">>]);
+win_basenameb(O) ->
+ basenameb(O,[<<"/">>,<<"\\">>]).
+basenameb(Bin,Sep) ->
+ Parts = [ X || X <- binary:split(Bin,Sep,[global]),
+ X =/= <<>> ],
+ if
+ Parts =:= [] ->
+ <<>>;
+ true ->
+ lists:last(Parts)
+ end.
+
+
+
basename1([$/|[]], Tail, DirSep2) ->
basename1([], Tail, DirSep2);
basename1([$/|Rest], _Tail, DirSep2) ->
@@ -155,26 +174,11 @@ basename1([Char|Rest], Tail, DirSep2) when is_integer(Char) ->
basename1([], Tail, _DirSep2) ->
lists:reverse(Tail).
-skip_prefix(Name, false) -> % No prefix for unix, but for VxWorks.
- case major_os_type() of
- vxworks ->
- case vxworks_first(Name) of
- {device, Rest, _Device} ->
- Rest;
- {not_device, _Rest, _First} ->
- Name
- end;
- _Else ->
- Name
- end;
-skip_prefix(Name, DrvSep) ->
- skip_prefix1(Name, DrvSep).
-
-skip_prefix1([L, DrvSep|Name], DrvSep) when is_integer(L) ->
+skip_prefix(Name, false) ->
+ Name;
+skip_prefix([L, DrvSep|Name], DrvSep) when ?IS_DRIVELETTER(L) ->
Name;
-skip_prefix1([L], _) when is_integer(L) ->
- [L];
-skip_prefix1(Name, _) ->
+skip_prefix(Name, _) ->
Name.
%% Returns the last component of the filename, with the given
@@ -190,7 +194,29 @@ skip_prefix1(Name, _) ->
%% rootname(basename("xxx.jam")) -> "xxx"
%% rootname(basename("xxx.erl")) -> "xxx"
--spec basename(name(), name()) -> string().
+-spec basename(file:name(), file:name()) -> file:filename().
+basename(Name, Ext) when is_binary(Name), is_list(Ext) ->
+ basename(Name,filename_string_to_binary(Ext));
+basename(Name, Ext) when is_list(Name), is_binary(Ext) ->
+ basename(filename_string_to_binary(Name),Ext);
+basename(Name, Ext) when is_binary(Name), is_binary(Ext) ->
+ BName = basename(Name),
+ LAll = byte_size(Name),
+ LN = byte_size(BName),
+ LE = byte_size(Ext),
+ case LN - LE of
+ Neg when Neg < 0 ->
+ BName;
+ Pos ->
+ StartLen = LAll - Pos - LE,
+ case Name of
+ <<_:StartLen/binary,Part:Pos/binary,Ext/binary>> ->
+ Part;
+ _Other ->
+ BName
+ end
+ end;
+
basename(Name0, Ext0) ->
Name = flatten(Name0),
Ext = flatten(Ext0),
@@ -204,7 +230,7 @@ basename([$/|[]], Ext, Tail, DrvSep2) ->
basename([], Ext, Tail, DrvSep2);
basename([$/|Rest], Ext, _Tail, DrvSep2) ->
basename(Rest, Ext, [], DrvSep2);
-basename([$\\|Rest], Ext, Tail, DirSep2) when is_integer(DirSep2) ->
+basename([DirSep2|Rest], Ext, Tail, DirSep2) when is_integer(DirSep2) ->
basename([$/|Rest], Ext, Tail, DirSep2);
basename([Char|Rest], Ext, Tail, DrvSep2) when is_integer(Char) ->
basename(Rest, Ext, [Char|Tail], DrvSep2);
@@ -216,21 +242,43 @@ basename([], _Ext, Tail, _DrvSep2) ->
%% Example: dirname("/usr/src/kalle.erl") -> "/usr/src",
%% dirname("kalle.erl") -> "."
--spec dirname(name()) -> string().
+-spec dirname(file:name()) -> file:filename().
+dirname(Name) when is_binary(Name) ->
+ {Dsep,Drivesep} = separators(),
+ SList = case Dsep of
+ Sep when is_integer(Sep) ->
+ [ <<Sep>> ];
+ _ ->
+ []
+ end,
+ {XPart0,Dirs} = case Drivesep of
+ X when is_integer(X) ->
+ case Name of
+ <<DL,X,Rest/binary>> when ?IS_DRIVELETTER(DL) ->
+ {<<DL,X>>,Rest};
+ _ ->
+ {<<>>,Name}
+ end;
+ _ ->
+ {<<>>,Name}
+ end,
+ Parts0 = binary:split(Dirs,[<<"/">>|SList],[global]),
+ %% Fairly short lists of parts, OK to reverse twice...
+ Parts = case Parts0 of
+ [] -> [];
+ _ -> lists:reverse(fstrip(tl(lists:reverse(Parts0))))
+ end,
+ XPart = case {Parts,XPart0} of
+ {[],<<>>} ->
+ <<".">>;
+ _ ->
+ XPart0
+ end,
+ dirjoin(Parts,XPart,<<"/">>);
+
dirname(Name0) ->
Name = flatten(Name0),
- case os:type() of
- vxworks ->
- {Devicep, Restname, FirstComp} = vxworks_first(Name),
- case Devicep of
- device ->
- dirname(Restname, FirstComp, [], separators());
- _ ->
- dirname(Name, [], [], separators())
- end;
- _ ->
- dirname(Name, [], [], separators())
- end.
+ dirname(Name, [], [], separators()).
dirname([[_|_]=List|Rest], Dir, File, Seps) ->
dirname(List++Rest, Dir, File, Seps);
@@ -258,6 +306,26 @@ dirname([], [DrvSep,Dl], File, {_,DrvSep}) ->
end;
dirname([], Dir, _, _) ->
lists:reverse(Dir).
+
+%% Compatibility with lists variant, remove trailing slashes
+fstrip([<<>>,X|Y]) ->
+ fstrip([X|Y]);
+fstrip(A) ->
+ A.
+
+
+dirjoin([<<>>|T],Acc,Sep) ->
+ dirjoin1(T,<<Acc/binary,"/">>,Sep);
+dirjoin(A,B,C) ->
+ dirjoin1(A,B,C).
+
+dirjoin1([],Acc,_) ->
+ Acc;
+dirjoin1([One],Acc,_) ->
+ <<Acc/binary,One/binary>>;
+dirjoin1([H|T],Acc,Sep) ->
+ dirjoin(T,<<Acc/binary,H/binary,Sep/binary>>,Sep).
+
%% Given a filename string, returns the file extension,
%% including the period. Returns an empty list if there
@@ -268,7 +336,31 @@ dirname([], Dir, _, _) ->
%%
%% On Windows: fn:dirname("\\usr\\src/kalle.erl") -> "/usr/src"
--spec extension(name()) -> string().
+-spec extension(file:name()) -> file:filename().
+extension(Name) when is_binary(Name) ->
+ {Dsep,_} = separators(),
+ SList = case Dsep of
+ Sep when is_integer(Sep) ->
+ [ <<Sep>> ];
+ _ ->
+ []
+ end,
+ case binary:matches(Name,[<<".">>]) of
+ nomatch -> % Bug in binary workaround :(
+ <<>>;
+ [] ->
+ <<>>;
+ List ->
+ {Pos,_} = lists:last(List),
+ <<_:Pos/binary,Part/binary>> = Name,
+ case binary:match(Part,[<<"/">>|SList]) of
+ nomatch ->
+ Part;
+ _ ->
+ <<>>
+ end
+ end;
+
extension(Name0) ->
Name = flatten(Name0),
extension(Name, [], major_os_type()).
@@ -281,8 +373,6 @@ extension([$/|Rest], _Result, OsType) ->
extension(Rest, [], OsType);
extension([$\\|Rest], _Result, win32) ->
extension(Rest, [], win32);
-extension([$\\|Rest], _Result, vxworks) ->
- extension(Rest, [], vxworks);
extension([Char|Rest], Result, OsType) when is_integer(Char) ->
extension(Rest, [Char|Result], OsType);
extension([], Result, _OsType) ->
@@ -290,23 +380,36 @@ extension([], Result, _OsType) ->
%% Joins a list of filenames with directory separators.
--spec join([string()]) -> string().
+-spec join([file:filename()]) -> file:filename().
join([Name1, Name2|Rest]) ->
join([join(Name1, Name2)|Rest]);
join([Name]) when is_list(Name) ->
join1(Name, [], [], major_os_type());
+join([Name]) when is_binary(Name) ->
+ join1b(Name, <<>>, [], major_os_type());
join([Name]) when is_atom(Name) ->
join([atom_to_list(Name)]).
%% Joins two filenames with directory separators.
--spec join(string(), string()) -> string().
+-spec join(file:filename(), file:filename()) -> file:filename().
join(Name1, Name2) when is_list(Name1), is_list(Name2) ->
OsType = major_os_type(),
case pathtype(Name2) of
relative -> join1(Name1, Name2, [], OsType);
_Other -> join1(Name2, [], [], OsType)
end;
+join(Name1, Name2) when is_binary(Name1), is_list(Name2) ->
+ join(Name1,filename_string_to_binary(Name2));
+join(Name1, Name2) when is_list(Name1), is_binary(Name2) ->
+ join(filename_string_to_binary(Name1),Name2);
+join(Name1, Name2) when is_binary(Name1), is_binary(Name2) ->
+ OsType = major_os_type(),
+ case pathtype(Name2) of
+ relative -> join1b(Name1, Name2, [], OsType);
+ _Other -> join1b(Name2, <<>>, [], OsType)
+ end;
+
join(Name1, Name2) when is_atom(Name1) ->
join(atom_to_list(Name1), Name2);
join(Name1, Name2) when is_atom(Name2) ->
@@ -321,8 +424,6 @@ when is_integer(UcLetter), UcLetter >= $A, UcLetter =< $Z ->
join1(Rest, RelativeName, [$:, UcLetter+$a-$A], win32);
join1([$\\|Rest], RelativeName, Result, win32) ->
join1([$/|Rest], RelativeName, Result, win32);
-join1([$\\|Rest], RelativeName, Result, vxworks) ->
- join1([$/|Rest], RelativeName, Result, vxworks);
join1([$/|Rest], RelativeName, [$., $/|Result], OsType) ->
join1(Rest, RelativeName, [$/|Result], OsType);
join1([$/|Rest], RelativeName, [$/|Result], OsType) ->
@@ -344,6 +445,26 @@ join1([Char|Rest], RelativeName, Result, OsType) when is_integer(Char) ->
join1([Atom|Rest], RelativeName, Result, OsType) when is_atom(Atom) ->
join1(atom_to_list(Atom)++Rest, RelativeName, Result, OsType).
+join1b(<<UcLetter, $:, Rest/binary>>, RelativeName, [], win32)
+when is_integer(UcLetter), UcLetter >= $A, UcLetter =< $Z ->
+ join1b(Rest, RelativeName, [$:, UcLetter+$a-$A], win32);
+join1b(<<$\\,Rest/binary>>, RelativeName, Result, win32) ->
+ join1b(<<$/,Rest/binary>>, RelativeName, Result, win32);
+join1b(<<$/,Rest/binary>>, RelativeName, [$., $/|Result], OsType) ->
+ join1b(Rest, RelativeName, [$/|Result], OsType);
+join1b(<<$/,Rest/binary>>, RelativeName, [$/|Result], OsType) ->
+ join1b(Rest, RelativeName, [$/|Result], OsType);
+join1b(<<>>, <<>>, Result, OsType) ->
+ list_to_binary(maybe_remove_dirsep(Result, OsType));
+join1b(<<>>, RelativeName, [$:|Rest], win32) ->
+ join1b(RelativeName, <<>>, [$:|Rest], win32);
+join1b(<<>>, RelativeName, [$/|Result], OsType) ->
+ join1b(RelativeName, <<>>, [$/|Result], OsType);
+join1b(<<>>, RelativeName, Result, OsType) ->
+ join1b(RelativeName, <<>>, [$/|Result], OsType);
+join1b(<<Char,Rest/binary>>, RelativeName, Result, OsType) when is_integer(Char) ->
+ join1b(Rest, RelativeName, [Char|Result], OsType).
+
maybe_remove_dirsep([$/, $:, Letter], win32) ->
[Letter, $:, $/];
maybe_remove_dirsep([$/], _) ->
@@ -357,7 +478,7 @@ maybe_remove_dirsep(Name, _) ->
%% a given base directory, which is is assumed to be normalised
%% by a previous call to join/{1,2}.
--spec append(string(), name()) -> string().
+-spec append(file:filename(), file:name()) -> file:filename().
append(Dir, Name) ->
Dir ++ [$/|Name].
@@ -373,22 +494,17 @@ append(Dir, Name) ->
%% current working volume. (Windows only)
%% Example: a:bar.erl, /temp/foo.erl
--spec pathtype(name()) -> 'absolute' | 'relative' | 'volumerelative'.
+-spec pathtype(file:name()) -> 'absolute' | 'relative' | 'volumerelative'.
pathtype(Atom) when is_atom(Atom) ->
pathtype(atom_to_list(Atom));
-pathtype(Name) when is_list(Name) ->
+pathtype(Name) when is_list(Name) or is_binary(Name) ->
case os:type() of
{unix, _} -> unix_pathtype(Name);
- {win32, _} -> win32_pathtype(Name);
- vxworks -> case vxworks_first(Name) of
- {device, _Rest, _Dev} ->
- absolute;
- _ ->
- relative
- end;
- {ose,_} -> unix_pathtype(Name)
+ {win32, _} -> win32_pathtype(Name)
end.
+unix_pathtype(<<$/,_/binary>>) ->
+ absolute;
unix_pathtype([$/|_]) ->
absolute;
unix_pathtype([List|Rest]) when is_list(List) ->
@@ -404,6 +520,15 @@ win32_pathtype([Atom|Rest]) when is_atom(Atom) ->
win32_pathtype(atom_to_list(Atom)++Rest);
win32_pathtype([Char, List|Rest]) when is_list(List) ->
win32_pathtype([Char|List++Rest]);
+win32_pathtype(<<$/, $/, _/binary>>) -> absolute;
+win32_pathtype(<<$\\, $/, _/binary>>) -> absolute;
+win32_pathtype(<<$/, $\\, _/binary>>) -> absolute;
+win32_pathtype(<<$\\, $\\, _/binary>>) -> absolute;
+win32_pathtype(<<$/, _/binary>>) -> volumerelative;
+win32_pathtype(<<$\\, _/binary>>) -> volumerelative;
+win32_pathtype(<<_Letter, $:, $/, _/binary>>) -> absolute;
+win32_pathtype(<<_Letter, $:, $\\, _/binary>>) -> absolute;
+win32_pathtype(<<_Letter, $:, _/binary>>) -> volumerelative;
win32_pathtype([$/, $/|_]) -> absolute;
win32_pathtype([$\\, $/|_]) -> absolute;
win32_pathtype([$/, $\\|_]) -> absolute;
@@ -422,7 +547,9 @@ win32_pathtype(_) -> relative.
%% Examples: rootname("/jam.src/kalle") -> "/jam.src/kalle"
%% rootname("/jam.src/foo.erl") -> "/jam.src/foo"
--spec rootname(name()) -> string().
+-spec rootname(file:name()) -> file:filename().
+rootname(Name) when is_binary(Name) ->
+ list_to_binary(rootname(binary_to_list(Name))); % No need to handle unicode, . is < 128
rootname(Name0) ->
Name = flatten(Name0),
rootname(Name, [], [], major_os_type()).
@@ -431,8 +558,6 @@ rootname([$/|Rest], Root, Ext, OsType) ->
rootname(Rest, [$/]++Ext++Root, [], OsType);
rootname([$\\|Rest], Root, Ext, win32) ->
rootname(Rest, [$/]++Ext++Root, [], win32);
-rootname([$\\|Rest], Root, Ext, vxworks) ->
- rootname(Rest, [$/]++Ext++Root, [], vxworks);
rootname([$.|Rest], Root, [], OsType) ->
rootname(Rest, Root, ".", OsType);
rootname([$.|Rest], Root, Ext, OsType) ->
@@ -451,7 +576,13 @@ rootname([], Root, _Ext, _OsType) ->
%% Examples: rootname("/jam.src/kalle.jam", ".erl") -> "/jam.src/kalle.jam"
%% rootname("/jam.src/foo.erl", ".erl") -> "/jam.src/foo"
--spec rootname(name(), name()) -> string().
+-spec rootname(file:name(), file:name()) -> file:filename().
+rootname(Name, Ext) when is_binary(Name), is_binary(Ext) ->
+ list_to_binary(rootname(binary_to_list(Name),binary_to_list(Ext)));
+rootname(Name, Ext) when is_binary(Name) ->
+ rootname(Name,filename_string_to_binary(Ext));
+rootname(Name, Ext) when is_binary(Ext) ->
+ rootname(filename_string_to_binary(Name),Ext);
rootname(Name0, Ext0) ->
Name = flatten(Name0),
Ext = flatten(Ext0),
@@ -471,27 +602,55 @@ rootname2([Char|Rest], Ext, Result) when is_integer(Char) ->
%% split("foo/bar") -> ["foo", "bar"]
%% split("a:\\msdev\\include") -> ["a:/", "msdev", "include"]
--spec split(name()) -> [string()].
+-spec split(file:name()) -> [file:filename()].
+split(Name) when is_binary(Name) ->
+ case os:type() of
+ {win32, _} -> win32_splitb(Name);
+ _ -> unix_splitb(Name)
+ end;
+
split(Name0) ->
Name = flatten(Name0),
case os:type() of
- {unix, _} -> unix_split(Name);
{win32, _} -> win32_split(Name);
- vxworks -> vxworks_split(Name);
- {ose,_} -> unix_split(Name)
+ _ -> unix_split(Name)
end.
-%% If a VxWorks filename starts with '[/\].*[^/\]' '[/\].*:' or '.*:'
-%% that part of the filename is considered a device.
-%% The rest of the name is interpreted exactly as for win32.
-%% XXX - dirty solution to make filename:split([]) return the same thing on
-%% VxWorks as on unix and win32.
-vxworks_split([]) ->
- [];
-vxworks_split(L) ->
- {_Devicep, Rest, FirstComp} = vxworks_first(L),
- split(Rest, [], [lists:reverse(FirstComp)], win32).
+unix_splitb(Name) ->
+ L = binary:split(Name,[<<"/">>],[global]),
+ LL = case L of
+ [<<>>|Rest] ->
+ [<<"/">>|Rest];
+ _ ->
+ L
+ end,
+ [ X || X <- LL, X =/= <<>>].
+
+
+fix_driveletter(Letter0) ->
+ if
+ Letter0 >= $A, Letter0 =< $Z ->
+ Letter0+$a-$A;
+ true ->
+ Letter0
+ end.
+win32_splitb(<<Letter0,$:, Slash, Rest/binary>>) when (((Slash =:= $\\) orelse (Slash =:= $/)) andalso
+ ?IS_DRIVELETTER(Letter0)) ->
+ Letter = fix_driveletter(Letter0),
+ L = binary:split(Rest,[<<"/">>,<<"\\">>],[global]),
+ [<<Letter,$:,$/>> | [ X || X <- L, X =/= <<>> ]];
+win32_splitb(<<Letter0,$:,Rest/binary>>) when ?IS_DRIVELETTER(Letter0) ->
+ Letter = fix_driveletter(Letter0),
+ L = binary:split(Rest,[<<"/">>,<<"\\">>],[global]),
+ [<<Letter,$:>> | [ X || X <- L, X =/= <<>> ]];
+win32_splitb(<<Slash,Rest/binary>>) when ((Slash =:= $\\) orelse (Slash =:= $/)) ->
+ L = binary:split(Rest,[<<"/">>,<<"\\">>],[global]),
+ [<<$/>> | [ X || X <- L, X =/= <<>> ]];
+win32_splitb(Name) ->
+ L = binary:split(Name,[<<"/">>,<<"\\">>],[global]),
+ [ X || X <- L, X =/= <<>> ].
+
unix_split(Name) ->
split(Name, [], unix).
@@ -502,8 +661,6 @@ win32_split([X, $\\|Rest]) when is_integer(X) ->
win32_split([X, $/|Rest]);
win32_split([X, Y, $\\|Rest]) when is_integer(X), is_integer(Y) ->
win32_split([X, Y, $/|Rest]);
-win32_split([$/, $/|Rest]) ->
- split(Rest, [], [[$/, $/]]);
win32_split([UcLetter, $:|Rest]) when UcLetter >= $A, UcLetter =< $Z ->
win32_split([UcLetter+$a-$A, $:|Rest]);
win32_split([Letter, $:, $/|Rest]) ->
@@ -540,7 +697,7 @@ split([], Comp, Components, OsType) ->
%% will be converted to backslashes. On all platforms, the
%% name will be normalized as done by join/1.
--spec nativename(string()) -> string().
+-spec nativename(file:filename()) -> file:filename().
nativename(Name0) ->
Name = join([Name0]), %Normalize.
case os:type() of
@@ -557,13 +714,12 @@ win32_nativename([]) ->
separators() ->
case os:type() of
- {unix, _} -> {false, false};
{win32, _} -> {$\\, $:};
- vxworks -> {$\\, false};
- {ose,_} -> {false, false}
+ _ -> {false, false}
end.
+
%% find_src(Module) --
%% find_src(Module, Rules) --
%%
@@ -733,45 +889,12 @@ major_os_type() ->
OsT -> OsT
end.
-%% Need to take care of the first pathname component separately
-%% due to VxWorks less than good device naming rules.
-%% (i.e. this is VxWorks specific ...)
-%% The following four all starts with device names
-%% elrond:/foo -> elrond:
-%% elrond:\\foo.bar -> elrond:
-%% /DISK1:foo -> /DISK1:
-%% /usr/include -> /usr
-%% This one doesn't:
-%% foo/bar
-
-vxworks_first([]) ->
- {not_device, [], []};
-vxworks_first([$/|T]) ->
- vxworks_first2(device, T, [$/]);
-vxworks_first([$\\|T]) ->
- vxworks_first2(device, T, [$/]);
-vxworks_first([H|T]) when is_list(H) ->
- vxworks_first(H++T);
-vxworks_first([H|T]) ->
- vxworks_first2(not_device, T, [H]).
-
-vxworks_first2(Devicep, [], FirstComp) ->
- {Devicep, [], FirstComp};
-vxworks_first2(Devicep, [$/|T], FirstComp) ->
- {Devicep, [$/|T], FirstComp};
-vxworks_first2(Devicep, [$\\|T], FirstComp) ->
- {Devicep, [$/|T], FirstComp};
-vxworks_first2(_Devicep, [$:|T], FirstComp)->
- {device, T, [$:|FirstComp]};
-vxworks_first2(Devicep, [H|T], FirstComp) when is_list(H) ->
- vxworks_first2(Devicep, H++T, FirstComp);
-vxworks_first2(Devicep, [H|T], FirstComp) ->
- vxworks_first2(Devicep, T, [H|FirstComp]).
-
%% flatten(List)
%% Flatten a list, also accepting atoms.
--spec flatten(name()) -> string().
+-spec flatten(file:name()) -> file:filename().
+flatten(Bin) when is_binary(Bin) ->
+ Bin;
flatten(List) ->
do_flatten(List, []).
@@ -785,3 +908,12 @@ do_flatten([], Tail) ->
Tail;
do_flatten(Atom, Tail) when is_atom(Atom) ->
atom_to_list(Atom) ++ flatten(Tail).
+
+filename_string_to_binary(List) ->
+ case unicode:characters_to_binary(flatten(List),unicode,file:native_name_encoding()) of
+ {error,_,_} ->
+ erlang:error(badarg);
+ Bin when is_binary(Bin) ->
+ Bin
+ end.
+
diff --git a/lib/stdlib/src/gb_sets.erl b/lib/stdlib/src/gb_sets.erl
index 086dc79b46..113f29e252 100644
--- a/lib/stdlib/src/gb_sets.erl
+++ b/lib/stdlib/src/gb_sets.erl
@@ -657,7 +657,7 @@ intersection_2([], _, As, S) ->
intersection_2(_, [], As, S) ->
{S, balance_revlist(As, S)}.
--spec intersection([gb_set()]) -> gb_set().
+-spec intersection([gb_set(),...]) -> gb_set().
intersection([S | Ss]) ->
intersection_list(S, Ss).
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index 5aab547644..43df6f621d 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(gen).
@@ -212,7 +212,22 @@ do_call(Process, Label, Request, Timeout) ->
catch erlang:send(Process, {Label, {self(), Mref}, Request},
[noconnect]),
- wait_resp_mon(Node, Mref, Timeout)
+ receive
+ {Mref, Reply} ->
+ erlang:demonitor(Mref, [flush]),
+ {ok, Reply};
+ {'DOWN', Mref, _, _, noconnection} ->
+ exit({nodedown, Node});
+ {'DOWN', Mref, _, _, Reason} ->
+ exit(Reason)
+ after Timeout ->
+ erlang:demonitor(Mref),
+ receive
+ {'DOWN', Mref, _, _, _} -> true
+ after 0 -> true
+ end,
+ exit(timeout)
+ end
catch
error:_ ->
%% Node (C/Java?) is not supporting the monitor.
@@ -233,24 +248,6 @@ do_call(Process, Label, Request, Timeout) ->
end
end.
-wait_resp_mon(Node, Mref, Timeout) ->
- receive
- {Mref, Reply} ->
- erlang:demonitor(Mref, [flush]),
- {ok, Reply};
- {'DOWN', Mref, _, _, noconnection} ->
- exit({nodedown, Node});
- {'DOWN', Mref, _, _, Reason} ->
- exit(Reason)
- after Timeout ->
- erlang:demonitor(Mref),
- receive
- {'DOWN', Mref, _, _, _} -> true
- after 0 -> true
- end,
- exit(timeout)
- end.
-
wait_resp(Node, Tag, Timeout) ->
receive
{Tag, Reply} ->
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index 1b30aaf5eb..b1e9e3a02f 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(gen_event).
@@ -42,7 +42,6 @@
system_continue/3,
system_terminate/4,
system_code_change/4,
- print_event/3,
format_status/2]).
-import(error_logger, [error_msg/2]).
@@ -239,7 +238,7 @@ fetch_msg(Parent, ServerName, MSL, Debug, Hib) ->
Msg when Debug =:= [] ->
handle_msg(Msg, Parent, ServerName, MSL, []);
Msg ->
- Debug1 = sys:handle_debug(Debug, {?MODULE, print_event},
+ Debug1 = sys:handle_debug(Debug, fun print_event/3,
ServerName, {in, Msg}),
handle_msg(Msg, Parent, ServerName, MSL, Debug1)
end.
@@ -678,12 +677,23 @@ report_error(Handler, Reason, State, LastIn, SName) ->
_ ->
Reason
end,
+ Mod = Handler#handler.module,
+ FmtState = case erlang:function_exported(Mod, format_status, 2) of
+ true ->
+ Args = [get(), State],
+ case catch Mod:format_status(terminate, Args) of
+ {'EXIT', _} -> State;
+ Else -> Else
+ end;
+ _ ->
+ State
+ end,
error_msg("** gen_event handler ~p crashed.~n"
"** Was installed in ~p~n"
"** Last event was: ~p~n"
"** When handler state == ~p~n"
"** Reason == ~p~n",
- [handler(Handler),SName,LastIn,State,Reason1]).
+ [handler(Handler),SName,LastIn,FmtState,Reason1]).
handler(Handler) when not Handler#handler.id ->
Handler#handler.module;
@@ -712,10 +722,20 @@ get_modules(MSL) ->
%%-----------------------------------------------------------------
%% Status information
%%-----------------------------------------------------------------
-format_status(_Opt, StatusData) ->
- [_PDict, SysState, Parent, _Debug, [ServerName, MSL, _Hib]] = StatusData,
+format_status(Opt, StatusData) ->
+ [PDict, SysState, Parent, _Debug, [ServerName, MSL, _Hib]] = StatusData,
Header = lists:concat(["Status for event handler ", ServerName]),
+ FmtMSL = [case erlang:function_exported(Mod, format_status, 2) of
+ true ->
+ Args = [PDict, State],
+ case catch Mod:format_status(Opt, Args) of
+ {'EXIT', _} -> MSL;
+ Else -> MS#handler{state = Else}
+ end;
+ _ ->
+ MS
+ end || #handler{module = Mod, state = State} = MS <- MSL],
[{header, Header},
{data, [{"Status", SysState},
{"Parent", Parent}]},
- {items, {"Installed handlers", MSL}}].
+ {items, {"Installed handlers", FmtMSL}}].
diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl
index ba0275ae2b..7d9960b912 100644
--- a/lib/stdlib/src/gen_fsm.erl
+++ b/lib/stdlib/src/gen_fsm.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(gen_fsm).
@@ -116,7 +116,7 @@
-export([behaviour_info/1]).
%% Internal exports
--export([init_it/6, print_event/3,
+-export([init_it/6,
system_continue/3,
system_terminate/4,
system_code_change/4,
@@ -376,7 +376,7 @@ decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, Debug, Hib) ->
_Msg when Debug =:= [] ->
handle_msg(Msg, Parent, Name, StateName, StateData, Mod, Time);
_Msg ->
- Debug1 = sys:handle_debug(Debug, {?MODULE, print_event},
+ Debug1 = sys:handle_debug(Debug, fun print_event/3,
{Name, StateName}, {in, Msg}),
handle_msg(Msg, Parent, Name, StateName, StateData,
Mod, Time, Debug1)
@@ -466,11 +466,11 @@ handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, Debug) ->
From = from(Msg),
case catch dispatch(Msg, Mod, StateName, StateData) of
{next_state, NStateName, NStateData} ->
- Debug1 = sys:handle_debug(Debug, {?MODULE, print_event},
+ Debug1 = sys:handle_debug(Debug, fun print_event/3,
{Name, NStateName}, return),
loop(Parent, Name, NStateName, NStateData, Mod, infinity, Debug1);
{next_state, NStateName, NStateData, Time1} ->
- Debug1 = sys:handle_debug(Debug, {?MODULE, print_event},
+ Debug1 = sys:handle_debug(Debug, fun print_event/3,
{Name, NStateName}, return),
loop(Parent, Name, NStateName, NStateData, Mod, Time1, Debug1);
{reply, Reply, NStateName, NStateData} when From =/= undefined ->
@@ -519,7 +519,7 @@ reply({To, Tag}, Reply) ->
reply(Name, {To, Tag}, Reply, Debug, StateName) ->
reply({To, Tag}, Reply),
- sys:handle_debug(Debug, {?MODULE, print_event}, Name,
+ sys:handle_debug(Debug, fun print_event/3, Name,
{out, Reply, To, StateName}).
%%% ---------------------------------------------------
@@ -542,7 +542,18 @@ terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug) ->
{shutdown,_}=Shutdown ->
exit(Shutdown);
_ ->
- error_info(Reason, Name, Msg, StateName, StateData, Debug),
+ FmtStateData =
+ case erlang:function_exported(Mod, format_status, 2) of
+ true ->
+ Args = [get(), StateData],
+ case catch Mod:format_status(terminate, Args) of
+ {'EXIT', _} -> StateData;
+ Else -> Else
+ end;
+ _ ->
+ StateData
+ end,
+ error_info(Reason,Name,Msg,StateName,FmtStateData,Debug),
exit(Reason)
end
end.
@@ -603,22 +614,27 @@ get_msg(Msg) -> Msg.
format_status(Opt, StatusData) ->
[PDict, SysState, Parent, Debug, [Name, StateName, StateData, Mod, _Time]] =
StatusData,
- NameTag = if is_pid(Name) ->
- pid_to_list(Name);
- is_atom(Name) ->
- Name
- end,
- Header = lists:concat(["Status for state machine ", NameTag]),
+ StatusHdr = "Status for state machine",
+ Header = if
+ is_pid(Name) ->
+ lists:concat([StatusHdr, " ", pid_to_list(Name)]);
+ is_atom(Name); is_list(Name) ->
+ lists:concat([StatusHdr, " ", Name]);
+ true ->
+ {StatusHdr, Name}
+ end,
Log = sys:get_debug(log, Debug, []),
- Specfic =
+ DefaultStatus = [{data, [{"StateData", StateData}]}],
+ Specfic =
case erlang:function_exported(Mod, format_status, 2) of
true ->
case catch Mod:format_status(Opt,[PDict,StateData]) of
- {'EXIT', _} -> [{data, [{"StateData", StateData}]}];
- Else -> Else
+ {'EXIT', _} -> DefaultStatus;
+ StatusList when is_list(StatusList) -> StatusList;
+ Else -> [Else]
end;
_ ->
- [{data, [{"StateData", StateData}]}]
+ DefaultStatus
end,
[{header, Header},
{data, [{"Status", SysState},
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index f1a9a31c63..ac81df9cab 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(gen_server).
@@ -103,7 +103,7 @@
format_status/2]).
%% Internal exports
--export([init_it/6, print_event/3]).
+-export([init_it/6]).
-import(error_logger, [format/2]).
@@ -353,7 +353,7 @@ decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) ->
_Msg when Debug =:= [] ->
handle_msg(Msg, Parent, Name, State, Mod);
_Msg ->
- Debug1 = sys:handle_debug(Debug, {?MODULE, print_event},
+ Debug1 = sys:handle_debug(Debug, fun print_event/3,
Name, {in, Msg}),
handle_msg(Msg, Parent, Name, State, Mod, Debug1)
end.
@@ -589,11 +589,11 @@ handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod, Debug) ->
Debug1 = reply(Name, From, Reply, NState, Debug),
loop(Parent, Name, NState, Mod, Time1, Debug1);
{noreply, NState} ->
- Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
+ Debug1 = sys:handle_debug(Debug, fun print_event/3, Name,
{noreply, NState}),
loop(Parent, Name, NState, Mod, infinity, Debug1);
{noreply, NState, Time1} ->
- Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
+ Debug1 = sys:handle_debug(Debug, fun print_event/3, Name,
{noreply, NState}),
loop(Parent, Name, NState, Mod, Time1, Debug1);
{stop, Reason, Reply, NState} ->
@@ -625,11 +625,11 @@ handle_common_reply(Reply, Parent, Name, Msg, Mod, State) ->
handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Debug) ->
case Reply of
{noreply, NState} ->
- Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
+ Debug1 = sys:handle_debug(Debug, fun print_event/3, Name,
{noreply, NState}),
loop(Parent, Name, NState, Mod, infinity, Debug1);
{noreply, NState, Time1} ->
- Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
+ Debug1 = sys:handle_debug(Debug, fun print_event/3, Name,
{noreply, NState}),
loop(Parent, Name, NState, Mod, Time1, Debug1);
{stop, Reason, NState} ->
@@ -642,7 +642,7 @@ handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Debug) ->
reply(Name, {To, Tag}, Reply, State, Debug) ->
reply({To, Tag}, Reply),
- sys:handle_debug(Debug, {?MODULE, print_event}, Name,
+ sys:handle_debug(Debug, fun print_event/3, Name,
{out, Reply, To, State} ).
@@ -705,7 +705,18 @@ terminate(Reason, Name, Msg, Mod, State, Debug) ->
{shutdown,_}=Shutdown ->
exit(Shutdown);
_ ->
- error_info(Reason, Name, Msg, State, Debug),
+ FmtState =
+ case erlang:function_exported(Mod, format_status, 2) of
+ true ->
+ Args = [get(), State],
+ case catch Mod:format_status(terminate, Args) of
+ {'EXIT', _} -> State;
+ Else -> Else
+ end;
+ _ ->
+ State
+ end,
+ error_info(Reason, Name, Msg, FmtState, Debug),
exit(Reason)
end
end.
@@ -829,22 +840,27 @@ name_to_pid(Name) ->
%%-----------------------------------------------------------------
format_status(Opt, StatusData) ->
[PDict, SysState, Parent, Debug, [Name, State, Mod, _Time]] = StatusData,
- NameTag = if is_pid(Name) ->
- pid_to_list(Name);
- is_atom(Name) ->
- Name
- end,
- Header = lists:concat(["Status for generic server ", NameTag]),
+ StatusHdr = "Status for generic server",
+ Header = if
+ is_pid(Name) ->
+ lists:concat([StatusHdr, " ", pid_to_list(Name)]);
+ is_atom(Name); is_list(Name) ->
+ lists:concat([StatusHdr, " ", Name]);
+ true ->
+ {StatusHdr, Name}
+ end,
Log = sys:get_debug(log, Debug, []),
- Specfic =
+ DefaultStatus = [{data, [{"State", State}]}],
+ Specfic =
case erlang:function_exported(Mod, format_status, 2) of
true ->
case catch Mod:format_status(Opt, [PDict, State]) of
- {'EXIT', _} -> [{data, [{"State", State}]}];
- Else -> Else
+ {'EXIT', _} -> DefaultStatus;
+ StatusList when is_list(StatusList) -> StatusList;
+ Else -> [Else]
end;
_ ->
- [{data, [{"State", State}]}]
+ DefaultStatus
end,
[{header, Header},
{data, [{"Status", SysState},
diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl
index 1f8076e864..1d0f9374bc 100644
--- a/lib/stdlib/src/io.erl
+++ b/lib/stdlib/src/io.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(io).
@@ -32,6 +32,7 @@
parse_erl_form/1,parse_erl_form/2,parse_erl_form/3]).
-export([request/1,request/2,requests/1,requests/2]).
+-export_type([device/0, format/0]).
%%-------------------------------------------------------------------------
diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
index 26f6ec8931..4ca9d079b7 100644
--- a/lib/stdlib/src/io_lib.erl
+++ b/lib/stdlib/src/io_lib.erl
@@ -75,6 +75,8 @@
collect_line/2, collect_line/3, collect_line/4,
get_until/3, get_until/4]).
+-export_type([chars/0]).
+
%%----------------------------------------------------------------------
%% XXX: overapproximates a deep list of (unicode) characters
diff --git a/lib/stdlib/src/io_lib_fread.erl b/lib/stdlib/src/io_lib_fread.erl
index 74316dc730..33553692bc 100644
--- a/lib/stdlib/src/io_lib_fread.erl
+++ b/lib/stdlib/src/io_lib_fread.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(io_lib_fread).
@@ -22,6 +22,8 @@
-export([fread/2,fread/3]).
+-export_type([continuation/0, fread_2_ret/0, fread_3_ret/0]).
+
-import(lists, [reverse/1,reverse/2]).
%%-----------------------------------------------------------------------
diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl
index e1f8d1c200..c669c1f7c1 100644
--- a/lib/stdlib/src/lists.erl
+++ b/lib/stdlib/src/lists.erl
@@ -1,23 +1,26 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(lists).
+-compile({no_auto_import,[max/2]}).
+-compile({no_auto_import,[min/2]}).
+
-export([append/2, append/1, subtract/2, reverse/1,
nth/2, nthtail/2, prefix/2, suffix/2, last/1,
seq/2, seq/3, sum/1, duplicate/2, min/1, max/1, sublist/2, sublist/3,
@@ -25,7 +28,7 @@
unzip/1, unzip3/1, zip/2, zip3/3, zipwith/3, zipwith3/4,
sort/1, merge/1, merge/2, rmerge/2, merge3/3, rmerge3/3,
usort/1, umerge/1, umerge3/3, umerge/2, rumerge3/3, rumerge/2,
- concat/1, flatten/1, flatten/2, flat_length/1, flatlength/1,
+ concat/1, flatten/1, flatten/2, flatlength/1,
keydelete/3, keyreplace/4, keytake/3, keystore/4,
keysort/2, keymerge/3, rkeymerge/3, rukeymerge/3,
ukeysort/2, ukeymerge/3, keymap/3]).
@@ -40,8 +43,6 @@
mapfoldl/3,mapfoldr/3,foreach/2,takewhile/2,dropwhile/2,splitwith/2,
split/2]).
--deprecated([flat_length/1]).
-
%% member(X, L) -> (true | false)
%% test if X is a member of the list L
%% Now a BIF!
@@ -349,7 +350,7 @@ sort_1(X, [], R) ->
%% merge(List) -> L
%% merges a list of sorted lists
--spec merge([T]) -> [T].
+-spec merge([[T]]) -> [T].
merge(L) ->
mergel(L, []).
@@ -357,7 +358,7 @@ merge(L) ->
%% merge3(X, Y, Z) -> L
%% merges three sorted lists X, Y and Z
--spec merge3([_], [_], [_]) -> [_].
+-spec merge3([X], [Y], [Z]) -> [(X | Y | Z)].
merge3(L1, [], L3) ->
merge(L1, L3);
@@ -369,7 +370,7 @@ merge3(L1, [H2 | T2], [H3 | T3]) ->
%% rmerge3(X, Y, Z) -> L
%% merges three reversed sorted lists X, Y and Z
--spec rmerge3([_], [_], [_]) -> [_].
+-spec rmerge3([X], [Y], [Z]) -> [(X | Y | Z)].
rmerge3(L1, [], L3) ->
rmerge(L1, L3);
@@ -381,7 +382,7 @@ rmerge3(L1, [H2 | T2], [H3 | T3]) ->
%% merge(X, Y) -> L
%% merges two sorted lists X and Y
--spec merge([_], [_]) -> [_].
+-spec merge([X], [Y]) -> [(X | Y)].
merge(T1, []) ->
T1;
@@ -393,7 +394,7 @@ merge(T1, [H2 | T2]) ->
%% reverse(rmerge(reverse(A),reverse(B))) is equal to merge(I,A,B).
--spec rmerge([_], [_]) -> [_].
+-spec rmerge([X], [Y]) -> [(X | Y)].
rmerge(T1, []) ->
T1;
@@ -419,12 +420,12 @@ thing_to_list(X) when is_list(X) -> X. %Assumed to be a string
%% flatten(List, Tail)
%% Flatten a list, adding optional tail.
--spec flatten([_]) -> [_].
+-spec flatten([term()]) -> [term()].
flatten(List) when is_list(List) ->
do_flatten(List, []).
--spec flatten([_], [_]) -> [_].
+-spec flatten([term()], [term()]) -> [term()].
flatten(List, Tail) when is_list(List), is_list(Tail) ->
do_flatten(List, Tail).
@@ -436,17 +437,10 @@ do_flatten([H|T], Tail) ->
do_flatten([], Tail) ->
Tail.
-%% flat_length(List) (undocumented can be removed later)
-%% Calculate the length of a list of lists.
-
--spec flat_length([_]) -> non_neg_integer().
-
-flat_length(List) -> flatlength(List).
-
%% flatlength(List)
%% Calculate the length of a list of lists.
--spec flatlength([_]) -> non_neg_integer().
+-spec flatlength([term()]) -> non_neg_integer().
flatlength(List) ->
flatlength(List, 0).
@@ -487,7 +481,7 @@ flatlength([], L) -> L.
% keysearch3(Key, N, T);
%keysearch3(Key, N, []) -> false.
--spec keydelete(_, pos_integer(), [T]) -> [T].
+-spec keydelete(term(), pos_integer(), [T]) -> [T] when T :: tuple().
keydelete(K, N, L) when is_integer(N), N > 0 ->
keydelete3(K, N, L).
@@ -497,7 +491,7 @@ keydelete3(Key, N, [H|T]) ->
[H|keydelete3(Key, N, T)];
keydelete3(_, _, []) -> [].
--spec keyreplace(_, pos_integer(), [_], tuple()) -> [_].
+-spec keyreplace(term(), pos_integer(), [tuple()], tuple()) -> [tuple()].
keyreplace(K, N, L, New) when is_integer(N), N > 0, is_tuple(New) ->
keyreplace3(K, N, L, New).
@@ -508,7 +502,8 @@ keyreplace3(Key, Pos, [H|T], New) ->
[H|keyreplace3(Key, Pos, T, New)];
keyreplace3(_, _, [], _) -> [].
--spec keytake(_, pos_integer(), [_]) -> {'value', tuple(), [_]} | 'false'.
+-spec keytake(term(), pos_integer(), [tuple()]) ->
+ {'value', tuple(), [tuple()]} | 'false'.
keytake(Key, N, L) when is_integer(N), N > 0 ->
keytake(Key, N, L, []).
@@ -519,7 +514,8 @@ keytake(Key, N, [H|T], L) ->
keytake(Key, N, T, [H|L]);
keytake(_K, _N, [], _L) -> false.
--spec keystore(_, pos_integer(), [_], tuple()) -> [_].
+-spec keystore(term(), pos_integer(), [tuple()], tuple()) -> [tuple(),...].
+
keystore(K, N, L, New) when is_integer(N), N > 0, is_tuple(New) ->
keystore2(K, N, L, New).
@@ -530,7 +526,7 @@ keystore2(Key, N, [H|T], New) ->
keystore2(_Key, _N, [], New) ->
[New].
--spec keysort(pos_integer(), [T]) -> [T] when is_subtype(T, tuple()).
+-spec keysort(pos_integer(), [T]) -> [T] when T :: tuple().
keysort(I, L) when is_integer(I), I > 0 ->
case L of
@@ -588,7 +584,7 @@ keysort_1(_I, X, _EX, [], R) ->
lists:reverse(R, [X]).
-spec keymerge(pos_integer(), [X], [Y]) ->
- [R] when is_subtype(X, tuple()), is_subtype(Y, tuple()), is_subtype(R, tuple()).
+ [R] when X :: tuple(), Y :: tuple(), R :: tuple().
keymerge(Index, T1, L2) when is_integer(Index), Index > 0 ->
case L2 of
@@ -603,7 +599,7 @@ keymerge(Index, T1, L2) when is_integer(Index), Index > 0 ->
%% reverse(rkeymerge(I,reverse(A),reverse(B))) is equal to keymerge(I,A,B).
-spec rkeymerge(pos_integer(), [X], [Y]) ->
- [R] when is_subtype(X, tuple()), is_subtype(Y, tuple()), is_subtype(R, tuple()).
+ [R] when X :: tuple(), Y :: tuple(), R :: tuple().
rkeymerge(Index, T1, L2) when is_integer(Index), Index > 0 ->
case L2 of
@@ -615,7 +611,7 @@ rkeymerge(Index, T1, L2) when is_integer(Index), Index > 0 ->
lists:reverse(M, [])
end.
--spec ukeysort(pos_integer(), [T]) -> [T] when is_subtype(T, tuple()).
+-spec ukeysort(pos_integer(), [T]) -> [T] when T :: tuple().
ukeysort(I, L) when is_integer(I), I > 0 ->
case L of
@@ -681,7 +677,7 @@ ukeysort_1(_I, X, _EX, []) ->
[X].
-spec ukeymerge(pos_integer(), [X], [Y]) ->
- [(X | Y)] when is_subtype(X, tuple()), is_subtype(Y, tuple()).
+ [(X | Y)] when X :: tuple(), Y :: tuple().
ukeymerge(Index, L1, T2) when is_integer(Index), Index > 0 ->
case L1 of
@@ -696,7 +692,7 @@ ukeymerge(Index, L1, T2) when is_integer(Index), Index > 0 ->
%% reverse(rukeymerge(I,reverse(A),reverse(B))) is equal to ukeymerge(I,A,B).
-spec rukeymerge(pos_integer(), [X], [Y]) ->
- [(X | Y)] when is_subtype(X, tuple()), is_subtype(Y, tuple()).
+ [(X | Y)] when X :: tuple(), Y :: tuple().
rukeymerge(Index, T1, L2) when is_integer(Index), Index > 0 ->
case L2 of
@@ -708,7 +704,7 @@ rukeymerge(Index, T1, L2) when is_integer(Index), Index > 0 ->
lists:reverse(M, [])
end.
--spec keymap(fun((_) -> _), pos_integer(), [tuple()]) -> [tuple()].
+-spec keymap(fun((term()) -> term()), pos_integer(), [tuple()]) -> [tuple()].
keymap(Fun, Index, [Tup|Tail]) ->
[setelement(Index, Tup, Fun(element(Index, Tup)))|keymap(Fun, Index, Tail)];
@@ -731,7 +727,7 @@ sort(Fun, [X, Y | T]) ->
fsplit_2(Y, X, Fun, T, [], [])
end.
--spec merge(fun((X, Y) -> boolean()), [X], [Y]) -> [_].
+-spec merge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)].
merge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) ->
lists:reverse(fmerge2_1(T1, H2, Fun, T2, []), []);
@@ -740,7 +736,7 @@ merge(Fun, T1, []) when is_function(Fun, 2) ->
%% reverse(rmerge(F,reverse(A),reverse(B))) is equal to merge(F,A,B).
--spec rmerge(fun((X, Y) -> boolean()), [X], [Y]) -> [_].
+-spec rmerge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)].
rmerge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) ->
lists:reverse(rfmerge2_1(T1, H2, Fun, T2, []), []);
@@ -774,7 +770,7 @@ usort_1(Fun, X, [Y | L]) ->
ufsplit_2(Y, L, Fun, [X])
end.
--spec umerge(fun((X, Y) -> boolean()), [X], [Y]) -> [_].
+-spec umerge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)].
umerge(Fun, [], T2) when is_function(Fun, 2) ->
T2;
@@ -783,7 +779,7 @@ umerge(Fun, [H1 | T1], T2) when is_function(Fun, 2) ->
%% reverse(rumerge(F,reverse(A),reverse(B))) is equal to umerge(F,A,B).
--spec rumerge(fun((X, Y) -> boolean()), [X], [Y]) -> [_].
+-spec rumerge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)].
rumerge(Fun, T1, []) when is_function(Fun, 2) ->
T1;
@@ -848,7 +844,7 @@ usort_1(X, []) ->
%% umerge(List) -> L
%% merges a list of sorted lists without duplicates, removes duplicates
--spec umerge([T]) -> [T].
+-spec umerge([[T]]) -> [T].
umerge(L) ->
umergel(L).
@@ -857,7 +853,7 @@ umerge(L) ->
%% merges three sorted lists X, Y and Z without duplicates,
%% removes duplicates
--spec umerge3([_], [_], [_]) -> [_].
+-spec umerge3([X], [Y], [Z]) -> [(X | Y | Z)].
umerge3(L1, [], L3) ->
umerge(L1, L3);
@@ -870,7 +866,7 @@ umerge3(L1, [H2 | T2], [H3 | T3]) ->
%% merges three reversed sorted lists X, Y and Z without duplicates,
%% removes duplicates
--spec rumerge3([_], [_], [_]) -> [_].
+-spec rumerge3([X], [Y], [Z]) -> [(X | Y | Z)].
rumerge3(L1, [], L3) ->
rumerge(L1, L3);
@@ -882,7 +878,7 @@ rumerge3(L1, [H2 | T2], [H3 | T3]) ->
%% umerge(X, Y) -> L
%% merges two sorted lists X and Y without duplicates, removes duplicates
--spec umerge([_], [_]) -> [_].
+-spec umerge([X], [Y]) -> [(X | Y)].
umerge([], T2) ->
T2;
@@ -895,7 +891,7 @@ umerge([H1 | T1], T2) ->
%% reverse(rumerge(reverse(A),reverse(B))) is equal to umerge(I,A,B).
--spec rumerge([_], [_]) -> [_].
+-spec rumerge([X], [Y]) -> [(X | Y)].
rumerge(T1, []) ->
T1;
@@ -958,13 +954,13 @@ flatmap(F, [Hd|Tail]) ->
F(Hd) ++ flatmap(F, Tail);
flatmap(F, []) when is_function(F, 1) -> [].
--spec foldl(fun((T, _) -> _), _, [T]) -> _.
+-spec foldl(fun((T, term()) -> term()), term(), [T]) -> term().
foldl(F, Accu, [Hd|Tail]) ->
foldl(F, F(Hd, Accu), Tail);
foldl(F, Accu, []) when is_function(F, 2) -> Accu.
--spec foldr(fun((T, _) -> _), _, [T]) -> _.
+-spec foldr(fun((T, term()) -> term()), term(), [T]) -> term().
foldr(F, Accu, [Hd|Tail]) ->
F(Hd, foldr(F, Accu, Tail));
@@ -1004,14 +1000,14 @@ zf(F, [Hd|Tail]) ->
end;
zf(F, []) when is_function(F, 1) -> [].
--spec foreach(F :: fun((T) -> _), List :: [T]) -> 'ok'.
+-spec foreach(F :: fun((T) -> term()), List :: [T]) -> 'ok'.
foreach(F, [Hd|Tail]) ->
F(Hd),
foreach(F, Tail);
foreach(F, []) when is_function(F, 1) -> ok.
--spec mapfoldl(fun((T, _) -> {_, _}), _, [T]) -> {[_], _}.
+-spec mapfoldl(fun((A, term()) -> {B, term()}), term(), [A]) -> {[B], term()}.
mapfoldl(F, Accu0, [Hd|Tail]) ->
{R,Accu1} = F(Hd, Accu0),
@@ -1019,7 +1015,7 @@ mapfoldl(F, Accu0, [Hd|Tail]) ->
{[R|Rs],Accu2};
mapfoldl(F, Accu, []) when is_function(F, 2) -> {[],Accu}.
--spec mapfoldr(fun((T, _) -> {_, _}), _, [T]) -> {[_], _}.
+-spec mapfoldr(fun((A, term()) -> {B, term()}), term(), [A]) -> {[B], term()}.
mapfoldr(F, Accu0, [Hd|Tail]) ->
{Rs,Accu1} = mapfoldr(F, Accu0, Tail),
diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl
index 78b1de6e16..a249dea525 100644
--- a/lib/stdlib/src/ms_transform.erl
+++ b/lib/stdlib/src/ms_transform.erl
@@ -43,6 +43,7 @@
-define(ERR_GENREMOTECALL,22).
-define(ERR_GENBINCONSTRUCT,23).
-define(ERR_GENDISALLOWEDOP,24).
+-define(WARN_SHADOW_VAR,50).
-define(ERR_GUARDMATCH,?ERR_GENMATCH+?ERROR_BASE_GUARD).
-define(ERR_BODYMATCH,?ERR_GENMATCH+?ERROR_BASE_BODY).
-define(ERR_GUARDLOCALCALL,?ERR_GENLOCALCALL+?ERROR_BASE_GUARD).
@@ -63,8 +64,13 @@
-define(ERR_BODYDISALLOWEDOP,?ERR_GENDISALLOWEDOP+?ERROR_BASE_BODY).
%%
-%% Called by compiler or ets/dbg:fun2ms when errors occur
+%% Called by compiler or ets/dbg:fun2ms when errors/warnings occur
%%
+format_error({?WARN_SHADOW_VAR,Name}) ->
+ lists:flatten(
+ io_lib:format("variable ~p shadowed in ms_transform fun head",
+ [Name]));
+
format_error(?ERR_NOFUN) ->
"Parameter of ets/dbg:fun2ms/1 is not a literal fun";
format_error(?ERR_ETS_HEAD) ->
@@ -182,7 +188,7 @@ format_error(Else) ->
%%
transform_from_shell(Dialect, Clauses, BoundEnvironment) ->
SaveFilename = setup_filename(),
- case catch ms_clause_list(1,Clauses,Dialect) of
+ case catch ms_clause_list(1,Clauses,Dialect,gb_sets:new()) of
{'EXIT',Reason} ->
cleanup_filename(SaveFilename),
exit(Reason);
@@ -207,6 +213,7 @@ transform_from_shell(Dialect, Clauses, BoundEnvironment) ->
%%
parse_transform(Forms, _Options) ->
SaveFilename = setup_filename(),
+ %io:format("Forms: ~p~n",[Forms]),
case catch forms(Forms) of
{'EXIT',Reason} ->
cleanup_filename(SaveFilename),
@@ -215,12 +222,31 @@ parse_transform(Forms, _Options) ->
{error, [{cleanup_filename(SaveFilename),
[{Line, ?MODULE, R}]}], []};
Else ->
- cleanup_filename(SaveFilename),
+ %io:format("Transformed into: ~p~n",[Else]),
+ case get_warnings() of
+ [] ->
+ cleanup_filename(SaveFilename),
+ Else;
+ WL ->
+ FName = cleanup_filename(SaveFilename) ,
+ WList = [ {FName, [{L, ?MODULE, R}]} || {L,R} <- WL ],
+ {warning, Else, WList}
+ end
+ end.
+
+get_warnings() ->
+ case get(warnings) of
+ undefined ->
+ [];
+ Else ->
Else
end.
+add_warning(Line,R) ->
+ put(warnings,[{Line,R}| get_warnings()]).
+
setup_filename() ->
- {erase(filename),erase(records)}.
+ {erase(filename),erase(records),erase(warnings)}.
put_filename(Name) ->
put(filename,Name).
@@ -235,7 +261,7 @@ get_records() ->
Else ->
Else
end.
-cleanup_filename({Old,OldRec}) ->
+cleanup_filename({Old,OldRec,OldWarnings}) ->
Ret = case erase(filename) of
undefined ->
"TOP_LEVEL";
@@ -248,6 +274,12 @@ cleanup_filename({Old,OldRec}) ->
Rec ->
put(records,Rec)
end,
+ case OldWarnings of
+ undefined ->
+ erase(warnings);
+ Warn ->
+ put(warnings,Warn)
+ end,
case Old of
undefined ->
Ret;
@@ -285,42 +317,77 @@ form({function,Line,Name0,Arity0,Clauses0}) ->
form(AnyOther) ->
AnyOther.
function(Name, Arity, Clauses0) ->
- Clauses1 = clauses(Clauses0),
+ {Clauses1,_} = clauses(Clauses0,gb_sets:new()),
{Name,Arity,Clauses1}.
-clauses([C0|Cs]) ->
- C1 = clause(C0),
- [C1|clauses(Cs)];
-clauses([]) -> [].
-clause({clause,Line,H0,G0,B0}) ->
- B1 = copy(B0),
- {clause,Line,H0,G0,B1}.
+clauses([C0|Cs],Bound) ->
+ {C1,Bound1} = clause(C0,Bound),
+ {C2,Bound2} = clauses(Cs,Bound1),
+ {[C1|C2],Bound2};
+clauses([],Bound) -> {[],Bound}.
+clause({clause,Line,H0,G0,B0},Bound) ->
+ {H1,Bound1} = copy(H0,Bound),
+ {B1,Bound2} = copy(B0,Bound1),
+ {{clause,Line,H1,G0,B1},Bound2}.
copy({call,Line,{remote,_Line2,{atom,_Line3,ets},{atom,_Line4,fun2ms}},
- As0}) ->
- transform_call(ets,Line,As0);
+ As0},Bound) ->
+ {transform_call(ets,Line,As0,Bound),Bound};
copy({call,Line,{remote,_Line2,{record_field,_Line3,
{atom,_Line4,''},{atom,_Line5,ets}},
- {atom,_Line6,fun2ms}}, As0}) ->
+ {atom,_Line6,fun2ms}}, As0},Bound) ->
%% Packages...
- transform_call(ets,Line,As0);
+ {transform_call(ets,Line,As0,Bound),Bound};
copy({call,Line,{remote,_Line2,{atom,_Line3,dbg},{atom,_Line4,fun2ms}},
- As0}) ->
- transform_call(dbg,Line,As0);
-copy(T) when is_tuple(T) ->
- list_to_tuple(copy_list(tuple_to_list(T)));
-copy(L) when is_list(L) ->
- copy_list(L);
-copy(AnyOther) ->
- AnyOther.
+ As0},Bound) ->
+ {transform_call(dbg,Line,As0,Bound),Bound};
+copy({match,Line,A,B},Bound) ->
+ {B1,Bound1} = copy(B,Bound),
+ {A1,Bound2} = copy(A,Bound),
+ {{match,Line,A1,B1},gb_sets:union(Bound1,Bound2)};
+copy({var,_Line,'_'} = VarDef,Bound) ->
+ {VarDef,Bound};
+copy({var,_Line,Name} = VarDef,Bound) ->
+ Bound1 = gb_sets:add(Name,Bound),
+ {VarDef,Bound1};
+copy({'fun',Line,{clauses,Clauses}},Bound) -> % Dont export bindings from funs
+ {NewClauses,_IgnoredBindings} = copy_list(Clauses,Bound),
+ {{'fun',Line,{clauses,NewClauses}},Bound};
+copy({'case',Line,Of,ClausesList},Bound) -> % Dont export bindings from funs
+ {NewOf,NewBind0} = copy(Of,Bound),
+ {NewClausesList,NewBindings} = copy_case_clauses(ClausesList,NewBind0,[]),
+ {{'case',Line,NewOf,NewClausesList},NewBindings};
+copy(T,Bound) when is_tuple(T) ->
+ {L,Bound1} = copy_list(tuple_to_list(T),Bound),
+ {list_to_tuple(L),Bound1};
+copy(L,Bound) when is_list(L) ->
+ copy_list(L,Bound);
+copy(AnyOther,Bound) ->
+ {AnyOther,Bound}.
-copy_list([H|T]) ->
- [copy(H)|copy_list(T)];
-copy_list([]) ->
- [].
+copy_case_clauses([],Bound,AddSets) ->
+ ReallyAdded = gb_sets:intersection(AddSets),
+ {[],gb_sets:union(Bound,ReallyAdded)};
+copy_case_clauses([{clause,Line,Match,Guard,Clauses}|T],Bound,AddSets) ->
+ {NewMatch,MatchBinds} = copy(Match,Bound),
+ {NewGuard,GuardBinds} = copy(Guard,MatchBinds), %% Really no new binds
+ {NewClauses,AllBinds} = copy(Clauses,GuardBinds),
+ %% To limit the setsizes, I subtract what I had before the case clause
+ %% and add it in the end
+ AddedBinds = gb_sets:subtract(AllBinds,Bound),
+ {NewTail,ExportedBindings} =
+ copy_case_clauses(T,Bound,[AddedBinds | AddSets]),
+ {[{clause,Line,NewMatch,NewGuard,NewClauses}|NewTail],ExportedBindings}.
-transform_call(Type,_Line,[{'fun',Line2,{clauses, ClauseList}}]) ->
- ms_clause_list(Line2, ClauseList,Type);
-transform_call(_Type,Line,_NoAbstractFun) ->
+copy_list([H|T],Bound) ->
+ {C1,Bound1} = copy(H,Bound),
+ {C2,Bound2} = copy_list(T,Bound1),
+ {[C1|C2],Bound2};
+copy_list([],Bound) ->
+ {[],Bound}.
+
+transform_call(Type,_Line,[{'fun',Line2,{clauses, ClauseList}}],Bound) ->
+ ms_clause_list(Line2, ClauseList,Type,Bound);
+transform_call(_Type,Line,_NoAbstractFun,_) ->
throw({error,Line,?ERR_NOFUN}).
% Fixup semicolons in guards
@@ -329,18 +396,19 @@ ms_clause_expand({clause, Line, Parameters, Guard = [_,_|_], Body}) ->
ms_clause_expand(_Other) ->
false.
-ms_clause_list(Line,[H|T],Type) ->
+ms_clause_list(Line,[H|T],Type,Bound) ->
case ms_clause_expand(H) of
NewHead when is_list(NewHead) ->
- ms_clause_list(Line,NewHead ++ T, Type);
+ ms_clause_list(Line,NewHead ++ T, Type, Bound);
false ->
- {cons, Line, ms_clause(H,Type), ms_clause_list(Line, T,Type)}
+ {cons, Line, ms_clause(H, Type, Bound),
+ ms_clause_list(Line, T, Type, Bound)}
end;
-ms_clause_list(Line,[],_) ->
+ms_clause_list(Line,[],_,_) ->
{nil,Line}.
-ms_clause({clause, Line, Parameters, Guards, Body},Type) ->
+ms_clause({clause, Line, Parameters, Guards, Body},Type,Bound) ->
check_type(Line,Parameters,Type),
- {MSHead,Bindings} = transform_head(Parameters),
+ {MSHead,Bindings} = transform_head(Parameters,Bound),
MSGuards = transform_guards(Line, Guards, Bindings),
MSBody = transform_body(Line,Body,Bindings),
{tuple, Line, [MSHead,MSGuards,MSBody]}.
@@ -627,29 +695,31 @@ tg(Other,B) ->
Element = io_lib:format("unknown element ~w", [Other]),
throw({error,unknown,{?ERR_GENELEMENT+B#tgd.eb,Element}}).
-transform_head([V]) ->
+transform_head([V],OuterBound) ->
Bind = cre_bind(),
- {NewV,NewBind} = toplevel_head_match(V,Bind),
- th(NewV,NewBind).
+ {NewV,NewBind} = toplevel_head_match(V,Bind,OuterBound),
+ th(NewV,NewBind,OuterBound).
-toplevel_head_match({match,_,{var,_,VName},Expr},B) ->
+toplevel_head_match({match,Line,{var,_,VName},Expr},B,OB) ->
+ warn_var_clash(Line,VName,OB),
{Expr,new_bind({VName,'$_'},B)};
-toplevel_head_match({match,_,Expr,{var,_,VName}},B) ->
+toplevel_head_match({match,Line,Expr,{var,_,VName}},B,OB) ->
+ warn_var_clash(Line,VName,OB),
{Expr,new_bind({VName,'$_'},B)};
-toplevel_head_match(Other,B) ->
+toplevel_head_match(Other,B,_OB) ->
{Other,B}.
-th({record,Line,RName,RFields},B) ->
+th({record,Line,RName,RFields},B,OB) ->
% youch...
RDefs = get_records(),
{KeyList0,NewB} = lists:foldl(fun({record_field,_,{atom,_,Key},Value},
{L,B0}) ->
- {NV,B1} = th(Value,B0),
+ {NV,B1} = th(Value,B0,OB),
{[{Key,NV}|L],B1};
({record_field,_,{var,_,'_'},Value},
{L,B0}) ->
- {NV,B1} = th(Value,B0),
+ {NV,B1} = th(Value,B0,OB),
{[{{default},NV}|L],B1};
(_,_) ->
throw({error,Line,{?ERR_HEADBADREC,
@@ -692,9 +762,9 @@ th({record,Line,RName,RFields},B) ->
_ ->
throw({error,Line,{?ERR_HEADBADREC,RName}})
end;
-th({match,Line,_,_},_) ->
+th({match,Line,_,_},_,_) ->
throw({error,Line,?ERR_HEADMATCH});
-th({atom,Line,A},B) ->
+th({atom,Line,A},B,_OB) ->
case atom_to_list(A) of
[$$|NL] ->
case (catch list_to_integer(NL)) of
@@ -706,10 +776,11 @@ th({atom,Line,A},B) ->
_ ->
{{atom,Line,A},B}
end;
-th({bin_element,_Line0,{var, Line, A},_,_},_) ->
+th({bin_element,_Line0,{var, Line, A},_,_},_,_) ->
throw({error,Line,{?ERR_HEADBINMATCH,A}});
-th({var,Line,Name},B) ->
+th({var,Line,Name},B,OB) ->
+ warn_var_clash(Line,Name,OB),
case lkup_bind(Name,B) of
undefined ->
NewB = new_bind(Name,B),
@@ -717,16 +788,24 @@ th({var,Line,Name},B) ->
Trans ->
{{atom,Line,Trans},B}
end;
-th([H|T],B) ->
- {NH,NB} = th(H,B),
- {NT,NNB} = th(T,NB),
+th([H|T],B,OB) ->
+ {NH,NB} = th(H,B,OB),
+ {NT,NNB} = th(T,NB,OB),
{[NH|NT],NNB};
-th(T,B) when is_tuple(T) ->
- {L,NB} = th(tuple_to_list(T),B),
+th(T,B,OB) when is_tuple(T) ->
+ {L,NB} = th(tuple_to_list(T),B,OB),
{list_to_tuple(L),NB};
-th(Nonstruct,B) ->
+th(Nonstruct,B,_OB) ->
{Nonstruct,B}.
+warn_var_clash(Line,Name,OuterBound) ->
+ case gb_sets:is_member(Name,OuterBound) of
+ true ->
+ add_warning(Line,{?WARN_SHADOW_VAR,Name});
+ _ ->
+ ok
+ end.
+
%% Could be more efficient...
check_multi_field(_, _, [], _) ->
ok;
diff --git a/lib/stdlib/src/orddict.erl b/lib/stdlib/src/orddict.erl
index c7b52b933e..8a13992785 100644
--- a/lib/stdlib/src/orddict.erl
+++ b/lib/stdlib/src/orddict.erl
@@ -151,7 +151,7 @@ map(F, [{Key,Val}|D]) ->
[{Key,F(Key, Val)}|map(F, D)];
map(F, []) when is_function(F, 2) -> [].
--spec filter(fun((term(), term()) -> term()), orddict()) -> orddict().
+-spec filter(fun((term(), term()) -> boolean()), orddict()) -> orddict().
filter(F, [{Key,Val}=E|D]) ->
case F(Key, Val) of
diff --git a/lib/stdlib/src/ordsets.erl b/lib/stdlib/src/ordsets.erl
index 05041c15f1..4c72e351d0 100644
--- a/lib/stdlib/src/ordsets.erl
+++ b/lib/stdlib/src/ordsets.erl
@@ -26,12 +26,14 @@
-export([subtract/2,is_subset/2]).
-export([fold/3,filter/2]).
+-export_type([ordset/1]).
+
-type ordset(T) :: [T].
%% new() -> Set.
%% Return a new empty ordered set.
--spec new() -> ordset(term()).
+-spec new() -> [].
new() -> [].
@@ -84,7 +86,7 @@ is_element(_, []) -> false.
%% add_element(Element, OrdSet) -> OrdSet.
%% Return OrdSet with Element inserted in it.
--spec add_element(term(), ordset(_)) -> ordset(_).
+-spec add_element(E, ordset(T)) -> [T | E,...].
add_element(E, [H|Es]) when E > H -> [H|add_element(E, Es)];
add_element(E, [H|_]=Set) when E < H -> [E|Set];
@@ -94,7 +96,7 @@ add_element(E, []) -> [E].
%% del_element(Element, OrdSet) -> OrdSet.
%% Return OrdSet but with Element removed.
--spec del_element(term(), ordset(_)) -> ordset(_).
+-spec del_element(term(), ordset(T)) -> ordset(T).
del_element(E, [H|Es]) when E > H -> [H|del_element(E, Es)];
del_element(E, [H|_]=Set) when E < H -> Set;
@@ -104,7 +106,7 @@ del_element(_, []) -> [].
%% union(OrdSet1, OrdSet2) -> OrdSet
%% Return the union of OrdSet1 and OrdSet2.
--spec union(ordset(_), ordset(_)) -> ordset(_).
+-spec union(ordset(T1), ordset(T2)) -> ordset(T1 | T2).
union([E1|Es1], [E2|_]=Set2) when E1 < E2 ->
[E1|union(Es1, Set2)];
@@ -118,7 +120,7 @@ union(Es1, []) -> Es1.
%% union([OrdSet]) -> OrdSet
%% Return the union of the list of ordered sets.
--spec union([ordset(_)]) -> ordset(_).
+-spec union([ordset(T)]) -> ordset(T).
union([S1,S2|Ss]) ->
union1(union(S1, S2), Ss);
@@ -147,7 +149,7 @@ intersection(_, []) ->
%% intersection([OrdSet]) -> OrdSet.
%% Return the intersection of the list of ordered sets.
--spec intersection([ordset(_)]) -> ordset(_).
+-spec intersection([ordset(_),...]) -> ordset(_).
intersection([S1,S2|Ss]) ->
intersection1(intersection(S1, S2), Ss);
@@ -206,7 +208,7 @@ is_subset(_, []) -> false.
%% fold(Fun, Accumulator, OrdSet) -> Accumulator.
%% Fold function Fun over all elements in OrdSet and return Accumulator.
--spec fold(fun((_, _) -> _), _, ordset(_)) -> _.
+-spec fold(fun((T, term()) -> term()), term(), ordset(T)) -> term().
fold(F, Acc, Set) ->
lists:foldl(F, Acc, Set).
@@ -214,7 +216,7 @@ fold(F, Acc, Set) ->
%% filter(Fun, OrdSet) -> OrdSet.
%% Filter OrdSet with Fun.
--spec filter(fun((_) -> boolean()), ordset(_)) -> ordset(_).
+-spec filter(fun((T) -> boolean()), ordset(T)) -> ordset(T).
filter(F, Set) ->
lists:filter(F, Set).
diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl
index 7ea7de8d58..5c52dfcbf0 100644
--- a/lib/stdlib/src/otp_internal.erl
+++ b/lib/stdlib/src/otp_internal.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(otp_internal).
@@ -236,109 +236,137 @@ obsolete_1(erlang, fault, 2) ->
obsolete_1(file, rawopen, 2) ->
{removed, "deprecated (will be removed in R13B); use file:open/2 with the raw option"};
-obsolete_1(httpd, start, 0) -> {deprecated,{inets,start,[2,3]},"R14B"};
-obsolete_1(httpd, start, 1) -> {deprecated,{inets,start,[2,3]},"R14B"};
-obsolete_1(httpd, start_link, 1) -> {deprecated,{inets,start,[2,3]},"R14B"};
-obsolete_1(httpd, start_child, 0) -> {deprecated,{inets,start,[2,3]},"R14B"};
-obsolete_1(httpd, start_child, 1) -> {deprecated,{inets,start,[2,3]},"R14B"};
-obsolete_1(httpd, stop, 0) -> {deprecated,{inets,stop,2},"R14B"};
-obsolete_1(httpd, stop, 1) -> {deprecated,{inets,stop,2},"R14B"};
-obsolete_1(httpd, stop, 2) -> {deprecated,{inets,stop,2},"R14B"};
-obsolete_1(httpd, stop_child, 0) -> {deprecated,{inets,stop,2},"R14B"};
-obsolete_1(httpd, stop_child, 1) -> {deprecated,{inets,stop,2},"R14B"};
-obsolete_1(httpd, stop_child, 2) -> {deprecated,{inets,stop,2},"R14B"};
-obsolete_1(httpd, restart, 0) -> {deprecated,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, restart, 1) -> {deprecated,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, restart, 2) -> {deprecated,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, block, 0) -> {deprecated,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, block, 1) -> {deprecated,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, block, 2) -> {deprecated,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, block, 3) -> {deprecated,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, block, 4) -> {deprecated,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, unblock, 0) -> {deprecated,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, unblock, 1) -> {deprecated,{httpd,reload_config,2},"R14B"};
-obsolete_1(httpd, unblock, 2) -> {deprecated,{httpd,reload_config,2},"R14B"};
+obsolete_1(http, request, 1) -> {deprecated,{httpc,request,1},"R15B"};
+obsolete_1(http, request, 2) -> {deprecated,{httpc,request,2},"R15B"};
+obsolete_1(http, request, 4) -> {deprecated,{httpc,request,4},"R15B"};
+obsolete_1(http, request, 5) -> {deprecated,{httpc,request,5},"R15B"};
+obsolete_1(http, cancel_request, 1) -> {deprecated,{httpc,cancel_request,1},"R15B"};
+obsolete_1(http, cancel_request, 2) -> {deprecated,{httpc,cancel_request,2},"R15B"};
+obsolete_1(http, set_option, 2) -> {deprecated,{httpc,set_option,2},"R15B"};
+obsolete_1(http, set_option, 3) -> {deprecated,{httpc,set_option,3},"R15B"};
+obsolete_1(http, set_options, 1) -> {deprecated,{httpc,set_options,1},"R15B"};
+obsolete_1(http, set_options, 2) -> {deprecated,{httpc,set_options,2},"R15B"};
+obsolete_1(http, verify_cookies, 2) -> {deprecated,{httpc,verify_cookies,2},"R15B"};
+obsolete_1(http, verify_cookies, 3) -> {deprecated,{httpc,verify_cookies,3},"R15B"};
+obsolete_1(http, cookie_header, 1) -> {deprecated,{httpc,cookie_header,1},"R15B"};
+obsolete_1(http, cookie_header, 2) -> {deprecated,{httpc,cookie_header,2},"R15B"};
+obsolete_1(http, stream_next, 1) -> {deprecated,{httpc,stream_next,1},"R15B"};
+obsolete_1(http, default_profile, 0) -> {deprecated,{httpc,default_profile,0},"R15B"};
+
+obsolete_1(httpd, start, 0) -> {removed,{inets,start,[2,3]},"R14B"};
+obsolete_1(httpd, start, 1) -> {removed,{inets,start,[2,3]},"R14B"};
+obsolete_1(httpd, start_link, 0) -> {removed,{inets,start,[2,3]},"R14B"};
+obsolete_1(httpd, start_link, 1) -> {removed,{inets,start,[2,3]},"R14B"};
+obsolete_1(httpd, start_child, 0) -> {removed,{inets,start,[2,3]},"R14B"};
+obsolete_1(httpd, start_child, 1) -> {removed,{inets,start,[2,3]},"R14B"};
+obsolete_1(httpd, stop, 0) -> {removed,{inets,stop,2},"R14B"};
+obsolete_1(httpd, stop, 1) -> {removed,{inets,stop,2},"R14B"};
+obsolete_1(httpd, stop, 2) -> {removed,{inets,stop,2},"R14B"};
+obsolete_1(httpd, stop_child, 0) -> {removed,{inets,stop,2},"R14B"};
+obsolete_1(httpd, stop_child, 1) -> {removed,{inets,stop,2},"R14B"};
+obsolete_1(httpd, stop_child, 2) -> {removed,{inets,stop,2},"R14B"};
+obsolete_1(httpd, restart, 0) -> {removed,{httpd,reload_config,2},"R14B"};
+obsolete_1(httpd, restart, 1) -> {removed,{httpd,reload_config,2},"R14B"};
+obsolete_1(httpd, restart, 2) -> {removed,{httpd,reload_config,2},"R14B"};
+obsolete_1(httpd, block, 0) -> {removed,{httpd,reload_config,2},"R14B"};
+obsolete_1(httpd, block, 1) -> {removed,{httpd,reload_config,2},"R14B"};
+obsolete_1(httpd, block, 2) -> {removed,{httpd,reload_config,2},"R14B"};
+obsolete_1(httpd, block, 3) -> {removed,{httpd,reload_config,2},"R14B"};
+obsolete_1(httpd, block, 4) -> {removed,{httpd,reload_config,2},"R14B"};
+obsolete_1(httpd, unblock, 0) -> {removed,{httpd,reload_config,2},"R14B"};
+obsolete_1(httpd, unblock, 1) -> {removed,{httpd,reload_config,2},"R14B"};
+obsolete_1(httpd, unblock, 2) -> {removed,{httpd,reload_config,2},"R14B"};
obsolete_1(httpd_util, key1search, 2) -> {removed,{proplists,get_value,2},"R13B"};
obsolete_1(httpd_util, key1search, 3) -> {removed,{proplists,get_value,3},"R13B"};
-obsolete_1(ftp, open, 3) -> {deprecated,{inets,start,[2,3]},"R14B"};
-obsolete_1(ftp, force_active, 1) -> {deprecated,{inets,start,[2,3]},"R14B"};
+obsolete_1(ftp, open, 3) -> {removed,{inets,start,[2,3]},"R14B"};
+obsolete_1(ftp, force_active, 1) -> {removed,{inets,start,[2,3]},"R14B"};
%% Added in R12B-4.
obsolete_1(ssh_cm, connect, A) when 1 =< A, A =< 3 ->
- {deprecated,{ssh,connect,A},"R14B"};
+ {removed,{ssh,connect,A},"R14B"};
obsolete_1(ssh_cm, listen, A) when 2 =< A, A =< 4 ->
- {deprecated,{ssh,daemon,A},"R14B"};
+ {removed,{ssh,daemon,A},"R14B"};
obsolete_1(ssh_cm, stop_listener, 1) ->
- {deprecated,{ssh,stop_listener,[1,2]},"R14B"};
+ {removed,{ssh,stop_listener,[1,2]},"R14B"};
obsolete_1(ssh_cm, session_open, A) when A =:= 2; A =:= 4 ->
- {deprecated,{ssh_connection,session_channel,A},"R14B"};
+ {removed,{ssh_connection,session_channel,A},"R14B"};
obsolete_1(ssh_cm, direct_tcpip, A) when A =:= 6; A =:= 8 ->
- {deprecated,{ssh_connection,direct_tcpip,A}};
+ {removed,{ssh_connection,direct_tcpip,A}};
obsolete_1(ssh_cm, tcpip_forward, 3) ->
- {deprecated,{ssh_connection,tcpip_forward,3},"R14B"};
+ {removed,{ssh_connection,tcpip_forward,3},"R14B"};
obsolete_1(ssh_cm, cancel_tcpip_forward, 3) ->
- {deprecated,{ssh_connection,cancel_tcpip_forward,3},"R14B"};
+ {removed,{ssh_connection,cancel_tcpip_forward,3},"R14B"};
obsolete_1(ssh_cm, open_pty, A) when A =:= 3; A =:= 7; A =:= 9 ->
- {deprecated,{ssh_connection,open_pty,A},"R14"};
+ {removed,{ssh_connection,open_pty,A},"R14"};
obsolete_1(ssh_cm, setenv, 5) ->
- {deprecated,{ssh_connection,setenv,5},"R14B"};
+ {removed,{ssh_connection,setenv,5},"R14B"};
obsolete_1(ssh_cm, shell, 2) ->
- {deprecated,{ssh_connection,shell,2},"R14B"};
+ {removed,{ssh_connection,shell,2},"R14B"};
obsolete_1(ssh_cm, exec, 4) ->
- {deprecated,{ssh_connection,exec,4},"R14B"};
+ {removed,{ssh_connection,exec,4},"R14B"};
obsolete_1(ssh_cm, subsystem, 4) ->
- {deprecated,{ssh_connection,subsystem,4},"R14B"};
+ {removed,{ssh_connection,subsystem,4},"R14B"};
obsolete_1(ssh_cm, winch, A) when A =:= 4; A =:= 6 ->
- {deprecated,{ssh_connection,window_change,A},"R14B"};
+ {removed,{ssh_connection,window_change,A},"R14B"};
obsolete_1(ssh_cm, signal, 3) ->
- {deprecated,{ssh_connection,signal,3},"R14B"};
+ {removed,{ssh_connection,signal,3},"R14B"};
obsolete_1(ssh_cm, attach, A) when A =:= 2; A =:= 3 ->
- {deprecated,{ssh,attach,A}};
+ {removed,{ssh,attach,A}};
obsolete_1(ssh_cm, detach, 2) ->
- {deprecated,"no longer useful; will be removed in R14B"};
+ {removed,"no longer useful; will be removed in R14B"};
obsolete_1(ssh_cm, set_user_ack, 4) ->
- {deprecated,"no longer useful; will be removed in R14B"};
+ {removed,"no longer useful; will be removed in R14B"};
obsolete_1(ssh_cm, adjust_window, 3) ->
- {deprecated,{ssh_connection,adjust_window,3},"R14B"};
+ {removed,{ssh_connection,adjust_window,3},"R14B"};
obsolete_1(ssh_cm, close, 2) ->
- {deprecated,{ssh_connection,close,2},"R14B"};
+ {removed,{ssh_connection,close,2},"R14B"};
obsolete_1(ssh_cm, stop, 1) ->
- {deprecated,{ssh,close,1},"R14B"};
+ {removed,{ssh,close,1},"R14B"};
obsolete_1(ssh_cm, send_eof, 2) ->
- {deprecated,{ssh_connection,send_eof,2},"R14B"};
+ {removed,{ssh_connection,send_eof,2},"R14B"};
obsolete_1(ssh_cm, send, A) when A =:= 3; A =:= 4 ->
- {deprecated,{ssh_connection,send,A},"R14B"};
+ {removed,{ssh_connection,send,A},"R14B"};
obsolete_1(ssh_cm, send_ack, A) when 3 =< A, A =< 5 ->
- {deprecated,{ssh_connection,send,[3,4]},"R14B"};
+ {removed,{ssh_connection,send,[3,4]},"R14B"};
obsolete_1(ssh_ssh, connect, A) when 1 =< A, A =< 3 ->
- {deprecated,{ssh,shell,A},"R14B"};
+ {removed,{ssh,shell,A},"R14B"};
obsolete_1(ssh_sshd, listen, A) when 0 =< A, A =< 3 ->
- {deprecated,{ssh,daemon,[1,2,3]},"R14"};
+ {removed,{ssh,daemon,[1,2,3]},"R14"};
obsolete_1(ssh_sshd, stop, 1) ->
- {deprecated,{ssh,stop_listener,1}};
+ {removed,{ssh,stop_listener,1}};
%% Added in R13A.
obsolete_1(regexp, _, _) ->
{deprecated, "the regexp module is deprecated (will be removed in R15A); use the re module instead"};
obsolete_1(lists, flat_length, 1) ->
- {deprecated,{lists,flatlength,1},"R14"};
+ {removed,{lists,flatlength,1},"R14"};
obsolete_1(ssh_sftp, connect, A) when 1 =< A, A =< 3 ->
- {deprecated,{ssh_sftp,start_channel,A},"R14B"};
+ {removed,{ssh_sftp,start_channel,A},"R14B"};
obsolete_1(ssh_sftp, stop, 1) ->
- {deprecated,{ssh_sftp,stop_channel,1},"R14B"};
+ {removed,{ssh_sftp,stop_channel,1},"R14B"};
%% Added in R13B01.
obsolete_1(ssl_pkix, decode_cert_file, A) when A =:= 1; A =:= 2 ->
- {deprecated,"deprecated (will be removed in R14B); use public_key:pem_to_der/1 and public_key:pkix_decode_cert/2 instead"};
+ {removed,"removed in R14A; use public_key:pem_to_der/1 and public_key:pkix_decode_cert/2 instead"};
obsolete_1(ssl_pkix, decode_cert, A) when A =:= 1; A =:= 2 ->
- {deprecated,{public_key,pkix_decode_cert,2},"R14B"};
+ {removed,{public_key,pkix_decode_cert,2},"R14A"};
%% Added in R13B04.
obsolete_1(erlang, concat_binary, 1) ->
- {deprecated,{erlang,list_to_binary,1},"R14B"};
-
+ {deprecated,{erlang,list_to_binary,1},"R15B"};
+
+%% Added in R14A.
+obsolete_1(ssl, peercert, 2) ->
+ {deprecated,"deprecated (will be removed in R15A); use ssl:peercert/1 and public_key:pkix_decode_cert/2 instead"};
+
+%% Added in R14B.
+obsolete_1(public_key, pem_to_der, 1) ->
+ {deprecated,"deprecated (will be removed in R15A); use file:read_file/1 and public_key:pem_decode/1"};
+obsolete_1(public_key, decode_private_key, A) when A =:= 1; A =:= 2 ->
+ {deprecated,{public_key,pem_entry_decode,1},"R15A"};
+
obsolete_1(_, _, _) ->
no.
diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl
index 9aa5e0a71e..4fb64a3353 100644
--- a/lib/stdlib/src/proc_lib.erl
+++ b/lib/stdlib/src/proc_lib.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(proc_lib).
@@ -34,6 +34,8 @@
%% Internal exports.
-export([wake_up/3]).
+-export_type([spawn_option/0]).
+
%%-----------------------------------------------------------------------------
-type priority_level() :: 'high' | 'low' | 'max' | 'normal'.
diff --git a/lib/stdlib/src/proplists.erl b/lib/stdlib/src/proplists.erl
index 35d14891f1..6a45e0f868 100644
--- a/lib/stdlib/src/proplists.erl
+++ b/lib/stdlib/src/proplists.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%% =====================================================================
@@ -49,6 +49,8 @@
%% ---------------------------------------------------------------------
+-export_type([property/0]).
+
-type property() :: atom() | tuple().
-type aliases() :: [{any(), any()}].
diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl
index 6e48d95973..bc6944e520 100644
--- a/lib/stdlib/src/qlc.erl
+++ b/lib/stdlib/src/qlc.erl
@@ -24,6 +24,8 @@
%% External exports
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([parse_transform/2, transform_from_evaluator/2]).
-export([q/1, q/2]).
diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src
index 3e52c48e42..9d15f01683 100644
--- a/lib/stdlib/src/stdlib.app.src
+++ b/lib/stdlib/src/stdlib.app.src
@@ -1,20 +1,20 @@
%% This is an -*- erlang -*- file.
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
{application, stdlib,
@@ -23,6 +23,7 @@
{modules, [array,
base64,
beam_lib,
+ binary,
c,
calendar,
dets,
diff --git a/lib/stdlib/src/string.erl b/lib/stdlib/src/string.erl
index 6636a03f06..c987c224db 100644
--- a/lib/stdlib/src/string.erl
+++ b/lib/stdlib/src/string.erl
@@ -201,7 +201,7 @@ chars(C, 0, Tail) when is_integer(C) ->
-spec copies(string(), non_neg_integer()) -> string().
-copies(CharList, Num) when is_list(CharList), Num >= 0 ->
+copies(CharList, Num) when is_list(CharList), is_integer(Num), Num >= 0 ->
copies(CharList, Num, []).
copies(_CharList, 0, R) ->
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index 22269a8d1b..7102fb9f6e 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -21,7 +21,7 @@
-behaviour(gen_server).
%% External exports
--export([start_link/2,start_link/3,
+-export([start_link/2, start_link/3,
start_child/2, restart_child/2,
delete_child/2, terminate_child/2,
which_children/1, count_children/1,
@@ -33,28 +33,56 @@
-export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3]).
-export([handle_cast/2]).
+%%--------------------------------------------------------------------------
+
+-export_type([child_spec/0, del_err/0, startchild_ret/0, strategy/0]).
+
+%%--------------------------------------------------------------------------
+
+-type child_id() :: pid() | 'undefined'.
+-type mfargs() :: {module(), atom(), [term()]}.
+-type modules() :: [module()] | 'dynamic'.
+-type restart() :: 'permanent' | 'transient' | 'temporary'.
+-type shutdown() :: 'brutal_kill' | timeout().
+-type worker() :: 'worker' | 'supervisor'.
+-type sup_name() :: {'local', atom()} | {'global', atom()}.
+-type sup_ref() :: atom() | {atom(), atom()} | {'global', atom()} | pid().
+-type child_spec() :: {term(),mfargs(),restart(),shutdown(),worker(),modules()}.
+
+-type strategy() :: 'one_for_all' | 'one_for_one'
+ | 'rest_for_one' | 'simple_one_for_one'.
+
+%%--------------------------------------------------------------------------
+
+-record(child, {% pid is undefined when child is not running
+ pid = undefined :: child_id(),
+ name,
+ mfargs :: mfargs(),
+ restart_type :: restart(),
+ shutdown :: shutdown(),
+ child_type :: worker(),
+ modules = [] :: modules()}).
+-type child() :: #child{}.
+
-define(DICT, dict).
-record(state, {name,
- strategy,
- children = [],
- dynamics = ?DICT:new(),
- intensity,
- period,
+ strategy :: strategy(),
+ children = [] :: [child()],
+ dynamics = ?DICT:new() :: ?DICT(),
+ intensity :: non_neg_integer(),
+ period :: pos_integer(),
restarts = [],
module,
args}).
-
--record(child, {pid = undefined, % pid is undefined when child is not running
- name,
- mfa,
- restart_type,
- shutdown,
- child_type,
- modules = []}).
+-type state() :: #state{}.
-define(is_simple(State), State#state.strategy =:= simple_one_for_one).
+%%--------------------------------------------------------------------------
+
+-spec behaviour_info(atom()) -> 'undefined' | [{atom(), arity()}].
+
behaviour_info(callbacks) ->
[{init,1}];
behaviour_info(_Other) ->
@@ -65,21 +93,40 @@ behaviour_info(_Other) ->
%%% Servers/processes should/could also be built using gen_server.erl.
%%% SupName = {local, atom()} | {global, atom()}.
%%% ---------------------------------------------------
+
+-type startlink_err() :: {'already_started', pid()} | 'shutdown' | term().
+-type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}.
+
+-spec start_link(module(), term()) -> startlink_ret().
start_link(Mod, Args) ->
gen_server:start_link(supervisor, {self, Mod, Args}, []).
+-spec start_link(sup_name(), module(), term()) -> startlink_ret().
start_link(SupName, Mod, Args) ->
gen_server:start_link(SupName, supervisor, {SupName, Mod, Args}, []).
%%% ---------------------------------------------------
%%% Interface functions.
%%% ---------------------------------------------------
+
+-type info() :: term().
+-type startchild_err() :: 'already_present'
+ | {'already_started', child_id()} | term().
+-type startchild_ret() :: {'ok', child_id()} | {'ok', child_id(), info()}
+ | {'error', startchild_err()}.
+
+-spec start_child(sup_ref(), child_spec() | [term()]) -> startchild_ret().
start_child(Supervisor, ChildSpec) ->
call(Supervisor, {start_child, ChildSpec}).
+-type restart_err() :: 'running' | 'not_found' | 'simple_one_for_one' | term().
+-spec restart_child(sup_ref(), term()) ->
+ {'ok', child_id()} | {'ok', child_id(), info()} | {'error', restart_err()}.
restart_child(Supervisor, Name) ->
call(Supervisor, {restart_child, Name}).
+-type del_err() :: 'running' | 'not_found' | 'simple_one_for_one'.
+-spec delete_child(sup_ref(), term()) -> 'ok' | {'error', del_err()}.
delete_child(Supervisor, Name) ->
call(Supervisor, {delete_child, Name}).
@@ -89,9 +136,13 @@ delete_child(Supervisor, Name) ->
%% Note that the child is *always* terminated in some
%% way (maybe killed).
%%-----------------------------------------------------------------
+
+-type term_err() :: 'not_found' | 'simple_one_for_one'.
+-spec terminate_child(sup_ref(), term()) -> 'ok' | {'error', term_err()}.
terminate_child(Supervisor, Name) ->
call(Supervisor, {terminate_child, Name}).
+-spec which_children(sup_ref()) -> [{term(), child_id(), worker(), modules()}].
which_children(Supervisor) ->
call(Supervisor, which_children).
@@ -101,6 +152,7 @@ count_children(Supervisor) ->
call(Supervisor, Req) ->
gen_server:call(Supervisor, Req, infinity).
+-spec check_childspecs([child_spec()]) -> 'ok' | {'error', term()}.
check_childspecs(ChildSpecs) when is_list(ChildSpecs) ->
case check_startspec(ChildSpecs) of
{ok, _} -> ok;
@@ -113,6 +165,16 @@ check_childspecs(X) -> {error, {badarg, X}}.
%%% Initialize the supervisor.
%%%
%%% ---------------------------------------------------
+
+-type init_sup_name() :: sup_name() | 'self'.
+
+-type stop_rsn() :: 'shutdown' | {'bad_return', {module(),'init', term()}}
+ | {'bad_start_spec', term()} | {'start_spec', term()}
+ | {'supervisor_data', term()}.
+
+-spec init({init_sup_name(), module(), [term()]}) ->
+ {'ok', state()} | 'ignore' | {'stop', stop_rsn()}.
+
init({SupName, Mod, Args}) ->
process_flag(trap_exit, true),
case Mod:init(Args) of
@@ -130,7 +192,7 @@ init({SupName, Mod, Args}) ->
Error ->
{stop, {bad_return, {Mod, init, Error}}}
end.
-
+
init_children(State, StartSpec) ->
SupName = State#state.name,
case check_startspec(StartSpec) of
@@ -158,12 +220,12 @@ init_dynamic(_State, StartSpec) ->
%%-----------------------------------------------------------------
%% Func: start_children/2
-%% Args: Children = [#child] in start order
-%% SupName = {local, atom()} | {global, atom()} | {pid(),Mod}
+%% Args: Children = [child()] in start order
+%% SupName = {local, atom()} | {global, atom()} | {pid(), Mod}
%% Purpose: Start all children. The new list contains #child's
%% with pids.
%% Returns: {ok, NChildren} | {error, NChildren}
-%% NChildren = [#child] in termination order (reversed
+%% NChildren = [child()] in termination order (reversed
%% start order)
%%-----------------------------------------------------------------
start_children(Children, SupName) -> start_children(Children, [], SupName).
@@ -182,8 +244,8 @@ start_children([], NChildren, _SupName) ->
{ok, NChildren}.
do_start_child(SupName, Child) ->
- #child{mfa = {M, F, A}} = Child,
- case catch apply(M, F, A) of
+ #child{mfargs = {M, F, Args}} = Child,
+ case catch apply(M, F, Args) of
{ok, Pid} when is_pid(Pid) ->
NChild = Child#child{pid = Pid},
report_progress(NChild, SupName),
@@ -192,7 +254,7 @@ do_start_child(SupName, Child) ->
NChild = Child#child{pid = Pid},
report_progress(NChild, SupName),
{ok, Pid, Extra};
- ignore ->
+ ignore ->
{ok, undefined};
{error, What} -> {error, What};
What -> {error, What}
@@ -211,15 +273,17 @@ do_start_child_i(M, F, A) ->
What ->
{error, What}
end.
-
%%% ---------------------------------------------------
%%%
%%% Callback functions.
%%%
%%% ---------------------------------------------------
+-type call() :: 'which_children' | 'count_children' | {_, _}. % XXX: refine
+-spec handle_call(call(), term(), state()) -> {'reply', term(), state()}.
+
handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) ->
- #child{mfa = {M, F, A}} = hd(State#state.children),
+ #child{mfargs = {M, F, A}} = hd(State#state.children),
Args = A ++ EArgs,
case do_start_child_i(M, F, Args) of
{ok, Pid} ->
@@ -235,7 +299,7 @@ handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) ->
end;
%%% The requests terminate_child, delete_child and restart_child are
-%%% invalid for simple_one_for_one supervisors.
+%%% invalid for simple_one_for_one supervisors.
handle_call({_Req, _Data}, _From, State) when ?is_simple(State) ->
{reply, {error, simple_one_for_one}, State};
@@ -297,7 +361,7 @@ handle_call(which_children, _From, State) ->
Resp =
lists:map(fun(#child{pid = Pid, name = Name,
child_type = ChildType, modules = Mods}) ->
- {Name, Pid, ChildType, Mods}
+ {Name, Pid, ChildType, Mods}
end,
State#state.children),
{reply, Resp, State};
@@ -318,7 +382,6 @@ handle_call(count_children, _From, State) when ?is_simple(State) ->
{reply, Reply, State};
handle_call(count_children, _From, State) ->
-
%% Specs and children are together on the children list...
{Specs, Active, Supers, Workers} =
lists:foldl(fun(Child, Counts) ->
@@ -347,15 +410,19 @@ count_child(#child{pid = Pid, child_type = supervisor},
%%% Hopefully cause a function-clause as there is no API function
%%% that utilizes cast.
+-spec handle_cast('null', state()) -> {'noreply', state()}.
+
handle_cast(null, State) ->
error_logger:error_msg("ERROR: Supervisor received cast-message 'null'~n",
[]),
-
{noreply, State}.
%%
%% Take care of terminated children.
%%
+-spec handle_info(term(), state()) ->
+ {'noreply', state()} | {'stop', 'shutdown', state()}.
+
handle_info({'EXIT', Pid, Reason}, State) ->
case restart_child(Pid, Reason, State) of
{ok, State1} ->
@@ -368,9 +435,12 @@ handle_info(Msg, State) ->
error_logger:error_msg("Supervisor received unexpected message: ~p~n",
[Msg]),
{noreply, State}.
+
%%
%% Terminate this server.
%%
+-spec terminate(term(), state()) -> 'ok'.
+
terminate(_Reason, State) ->
terminate_children(State#state.children, State#state.name),
ok.
@@ -384,6 +454,9 @@ terminate(_Reason, State) ->
%% NOTE: This requires that the init function of the call-back module
%% does not have any side effects.
%%
+-spec code_change(term(), state(), term()) ->
+ {'ok', state()} | {'error', term()}.
+
code_change(_, State, _) ->
case (State#state.module):init(State#state.args) of
{ok, {SupFlags, StartSpec}} ->
@@ -411,7 +484,7 @@ check_flags({Strategy, MaxIntensity, Period}) ->
check_flags(What) ->
{bad_flags, What}.
-update_childspec(State, StartSpec) when ?is_simple(State) ->
+update_childspec(State, StartSpec) when ?is_simple(State) ->
case check_startspec(StartSpec) of
{ok, [Child]} ->
{ok, State#state{children = [Child]}};
@@ -437,7 +510,7 @@ update_childspec1([Child|OldC], Children, KeepOld) ->
update_childspec1(OldC, Children, [Child|KeepOld])
end;
update_childspec1([], Children, KeepOld) ->
- % Return them in (keeped) reverse start order.
+ %% Return them in (kept) reverse start order.
lists:reverse(Children ++ KeepOld).
update_chsp(OldCh, Children) ->
@@ -482,7 +555,7 @@ handle_start_child(Child, State) ->
%%% ---------------------------------------------------
%%% Restart. A process has terminated.
-%%% Returns: {ok, #state} | {shutdown, #state}
+%%% Returns: {ok, state()} | {shutdown, state()}
%%% ---------------------------------------------------
restart_child(Pid, Reason, State) when ?is_simple(State) ->
@@ -490,19 +563,19 @@ restart_child(Pid, Reason, State) when ?is_simple(State) ->
{ok, Args} ->
[Child] = State#state.children,
RestartType = Child#child.restart_type,
- {M, F, _} = Child#child.mfa,
- NChild = Child#child{pid = Pid, mfa = {M, F, Args}},
+ {M, F, _} = Child#child.mfargs,
+ NChild = Child#child{pid = Pid, mfargs = {M, F, Args}},
do_restart(RestartType, Reason, NChild, State);
error ->
{ok, State}
end;
restart_child(Pid, Reason, State) ->
Children = State#state.children,
- case lists:keysearch(Pid, #child.pid, Children) of
- {value, Child} ->
+ case lists:keyfind(Pid, #child.pid, Children) of
+ #child{} = Child ->
RestartType = Child#child.restart_type,
do_restart(RestartType, Reason, Child, State);
- _ ->
+ false ->
{ok, State}
end.
@@ -534,7 +607,7 @@ restart(Child, State) ->
end.
restart(simple_one_for_one, Child, State) ->
- #child{mfa = {M, F, A}} = Child,
+ #child{mfargs = {M, F, A}} = Child,
Dynamics = ?DICT:erase(Child#child.pid, State#state.dynamics),
case do_start_child_i(M, F, A) of
{ok, Pid} ->
@@ -580,9 +653,9 @@ restart(one_for_all, Child, State) ->
%%-----------------------------------------------------------------
%% Func: terminate_children/2
-%% Args: Children = [#child] in termination order
+%% Args: Children = [child()] in termination order
%% SupName = {local, atom()} | {global, atom()} | {pid(),Mod}
-%% Returns: NChildren = [#child] in
+%% Returns: NChildren = [child()] in
%% startup order (reversed termination order)
%%-----------------------------------------------------------------
terminate_children(Children, SupName) ->
@@ -595,14 +668,15 @@ terminate_children([], _SupName, Res) ->
Res.
do_terminate(Child, SupName) when Child#child.pid =/= undefined ->
- case shutdown(Child#child.pid,
- Child#child.shutdown) of
- ok ->
- Child#child{pid = undefined};
- {error, OtherReason} ->
- report_error(shutdown_error, OtherReason, Child, SupName),
- Child#child{pid = undefined}
- end;
+ case shutdown(Child#child.pid, Child#child.shutdown) of
+ ok ->
+ ok;
+ {error, normal} when Child#child.restart_type =/= permanent ->
+ ok;
+ {error, OtherReason} ->
+ report_error(shutdown_error, OtherReason, Child, SupName)
+ end,
+ Child#child{pid = undefined};
do_terminate(Child, _SupName) ->
Child.
@@ -617,7 +691,6 @@ do_terminate(Child, _SupName) ->
%% Returns: ok | {error, OtherReason} (this should be reported)
%%-----------------------------------------------------------------
shutdown(Pid, brutal_kill) ->
-
case monitor_child(Pid) of
ok ->
exit(Pid, kill),
@@ -630,9 +703,7 @@ shutdown(Pid, brutal_kill) ->
{error, Reason} ->
{error, Reason}
end;
-
shutdown(Pid, Time) ->
-
case monitor_child(Pid) of
ok ->
exit(Pid, shutdown), %% Try to shutdown gracefully
@@ -738,9 +809,9 @@ remove_child(Child, State) ->
%% MaxIntensity = integer()
%% Period = integer()
%% Mod :== atom()
-%% Arsg :== term()
+%% Args :== term()
%% Purpose: Check that Type is of correct type (!)
-%% Returns: {ok, #state} | Error
+%% Returns: {ok, state()} | Error
%%-----------------------------------------------------------------
init_state(SupName, Type, Mod, Args) ->
case catch init_state1(SupName, Type, Mod, Args) of
@@ -755,11 +826,11 @@ init_state1(SupName, {Strategy, MaxIntensity, Period}, Mod, Args) ->
validIntensity(MaxIntensity),
validPeriod(Period),
{ok, #state{name = supname(SupName,Mod),
- strategy = Strategy,
- intensity = MaxIntensity,
- period = Period,
- module = Mod,
- args = Args}};
+ strategy = Strategy,
+ intensity = MaxIntensity,
+ period = Period,
+ module = Mod,
+ args = Args}};
init_state1(_SupName, Type, _, _) ->
{invalid_type, Type}.
@@ -771,26 +842,26 @@ validStrategy(What) -> throw({invalid_strategy, What}).
validIntensity(Max) when is_integer(Max),
Max >= 0 -> true;
-validIntensity(What) -> throw({invalid_intensity, What}).
+validIntensity(What) -> throw({invalid_intensity, What}).
validPeriod(Period) when is_integer(Period),
Period > 0 -> true;
validPeriod(What) -> throw({invalid_period, What}).
-supname(self,Mod) -> {self(),Mod};
-supname(N,_) -> N.
+supname(self, Mod) -> {self(), Mod};
+supname(N, _) -> N.
%%% ------------------------------------------------------
%%% Check that the children start specification is valid.
%%% Shall be a six (6) tuple
%%% {Name, Func, RestartType, Shutdown, ChildType, Modules}
%%% where Name is an atom
-%%% Func is {Mod, Fun, Args} == {atom, atom, list}
+%%% Func is {Mod, Fun, Args} == {atom(), atom(), list()}
%%% RestartType is permanent | temporary | transient
%%% Shutdown = integer() | infinity | brutal_kill
%%% ChildType = supervisor | worker
%%% Modules = [atom()] | dynamic
-%%% Returns: {ok, [#child]} | Error
+%%% Returns: {ok, [child()]} | Error
%%% ------------------------------------------------------
check_startspec(Children) -> check_startspec(Children, []).
@@ -818,14 +889,14 @@ check_childspec(Name, Func, RestartType, Shutdown, ChildType, Mods) ->
validChildType(ChildType),
validShutdown(Shutdown, ChildType),
validMods(Mods),
- {ok, #child{name = Name, mfa = Func, restart_type = RestartType,
+ {ok, #child{name = Name, mfargs = Func, restart_type = RestartType,
shutdown = Shutdown, child_type = ChildType, modules = Mods}}.
validChildType(supervisor) -> true;
validChildType(worker) -> true;
validChildType(What) -> throw({invalid_child_type, What}).
-validName(_Name) -> true.
+validName(_Name) -> true.
validFunc({M, F, A}) when is_atom(M),
is_atom(F),
@@ -923,7 +994,7 @@ report_error(Error, Reason, Child, SupName) ->
extract_child(Child) ->
[{pid, Child#child.pid},
{name, Child#child.name},
- {mfa, Child#child.mfa},
+ {mfargs, Child#child.mfargs},
{restart_type, Child#child.restart_type},
{shutdown, Child#child.shutdown},
{child_type, Child#child.child_type}].
diff --git a/lib/stdlib/src/timer.erl b/lib/stdlib/src/timer.erl
index 36fdb48c75..b456c5d6c1 100644
--- a/lib/stdlib/src/timer.erl
+++ b/lib/stdlib/src/timer.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(timer).
@@ -22,7 +22,7 @@
send_after/3, send_after/2,
exit_after/3, exit_after/2, kill_after/2, kill_after/1,
apply_interval/4, send_interval/3, send_interval/2,
- cancel/1, sleep/1, tc/3, now_diff/2,
+ cancel/1, sleep/1, tc/2, tc/3, now_diff/2,
seconds/1, minutes/1, hours/1, hms/3]).
-export([start_link/0, start/0,
@@ -33,6 +33,9 @@
%% internal exports for test purposes only
-export([get_status/0]).
+%% types which can be used by other modules
+-export_type([tref/0]).
+
%% Max
-define(MAX_TIMEOUT, 16#0800000).
-define(TIMER_TAB, timer_tab).
@@ -41,54 +44,54 @@
%%
%% Time is in milliseconds.
%%
--opaque tref() :: any().
+-opaque tref() :: {integer(), reference()}.
-type time() :: non_neg_integer().
-type timestamp() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}.
%%
%% Interface functions
%%
--spec apply_after(time(), atom(), atom(), [_]) -> {'ok', tref()} | {'error', _}.
+-spec apply_after(time(), atom(), atom(), [term()]) -> {'ok', tref()} | {'error', term()}.
apply_after(Time, M, F, A) ->
req(apply_after, {Time, {M, F, A}}).
--spec send_after(time(), pid() | atom(), term()) -> {'ok', tref()} | {'error', _}.
+-spec send_after(time(), pid() | atom(), term()) -> {'ok', tref()} | {'error', term()}.
send_after(Time, Pid, Message) ->
req(apply_after, {Time, {?MODULE, send, [Pid, Message]}}).
--spec send_after(time(), _) -> {'ok', tref()} | {'error', _}.
+-spec send_after(time(), term()) -> {'ok', tref()} | {'error', term()}.
send_after(Time, Message) ->
send_after(Time, self(), Message).
--spec exit_after(time(), pid() | atom(), _) -> {'ok', tref()} | {'error', _}.
+-spec exit_after(time(), pid() | atom(), term()) -> {'ok', tref()} | {'error', term()}.
exit_after(Time, Pid, Reason) ->
req(apply_after, {Time, {erlang, exit, [Pid, Reason]}}).
--spec exit_after(time(), term()) -> {'ok', tref()} | {'error', _}.
+-spec exit_after(time(), term()) -> {'ok', tref()} | {'error', term()}.
exit_after(Time, Reason) ->
exit_after(Time, self(), Reason).
--spec kill_after(time(), pid() | atom()) -> {'ok', tref()} | {'error', _}.
+-spec kill_after(time(), pid() | atom()) -> {'ok', tref()} | {'error', term()}.
kill_after(Time, Pid) ->
exit_after(Time, Pid, kill).
--spec kill_after(time()) -> {'ok', tref()} | {'error', _}.
+-spec kill_after(time()) -> {'ok', tref()} | {'error', term()}.
kill_after(Time) ->
exit_after(Time, self(), kill).
--spec apply_interval(time(), atom(), atom(), [_]) -> {'ok', tref()} | {'error', _}.
+-spec apply_interval(time(), atom(), atom(), [term()]) -> {'ok', tref()} | {'error', term()}.
apply_interval(Time, M, F, A) ->
req(apply_interval, {Time, self(), {M, F, A}}).
--spec send_interval(time(), pid() | atom(), term()) -> {'ok', tref()} | {'error', _}.
+-spec send_interval(time(), pid() | atom(), term()) -> {'ok', tref()} | {'error', term()}.
send_interval(Time, Pid, Message) ->
req(apply_interval, {Time, Pid, {?MODULE, send, [Pid, Message]}}).
--spec send_interval(time(), term()) -> {'ok', tref()} | {'error', _}.
+-spec send_interval(time(), term()) -> {'ok', tref()} | {'error', term()}.
send_interval(Time, Message) ->
send_interval(Time, self(), Message).
--spec cancel(tref()) -> {'ok', 'cancel'} | {'error', _}.
+-spec cancel(tref()) -> {'ok', 'cancel'} | {'error', term()}.
cancel(BRef) ->
req(cancel, BRef).
@@ -98,10 +101,21 @@ sleep(T) ->
after T -> ok
end.
+
+%%
+%% Measure the execution time (in microseconds) for Fun(Args).
+%%
+-spec tc(function(), [_]) -> {time(), term()}.
+tc(F, A) ->
+ Before = erlang:now(),
+ Val = (catch apply(F, A)),
+ After = erlang:now(),
+ {now_diff(After, Before), Val}.
+
%%
%% Measure the execution time (in microseconds) for an MFA.
%%
--spec tc(atom(), atom(), [_]) -> {time(), term()}.
+-spec tc(atom(), atom(), [term()]) -> {time(), term()}.
tc(M, F, A) ->
Before = erlang:now(),
Val = (catch apply(M, F, A)),
@@ -141,7 +155,7 @@ hms(H, M, S) ->
start() ->
ensure_started().
--spec start_link() -> {'ok', pid()} | {'error', _}.
+-spec start_link() -> {'ok', pid()} | {'error', term()}.
start_link() ->
gen_server:start_link({local, timer_server}, ?MODULE, [], []).
@@ -152,6 +166,7 @@ init([]) ->
?INTERVAL_TAB = ets:new(?INTERVAL_TAB, [named_table,protected]),
{ok, [], infinity}.
+-spec ensure_started() -> 'ok'.
ensure_started() ->
case whereis(timer_server) of
undefined ->
@@ -175,6 +190,10 @@ req(Req, Arg) ->
%%
%% Time and Timeout is in milliseconds. Started is in microseconds.
%%
+-type timers() :: term(). % XXX: refine?
+
+-spec handle_call(term(), term(), timers()) ->
+ {'reply', term(), timers(), timeout()} | {'noreply', timers(), timeout()}.
handle_call({apply_after, {Time, Op}, Started}, _From, _Ts)
when is_integer(Time), Time >= 0 ->
BRef = {Started + 1000*Time, make_ref()},
@@ -194,7 +213,7 @@ handle_call({apply_interval, {Time, To, MFA}, Started}, _From, _Ts)
Interval = Time*1000,
BRef2 = {Started + Interval, Ref},
Timer = {BRef2, {repeat, Interval, Pid}, MFA},
- ets:insert(?INTERVAL_TAB,{BRef1,BRef2,Pid}),
+ ets:insert(?INTERVAL_TAB, {BRef1,BRef2,Pid}),
ets:insert(?TIMER_TAB, Timer),
Timeout = timer_timeout(SysTime),
{reply, {ok, BRef1}, [], Timeout};
@@ -202,7 +221,7 @@ handle_call({apply_interval, {Time, To, MFA}, Started}, _From, _Ts)
{reply, {error, badarg}, [], next_timeout()}
end;
handle_call({cancel, BRef = {_Time, Ref}, _}, _From, Ts)
- when is_reference(Ref) ->
+ when is_reference(Ref) ->
delete_ref(BRef),
{reply, {ok, cancel}, Ts, next_timeout()};
handle_call({cancel, _BRef, _}, _From, Ts) ->
@@ -214,6 +233,7 @@ handle_call({apply_interval, _, _}, _From, Ts) ->
handle_call(_Else, _From, Ts) -> % Catch anything else
{noreply, Ts, next_timeout()}.
+-spec handle_info(term(), timers()) -> {'noreply', timers(), timeout()}.
handle_info(timeout, Ts) -> % Handle timeouts
Timeout = timer_timeout(system_time()),
{noreply, Ts, Timeout};
@@ -223,19 +243,21 @@ handle_info({'EXIT', Pid, _Reason}, Ts) -> % Oops, someone died
handle_info(_OtherMsg, Ts) -> % Other Msg's
{noreply, Ts, next_timeout()}.
+-spec handle_cast(term(), timers()) -> {'noreply', timers(), timeout()}.
handle_cast(_Req, Ts) -> % Not predicted but handled
{noreply, Ts, next_timeout()}.
--spec terminate(_, _) -> 'ok'.
+-spec terminate(term(), _State) -> 'ok'.
terminate(_Reason, _State) ->
ok.
+-spec code_change(term(), State, term()) -> {'ok', State}.
code_change(_OldVsn, State, _Extra) ->
%% According to the man for gen server no timer can be set here.
{ok, State}.
%%
-%% timer_timeout(Timers, SysTime)
+%% timer_timeout(SysTime)
%%
%% Apply and remove already timed-out timers. A timer is a tuple
%% {Time, BRef, Op, MFA}, where Time is in microseconds.
@@ -279,12 +301,13 @@ delete_ref(BRef = {interval, _}) ->
ok
end;
delete_ref(BRef) ->
- ets:delete(?TIMER_TAB,BRef).
+ ets:delete(?TIMER_TAB, BRef).
%%
%% pid_delete
%%
+-spec pid_delete(pid()) -> 'ok'.
pid_delete(Pid) ->
IntervalTimerList =
ets:select(?INTERVAL_TAB,
@@ -292,13 +315,14 @@ pid_delete(Pid) ->
[{'==','$1',Pid}],
['$_']}]),
lists:foreach(fun({IntKey, TimerKey, _ }) ->
- ets:delete(?INTERVAL_TAB,IntKey),
- ets:delete(?TIMER_TAB,TimerKey)
+ ets:delete(?INTERVAL_TAB, IntKey),
+ ets:delete(?TIMER_TAB, TimerKey)
end, IntervalTimerList).
%% Calculate time to the next timeout. Returned timeout must fit in a
%% small int.
+-spec next_timeout() -> timeout().
next_timeout() ->
case ets:first(?TIMER_TAB) of
'$end_of_table' ->
@@ -358,7 +382,7 @@ get_pid(_) ->
get_status() ->
Info1 = ets:info(?TIMER_TAB),
- {value,{size,TotalNumTimers}} = lists:keysearch(size, 1, Info1),
+ {size,TotalNumTimers} = lists:keyfind(size, 1, Info1),
Info2 = ets:info(?INTERVAL_TAB),
- {value,{size,NumIntervalTimers}} = lists:keysearch(size, 1, Info2),
+ {size,NumIntervalTimers} = lists:keyfind(size, 1, Info2),
{{?TIMER_TAB,TotalNumTimers},{?INTERVAL_TAB,NumIntervalTimers}}.
diff --git a/lib/stdlib/src/unicode.erl b/lib/stdlib/src/unicode.erl
index 09b1deff9c..869505ba83 100644
--- a/lib/stdlib/src/unicode.erl
+++ b/lib/stdlib/src/unicode.erl
@@ -25,8 +25,17 @@
%% InEncoding is not {latin1 | unicode | utf8})
%%
--export([characters_to_list/1, characters_to_list_int/2, characters_to_binary/1,characters_to_binary_int/2, characters_to_binary/3,bom_to_encoding/1, encoding_to_bom/1]).
+-export([characters_to_list/1, characters_to_list_int/2,
+ characters_to_binary/1, characters_to_binary_int/2,
+ characters_to_binary/3,
+ bom_to_encoding/1, encoding_to_bom/1]).
+-export_type([encoding/0]).
+
+-type encoding() :: 'latin1' | 'unicode' | 'utf8'
+ | 'utf16' | {'utf16', endian()}
+ | 'utf32' | {'utf32', endian()}.
+-type endian() :: 'big' | 'little'.
characters_to_list(ML) ->
unicode:characters_to_list(ML,unicode).
diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl
index f44d97c227..d41aeefa59 100644
--- a/lib/stdlib/src/zip.erl
+++ b/lib/stdlib/src/zip.erl
@@ -1,26 +1,26 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(zip).
%% Basic api
-export([unzip/1, unzip/2, extract/1, extract/2,
- zip/2, zip/3, create/2, create/3,
+ zip/2, zip/3, create/2, create/3, foldl/3,
list_dir/1, list_dir/2, table/1, table/2,
t/1, tt/1]).
@@ -38,7 +38,7 @@
zip_t/1, zip_tt/1,
zip_list_dir/1, zip_list_dir/2,
zip_close/1]).
-
+
%% just for debugging zip server, not documented, not tested, not to be used
-export([zip_get_state/1]).
@@ -82,7 +82,7 @@
-record(openzip_opts, {
output, % output object (fun)
open_opts, % file:open options
- cwd % directory to relate paths to
+ cwd % directory to relate paths to
}).
% openzip record, state for an open zip-file
@@ -93,10 +93,10 @@
input, % archive io object (fun)
output, % output io object (fun)
zlib, % handle to open zlib
- cwd % directory to relate paths to
+ cwd % directory to relate paths to
}).
-% Things that I would like to add to the public record #zip_file,
+% Things that I would like to add to the public record #zip_file,
% but can't as it would make things fail at upgrade.
% Instead we use {#zip_file,#zip_file_extra} internally.
-record(zip_file_extra, {
@@ -278,7 +278,7 @@ file_name_search(Name,Files) ->
[ZFile|_] -> ZFile;
[] -> false
end.
-
+
%% %% add a file to an open archive
%% openzip_add(File, OpenZip) ->
%% case ?CATCH do_openzip_add(File, OpenZip) of
@@ -344,6 +344,25 @@ do_unzip(F, Options) ->
Input(close, In1),
{ok, Files}.
+%% Iterate over all files in a zip archive
+foldl(Fun, Acc0, Archive) when is_function(Fun, 4) ->
+ ZipFun =
+ fun({Name, GetInfo, GetBin}, A) ->
+ A2 = Fun(Name, GetInfo, GetBin, A),
+ {true, false, A2}
+ end,
+ case prim_zip:open(ZipFun, Acc0, Archive) of
+ {ok, PrimZip, Acc1} ->
+ ok = prim_zip:close(PrimZip),
+ {ok, Acc1};
+ {error, bad_eocd} ->
+ {error, "Not an archive file"};
+ {error, Reason} ->
+ {error, Reason}
+ end;
+foldl(_,_, _) ->
+ {error, einval}.
+
%% Create zip archive name F from Files or binaries
%%
%% Accepted options:
@@ -383,7 +402,7 @@ list_dir(F, Options) ->
do_list_dir(F, Options) ->
Opts = get_list_dir_options(F, Options),
- #list_dir_opts{input = Input, open_opts = OpO,
+ #list_dir_opts{input = Input, open_opts = OpO,
raw_iterator = RawIterator} = Opts,
In0 = Input({open, F, OpO}, []),
{Info, In1} = get_central_dir(In0, RawIterator, Input),
@@ -417,7 +436,7 @@ tt(F) when is_record(F, openzip) -> openzip_tt(F);
tt(F) -> t(F, fun raw_long_print_info_etc/5).
-%% option utils
+%% option utils
get_unzip_opt([], Opts) ->
Opts;
get_unzip_opt([verbose | Rest], Opts) ->
@@ -470,7 +489,7 @@ get_zip_opt([{cwd, CWD} | Rest], Opts) ->
get_zip_opt([{comment, C} | Rest], Opts) ->
get_zip_opt(Rest, Opts#zip_opts{comment = C});
get_zip_opt([{compress, Which} = O| Rest], Opts) ->
- Which2 =
+ Which2 =
case Which of
all ->
all;
@@ -485,7 +504,7 @@ get_zip_opt([{compress, Which} = O| Rest], Opts) ->
end,
get_zip_opt(Rest, Opts#zip_opts{compress = Which2});
get_zip_opt([{uncompress, Which} = O| Rest], Opts) ->
- Which2 =
+ Which2 =
case Which of
all ->
all;
@@ -560,16 +579,24 @@ get_openzip_options(Options) ->
get_input(F) when is_binary(F) ->
fun binary_io/2;
get_input(F) when is_list(F) ->
- fun file_io/2.
+ fun file_io/2;
+get_input(_) ->
+ throw(einval).
get_zip_input({F, B}) when is_binary(B), is_list(F) ->
fun binary_io/2;
+get_zip_input({F, B, #file_info{}}) when is_binary(B), is_list(F) ->
+ fun binary_io/2;
+get_zip_input({F, #file_info{}, B}) when is_binary(B), is_list(F) ->
+ fun binary_io/2;
get_zip_input(F) when is_list(F) ->
fun file_io/2;
get_zip_input({files, []}) ->
fun binary_io/2;
get_zip_input({files, [File | _]}) ->
- get_zip_input(File).
+ get_zip_input(File);
+get_zip_input(_) ->
+ throw(einval).
get_list_dir_options(F, Options) ->
Opts = #list_dir_opts{raw_iterator = fun raw_file_info_public/5,
@@ -620,6 +647,8 @@ put_eocd(N, Pos, Sz, Comment, Output, Out0) ->
get_filename({Name, _}, Type) ->
get_filename(Name, Type);
+get_filename({Name, _, _}, Type) ->
+ get_filename(Name, Type);
get_filename(Name, regular) ->
Name;
get_filename(Name, directory) ->
@@ -895,7 +924,7 @@ local_file_header_to_bin(
CompSize:32/little,
UncompSize:32/little,
FileNameLength:16/little,
- ExtraFieldLength:16/little>>.
+ ExtraFieldLength:16/little>>.
eocd_to_bin(#eocd{disk_num = DiskNum,
start_disk_num = StartDiskNum,
@@ -912,7 +941,7 @@ eocd_to_bin(#eocd{disk_num = DiskNum,
Offset:32/little,
ZipCommentLength:16/little>>.
-%% put together a local file header
+%% put together a local file header
local_file_header_from_info_method_name(#file_info{mtime = MTime},
UncompSize,
CompMethod, Name) ->
@@ -939,7 +968,7 @@ server_loop(OpenZip) ->
server_loop(NewOpenZip);
Error ->
From ! {self(), Error}
- end;
+ end;
{From, close} ->
From ! {self(), openzip_close(OpenZip)};
{From, get} ->
@@ -1024,7 +1053,7 @@ lists_foreach(F, [Hd|Tl]) ->
F(Hd),
lists_foreach(F, Tl).
-%% option utils
+%% option utils
get_openzip_opt([], Opts) ->
Opts;
get_openzip_opt([cooked | Rest], #openzip_opts{open_opts = OO} = Opts) ->
@@ -1121,7 +1150,7 @@ raw_file_info_public(CD, FileName, FileComment, BExtraField, Acc0) ->
Other -> Other
end,
[H2|T].
-
+
%% make a file_info from a central directory header
cd_file_header_to_file_info(FileName,
@@ -1213,8 +1242,8 @@ get_z_file(In0, Z, Input, Output, OpO, FB, CWD, {ZipFile,Extra}) ->
{dir, In3};
_ ->
%% FileInfo = local_file_header_to_file_info(LH)
- %%{Out, In4, CRC, UncompSize} =
- {Out, In4, CRC, _UncompSize} =
+ %%{Out, In4, CRC, UncompSize} =
+ {Out, In4, CRC, _UncompSize} =
get_z_data(CompMethod, In3, FileName1,
CompSize, Input, Output, OpO, Z),
In5 = skip_z_data_descriptor(GPFlag, Input, In4),
@@ -1280,7 +1309,7 @@ get_z_data_loop(CompSize, UncompSize, In0, Out0, Input, Output, Z) ->
Out1 = Output({write, Uncompressed}, Out0),
get_z_data_loop(CompSize-N, UncompSize + iolist_size(Uncompressed),
In1, Out1, Input, Output, Z)
- end.
+ end.
%% skip data descriptor if any
@@ -1298,7 +1327,7 @@ dos_date_time_to_datetime(DosDate, DosTime) ->
<<Hour:5, Min:6, Sec:5>> = <<DosTime:16>>,
<<YearFrom1980:7, Month:4, Day:5>> = <<DosDate:16>>,
{{YearFrom1980+1980, Month, Day},
- {Hour, Min, Sec}}.
+ {Hour, Min, Sec}}.
dos_date_time_from_datetime({{Year, Month, Day}, {Hour, Min, Sec}}) ->
YearFrom1980 = Year-1980,
@@ -1319,7 +1348,6 @@ unix_extra_field_and_var_from_bin(<<TSize:16/little,
Var};
unix_extra_field_and_var_from_bin(_) ->
throw(bad_unix_extra_field).
-
%% A pwrite-like function for iolists (used by memory-option)
@@ -1478,6 +1506,8 @@ local_file_header_from_bin(_) ->
%% io functions
binary_io({file_info, {_Filename, _B, #file_info{} = FI}}, _A) ->
FI;
+binary_io({file_info, {_Filename, #file_info{} = FI, _B}}, _A) ->
+ FI;
binary_io({file_info, {_Filename, B}}, A) ->
binary_io({file_info, B}, A);
binary_io({file_info, B}, _) ->
@@ -1493,9 +1523,11 @@ binary_io({file_info, B}, _) ->
links = 1, major_device = 0,
minor_device = 0, inode = 0,
uid = 0, gid = 0};
-binary_io({open, {_Filename, B, _FI}, _Opts}, _) ->
+binary_io({open, {_Filename, B, _FI}, _Opts}, _) when is_binary(B) ->
+ {0, B};
+binary_io({open, {_Filename, _FI, B}, _Opts}, _) when is_binary(B) ->
{0, B};
-binary_io({open, {_Filename, B}, _Opts}, _) ->
+binary_io({open, {_Filename, B}, _Opts}, _) when is_binary(B) ->
{0, B};
binary_io({open, B, _Opts}, _) when is_binary(B) ->
{0, B};
diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile
index 9beac93eb8..3bbd9ce318 100644
--- a/lib/stdlib/test/Makefile
+++ b/lib/stdlib/test/Makefile
@@ -9,6 +9,8 @@ MODULES= \
array_SUITE \
base64_SUITE \
beam_lib_SUITE \
+ binary_module_SUITE \
+ binref \
c_SUITE \
calendar_SUITE \
dets_SUITE \
diff --git a/lib/stdlib/test/binary_module_SUITE.erl b/lib/stdlib/test/binary_module_SUITE.erl
new file mode 100644
index 0000000000..e4cdcf6125
--- /dev/null
+++ b/lib/stdlib/test/binary_module_SUITE.erl
@@ -0,0 +1,1323 @@
+-module(binary_module_SUITE).
+
+-export([all/1, interesting/1,random_ref_comp/1,random_ref_sr_comp/1,
+ random_ref_fla_comp/1,parts/1, bin_to_list/1, list_to_bin/1,
+ copy/1, referenced/1,guard/1,encode_decode/1,badargs/1,longest_common_trap/1]).
+
+-export([random_number/1, make_unaligned/1]).
+
+
+
+%%-define(STANDALONE,1).
+
+-ifdef(STANDALONE).
+
+-define(line,erlang:display({?MODULE,?LINE}),).
+
+-else.
+
+-include("test_server.hrl").
+-export([init_per_testcase/2, fin_per_testcase/2]).
+% Default timetrap timeout (set in init_per_testcase).
+% Some of these testcases are really heavy...
+-define(default_timeout, ?t:minutes(20)).
+
+-endif.
+
+
+
+-ifdef(STANDALONE).
+-export([run/0]).
+
+run() ->
+ [ apply(?MODULE,X,[[]]) || X <- all(suite) ].
+
+-else.
+
+init_per_testcase(_Case, Config) ->
+ ?line Dog = ?t:timetrap(?default_timeout),
+ [{watchdog, Dog} | Config].
+
+fin_per_testcase(_Case, Config) ->
+ ?line Dog = ?config(watchdog, Config),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+-endif.
+
+all(suite) -> [interesting,random_ref_fla_comp,random_ref_sr_comp,
+ random_ref_comp,parts,bin_to_list, list_to_bin, copy,
+ referenced,guard,encode_decode,badargs,longest_common_trap].
+
+-define(MASK_ERROR(EXPR),mask_error((catch (EXPR)))).
+
+
+badargs(doc) ->
+ ["Tests various badarg exceptions in the module"];
+badargs(Config) when is_list(Config) ->
+ ?line badarg = ?MASK_ERROR(binary:compile_pattern([<<1,2,3:3>>])),
+ ?line badarg = ?MASK_ERROR(binary:compile_pattern([<<1,2,3>>|<<1,2>>])),
+ ?line badarg = ?MASK_ERROR(binary:compile_pattern(<<1,2,3:3>>)),
+ ?line badarg = ?MASK_ERROR(binary:compile_pattern(<<>>)),
+ ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3:3>>,<<1>>)),
+ ?line badarg = ?MASK_ERROR(binary:matches(<<1,2,3:3>>,<<1>>)),
+ ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>,
+ [{scope,{0,1},1}])),
+ ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>,
+ [{scape,{0,1}}])),
+ ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>,
+ [{scope,{0,1,1}}])),
+ ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>,[{scope,0,1}])),
+ ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>,[{scope,[0,1]}])),
+ ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>,
+ [{scope,{0.1,1}}])),
+ ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>,
+ [{scope,{1,1.1}}])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:match(<<1,2,3>>,<<1>>,
+ [{scope,{16#FF,
+ 16#FFFFFFFFFFFFFFFF}}])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:match(<<1,2,3>>,<<1>>,
+ [{scope,{16#FFFFFFFFFFFFFFFF,
+ -16#7FFFFFFFFFFFFFFF-1}}])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:match(<<1,2,3>>,<<1>>,
+ [{scope,{16#FFFFFFFFFFFFFFFF,
+ 16#7FFFFFFFFFFFFFFF}}])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:part(<<1,2,3>>,{16#FF,
+ 16#FFFFFFFFFFFFFFFF})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:part(<<1,2,3>>,{16#FFFFFFFFFFFFFFFF,
+ -16#7FFFFFFFFFFFFFFF-1})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:part(<<1,2,3>>,{16#FFFFFFFFFFFFFFFF,
+ 16#7FFFFFFFFFFFFFFF})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:part(make_unaligned(<<1,2,3>>),{1,1,1})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary_part(make_unaligned(<<1,2,3>>),{1,1,1})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary_part(make_unaligned(<<1,2,3>>),{16#FFFFFFFFFFFFFFFF,
+ -16#7FFFFFFFFFFFFFFF-1})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary_part(make_unaligned(<<1,2,3>>),{16#FF,
+ 16#FFFFFFFFFFFFFFFF})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary_part(make_unaligned(<<1,2,3>>),{16#FFFFFFFFFFFFFFFF,
+ 16#7FFFFFFFFFFFFFFF})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary_part(make_unaligned(<<1,2,3>>),{16#FFFFFFFFFFFFFFFFFF,
+ -16#7FFF})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary_part(make_unaligned(<<1,2,3>>),{16#FF,
+ -16#7FFF})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3>>,{16#FF,
+ 16#FFFFFFFFFFFFFFFF})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3>>,{16#FFFFFFFFFFFFFFFF,
+ -16#7FFFFFFFFFFFFFFF-1})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3>>,{16#FFFFFFFFFFFFFFFF,
+ 16#7FFFFFFFFFFFFFFF})),
+ ?line [1,2,3] =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3>>)),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3>>,[])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3>>,{1,2,3})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3>>,{1.0,1})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3>>,{1,1.0})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3:3>>,{1,1})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3:3>>)),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list([1,2,3])),
+
+ ?line nomatch =
+ ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>,[{scope,{0,0}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:match(<<1,2,3>>,{bm,<<>>},[{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:match(<<1,2,3>>,[],[{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:match(<<1,2,3>>,{ac,<<>>},[{scope,{0,1}}])),
+ ?line {bm,BMMagic} = binary:compile_pattern([<<1,2,3>>]),
+ ?line {ac,ACMagic} = binary:compile_pattern([<<1,2,3>>,<<4,5>>]),
+ ?line badarg =
+ ?MASK_ERROR(binary:match(<<1,2,3>>,{bm,ACMagic},[{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:match(<<1,2,3>>,{ac,BMMagic},[{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:match(<<1,2,3>>,
+ {bm,ets:match_spec_compile([{'_',[],['$_']}])},
+ [{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:match(<<1,2,3>>,
+ {ac,ets:match_spec_compile([{'_',[],['$_']}])},
+ [{scope,{0,1}}])),
+ ?line [] =
+ ?MASK_ERROR(binary:matches(<<1,2,3>>,<<1>>,[{scope,{0,0}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:matches(<<1,2,3>>,{bm,<<>>},[{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:matches(<<1,2,3>>,[],[{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:matches(<<1,2,3>>,{ac,<<>>},[{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:matches(<<1,2,3>>,{bm,ACMagic},[{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:matches(<<1,2,3>>,{ac,BMMagic},[{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:matches(<<1,2,3>>,
+ {bm,ets:match_spec_compile([{'_',[],['$_']}])},
+ [{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:matches(<<1,2,3>>,
+ {ac,ets:match_spec_compile([{'_',[],['$_']}])},
+ [{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:longest_common_prefix(
+ [<<0:10000,1,2,4,1:3>>,
+ <<0:10000,1,2,3>>])),
+ ?line badarg =
+ ?MASK_ERROR(binary:longest_common_suffix(
+ [<<0:10000,1,2,4,1:3>>,
+ <<0:10000,1,2,3>>])),
+ ?line badarg =
+ ?MASK_ERROR(binary:encode_unsigned(-1)),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:encode_unsigned(-16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:first(<<1,2,4,1:3>>)),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:first([1,2,4])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:last(<<1,2,4,1:3>>)),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:last([1,2,4])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:at(<<1,2,4,1:3>>,2)),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:at(<<>>,2)),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:at([1,2,4],2)),
+ ok.
+
+longest_common_trap(doc) ->
+ ["Whitebox test to force special trap conditions in longest_common_{prefix,suffix}"];
+longest_common_trap(Config) when is_list(Config) ->
+ ?line erts_debug:set_internal_state(available_internal_state,true),
+ ?line io:format("oldlimit: ~p~n",
+ [erts_debug:set_internal_state(binary_loop_limit,10)]),
+ erlang:bump_reductions(10000000),
+ ?line _ = binary:longest_common_prefix(
+ [<<0:10000,1,2,4>>,
+ <<0:10000,1,2,3>>,
+ <<0:10000,1,3,3>>,
+ <<0:10000,1,2,4>>,
+ <<0:10000,1,2,4>>,
+ <<0:10000,1,2,3>>,
+ <<0:10000,1,3,3>>,
+ <<0:10000,1,2,3>>,
+ <<0:10000,1,3,3>>,
+ <<0:10000,1,2,4>>,
+ <<0:10000,1,2,4>>,
+ <<0:10000,1,2,3>>,
+ <<0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0:10000,1,3,3>>,
+ <<0:10000,1,2,4>>]),
+ ?line _ = binary:longest_common_prefix(
+ [<<0:10000,1,2,4>>,
+ <<0:10000,1,2,3>>,
+ <<0:10000,1,3,3>>,
+ <<0:10000,1,2,4>>,
+ <<0:10000,1,2,4>>,
+ <<0:10000,1,2,3>>,
+ <<0:10000,1,3,3>>,
+ <<0:10000,1,2,3>>,
+ <<0:10000,1,3,3>>,
+ <<0:10000,1,2,4>>,
+ <<0:10000,1,2,4>>,
+ <<0:10000,1,2,3>>,
+ <<0,0,0,0,0,0,0,0,0,0,0,0,0,0>>,
+ <<0:10000,1,2,4>>]),
+ erlang:bump_reductions(10000000),
+ ?line _ = binary:longest_common_suffix(
+ [<<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,3,3,0:10000,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>,
+ <<1,2,4,0:10000>>]),
+ ?line _ = binary:longest_common_suffix(
+ [<<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<0,0,0,0,0,0,0,0,0,0,0,0,0,0>>,
+ <<1,2,4,0:10000>>]),
+ Subj = subj(),
+ Len = byte_size(Subj),
+ ?line Len = binary:longest_common_suffix(
+ [Subj,Subj,Subj]),
+ ?line io:format("limit was: ~p~n",
+ [erts_debug:set_internal_state(binary_loop_limit,
+ default)]),
+ ?line erts_debug:set_internal_state(available_internal_state,false),
+ ok.
+
+subj() ->
+ Me = self(),
+ spawn(fun() ->
+ X0 = iolist_to_binary([
+ "1234567890",
+ %lists:seq(16#21, 16#7e),
+ lists:duplicate(100, $x)
+ ]),
+ Me ! X0,
+ receive X -> X end
+ end),
+ X0 = receive A -> A end,
+ <<X1:32/binary,_/binary>> = X0,
+ Subject= <<X1/binary>>,
+ Subject.
+
+
+interesting(doc) ->
+ ["Try some interesting patterns"];
+interesting(Config) when is_list(Config) ->
+ X = do_interesting(binary),
+ X = do_interesting(binref).
+
+do_interesting(Module) ->
+ ?line {0,4} = Module:match(<<"123456">>,
+ Module:compile_pattern([<<"12">>,<<"1234">>,
+ <<"23">>,<<"3">>,
+ <<"34">>,<<"456">>,
+ <<"45">>,<<"6">>])),
+ ?line [{0,4},{5,1}] = Module:matches(<<"123456">>,
+ Module:compile_pattern([<<"12">>,<<"1234">>,
+ <<"23">>,<<"3">>,
+ <<"34">>,<<"456">>,
+ <<"45">>,<<"6">>])),
+ ?line [{0,4}] = Module:matches(<<"123456">>,
+ Module:compile_pattern([<<"12">>,<<"1234">>,
+ <<"23">>,<<"3">>,
+ <<"34">>,<<"456">>,
+ <<"45">>])),
+ ?line [{0,2},{2,2}] = Module:matches(<<"123456">>,
+ Module:compile_pattern([<<"12">>,
+ <<"23">>,<<"3">>,
+ <<"34">>,<<"456">>,
+ <<"45">>])),
+ ?line {1,4} = Module:match(<<"123456">>,
+ Module:compile_pattern([<<"34">>,<<"34">>,
+ <<"12347">>,<<"2345">>])),
+ ?line [{1,4}] = Module:matches(<<"123456">>,
+ Module:compile_pattern([<<"34">>,<<"34">>,
+ <<"12347">>,<<"2345">>])),
+ ?line [{2,2}] = Module:matches(<<"123456">>,
+ Module:compile_pattern([<<"34">>,<<"34">>,
+ <<"12347">>,<<"2346">>])),
+
+ ?line {0,4} = Module:match(<<"123456">>,
+ [<<"12">>,<<"1234">>,
+ <<"23">>,<<"3">>,
+ <<"34">>,<<"456">>,
+ <<"45">>,<<"6">>]),
+ ?line [{0,4},{5,1}] = Module:matches(<<"123456">>,
+ [<<"12">>,<<"1234">>,
+ <<"23">>,<<"3">>,
+ <<"34">>,<<"456">>,
+ <<"45">>,<<"6">>]),
+ ?line [{0,4}] = Module:matches(<<"123456">>,
+ [<<"12">>,<<"1234">>,
+ <<"23">>,<<"3">>,
+ <<"34">>,<<"456">>,
+ <<"45">>]),
+ ?line [{0,2},{2,2}] = Module:matches(<<"123456">>,
+ [<<"12">>,
+ <<"23">>,<<"3">>,
+ <<"34">>,<<"456">>,
+ <<"45">>]),
+ ?line {1,4} = Module:match(<<"123456">>,
+ [<<"34">>,<<"34">>,
+ <<"12347">>,<<"2345">>]),
+ ?line [{1,4}] = Module:matches(<<"123456">>,
+ [<<"34">>,<<"34">>,
+ <<"12347">>,<<"2345">>]),
+ ?line [{2,2}] = Module:matches(<<"123456">>,
+ [<<"34">>,<<"34">>,
+ <<"12347">>,<<"2346">>]),
+ ?line nomatch = Module:match(<<1,2,3,4>>,<<2>>,[{scope,{0,1}}]),
+ ?line {1,1} = Module:match(<<1,2,3,4>>,<<2>>,[{scope,{0,2}}]),
+ ?line nomatch = Module:match(<<1,2,3,4>>,<<2,3>>,[{scope,{0,2}}]),
+ ?line {1,2} = Module:match(<<1,2,3,4>>,<<2,3>>,[{scope,{0,3}}]),
+ ?line {1,2} = Module:match(<<1,2,3,4>>,<<2,3>>,[{scope,{0,4}}]),
+ ?line badarg = ?MASK_ERROR(Module:match(<<1,2,3,4>>,<<2,3>>,
+ [{scope,{0,5}}])),
+ ?line {1,2} = Module:match(<<1,2,3,4>>,<<2,3>>,[{scope,{4,-4}}]),
+ ?line {0,3} = Module:match(<<1,2,3,4>>,<<1,2,3>>,[{scope,{4,-4}}]),
+ ?line {0,4} = Module:match(<<1,2,3,4>>,<<1,2,3,4>>,[{scope,{4,-4}}]),
+ ?line badarg = ?MASK_ERROR(Module:match(<<1,2,3,4>>,<<1,2,3,4>>,
+ [{scope,{3,-4}}])),
+ ?line [] = Module:matches(<<1,2,3,4>>,<<2>>,[{scope,{0,1}}]),
+ ?line [{1,1}] = Module:matches(<<1,2,3,4>>,[<<2>>,<<3>>],[{scope,{0,2}}]),
+ ?line [] = Module:matches(<<1,2,3,4>>,<<2,3>>,[{scope,{0,2}}]),
+ ?line [{1,2}] = Module:matches(<<1,2,3,4>>,<<2,3>>,[{scope,{0,3}}]),
+ ?line [{1,2}] = Module:matches(<<1,2,3,4>>,<<2,3>>,[{scope,{0,4}}]),
+ ?line [{1,2}] = Module:matches(<<1,2,3,4>>,[<<2,3>>,<<4>>],
+ [{scope,{0,3}}]),
+ ?line [{1,2},{3,1}] = Module:matches(<<1,2,3,4>>,[<<2,3>>,<<4>>],
+ [{scope,{0,4}}]),
+ ?line badarg = ?MASK_ERROR(Module:matches(<<1,2,3,4>>,<<2,3>>,
+ [{scope,{0,5}}])),
+ ?line [{1,2}] = Module:matches(<<1,2,3,4>>,<<2,3>>,[{scope,{4,-4}}]),
+ ?line [{1,2},{3,1}] = Module:matches(<<1,2,3,4>>,[<<2,3>>,<<4>>],
+ [{scope,{4,-4}}]),
+ ?line [{0,3}] = Module:matches(<<1,2,3,4>>,<<1,2,3>>,[{scope,{4,-4}}]),
+ ?line [{0,4}] = Module:matches(<<1,2,3,4>>,<<1,2,3,4>>,[{scope,{4,-4}}]),
+ ?line badarg = ?MASK_ERROR(Module:matches(<<1,2,3,4>>,<<1,2,3,4>>,
+ [{scope,{3,-4}}])),
+ ?line badarg = ?MASK_ERROR(Module:matches(<<1,2,3,4>>,[<<1,2,3,4>>],
+ [{scope,{3,-4}}])),
+ ?line [<<1,2,3>>,<<6,7,8>>] = Module:split(<<1,2,3,4,5,6,7,8>>,<<4,5>>),
+ ?line [<<1,2,3>>,<<6,7,8>>] = Module:split(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>]),
+ ?line [<<1,2,3>>,<<6>>,<<8>>] = Module:split(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>],[global]),
+ ?line [<<1,2,3>>,<<6>>,<<>>,<<>>] = Module:split(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],
+ [global]),
+ ?line [<<1,2,3>>,<<6>>] = Module:split(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],
+ [global,trim]),
+ ?line [<<1,2,3,4,5,6,7,8>>] = Module:split(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],
+ [global,trim,{scope,{0,4}}]),
+ ?line [<<1,2,3>>,<<6,7,8>>] = Module:split(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],
+ [global,trim,{scope,{0,5}}]),
+ ?line badarg = ?MASK_ERROR(
+ Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],<<99>>,
+ [global,trim,{scope,{0,5}}])),
+ ?line <<1,2,3,99,6,7,8>> = Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],<<99>>,[]),
+ ?line <<1,2,3,99,6,99,99>> = Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],<<99>>,
+ [global]),
+ ?line <<1,2,3,99,6,7,8>> = Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],<<99>>,
+ [global,{scope,{0,5}}]),
+ ?line <<1,2,3,99,6,7,8>> = Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],<<99>>,
+ [global,{scope,{0,5}}]),
+ ?line <<1,2,3,99,6,7,8>> = Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],<<99>>,
+ [global,{scope,{0,5}}]),
+ ?line badarg = ?MASK_ERROR(Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],<<99>>,
+ [global,{scope,{0,5}},
+ {insert,1}])),
+ ?line <<1,2,3,99,4,5,6,7,8>> = Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],<<99>>,
+ [global,{scope,{0,5}},
+ {insert_replaced,1}]),
+ ?line <<1,2,3,9,4,5,9,6,7,8>> = Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],
+ <<9,9>>,
+ [global,{scope,{0,5}},
+ {insert_replaced,1}]),
+ ?line badarg = ?MASK_ERROR(Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],<<>>,
+ [global,{scope,{0,5}},
+ {insert_replaced,1}])),
+ ?line 2 = Module:longest_common_prefix([<<1,2,4>>,<<1,2,3>>]),
+ ?line 2 = Module:longest_common_prefix([<<1,2,4>>,<<1,2>>]),
+ ?line 1 = Module:longest_common_prefix([<<1,2,4>>,<<1>>]),
+ ?line 0 = Module:longest_common_prefix([<<1,2,4>>,<<>>]),
+ ?line 1 = Module:longest_common_prefix([<<1,2,4>>,<<1,2,3>>,<<1,3,3>>]),
+ ?line 1 = Module:longest_common_prefix([<<1,2,4>>,<<1,2,3>>,<<1,3,3>>,<<1,2,4>>]),
+ ?line 1251 = Module:longest_common_prefix([<<0:10000,1,2,4>>,
+ <<0:10000,1,2,3>>,
+ <<0:10000,1,3,3>>,
+ <<0:10000,1,2,4>>]),
+ ?line 12501 = Module:longest_common_prefix([<<0:100000,1,2,4>>,
+ <<0:100000,1,2,3>>,
+ <<0:100000,1,3,3>>,
+ <<0:100000,1,2,4>>]),
+ ?line 1251 = Module:longest_common_prefix(
+ [make_unaligned(<<0:10000,1,2,4>>),
+ <<0:10000,1,2,3>>,
+ make_unaligned(<<0:10000,1,3,3>>),
+ <<0:10000,1,2,4>>]),
+ ?line 12501 = Module:longest_common_prefix(
+ [<<0:100000,1,2,4>>,
+ make_unaligned(<<0:100000,1,2,3>>),
+ <<0:100000,1,3,3>>,
+ make_unaligned(<<0:100000,1,2,4>>)]),
+ ?line 1250001 = Module:longest_common_prefix([<<0:10000000,1,2,4>>,
+ <<0:10000000,1,2,3>>,
+ <<0:10000000,1,3,3>>,
+ <<0:10000000,1,2,4>>]),
+ if % Too cruel for the reference implementation
+ Module =:= binary ->
+ ?line erts_debug:set_internal_state(available_internal_state,true),
+ ?line io:format("oldlimit: ~p~n",
+ [erts_debug:set_internal_state(
+ binary_loop_limit,100)]),
+ ?line 1250001 = Module:longest_common_prefix(
+ [<<0:10000000,1,2,4>>,
+ <<0:10000000,1,2,3>>,
+ <<0:10000000,1,3,3>>,
+ <<0:10000000,1,2,4>>]),
+ ?line io:format("limit was: ~p~n",
+ [erts_debug:set_internal_state(binary_loop_limit,
+ default)]),
+ ?line erts_debug:set_internal_state(available_internal_state,
+ false);
+ true ->
+ ok
+ end,
+ ?line 1 = Module:longest_common_suffix([<<0:100000000,1,2,4,5>>,
+ <<0:100000000,1,2,3,5>>,
+ <<0:100000000,1,3,3,5>>,
+ <<0:100000000,1,2,4,5>>]),
+ ?line 1 = Module:longest_common_suffix([<<1,2,4,5>>,
+ <<0:100000000,1,2,3,5>>,
+ <<0:100000000,1,3,3,5>>,
+ <<0:100000000,1,2,4,5>>]),
+ ?line 1 = Module:longest_common_suffix([<<1,2,4,5,5>>,<<5,5>>,
+ <<0:100000000,1,3,3,5,5>>,
+ <<0:100000000,1,2,4,5>>]),
+ ?line 0 = Module:longest_common_suffix([<<1,2,4,5,5>>,<<5,5>>,
+ <<0:100000000,1,3,3,5,5>>,
+ <<0:100000000,1,2,4>>]),
+ ?line 2 = Module:longest_common_suffix([<<1,2,4,5,5>>,<<5,5>>,
+ <<0:100000000,1,3,3,5,5>>,
+ <<0:100000000,1,2,4,5,5>>]),
+ ?line 1 = Module:longest_common_suffix([<<1,2,4,5,5>>,<<5>>,
+ <<0:100000000,1,3,3,5,5>>,
+ <<0:100000000,1,2,4,5,5>>]),
+ ?line 0 = Module:longest_common_suffix([<<1,2,4,5,5>>,<<>>,
+ <<0:100000000,1,3,3,5,5>>,
+ <<0:100000000,1,2,4,5,5>>]),
+ ?line 0 = Module:longest_common_suffix([<<>>,<<0:100000000,1,3,3,5,5>>,
+ <<0:100000000,1,2,4,5,5>>]),
+ ?line 0 = Module:longest_common_suffix([<<>>,<<0:100000000,1,3,3,5,5>>,
+ <<0:100000000,1,2,4,5,5>>]),
+ ?line 2 = Module:longest_common_suffix([<<5,5>>,<<0:100000000,1,3,3,5,5>>,
+ <<0:100000000,1,2,4,5,5>>]),
+ ?line 2 = Module:longest_common_suffix([<<5,5>>,<<5,5>>,<<4,5,5>>]),
+ ?line 2 = Module:longest_common_suffix([<<5,5>>,<<5,5>>,<<5,5>>]),
+ ?line 3 = Module:longest_common_suffix([<<4,5,5>>,<<4,5,5>>,<<4,5,5>>]),
+ ?line 0 = Module:longest_common_suffix([<<>>]),
+ ?line badarg = ?MASK_ERROR(Module:longest_common_suffix([])),
+ ?line badarg = ?MASK_ERROR(Module:longest_common_suffix([apa])),
+ ?line badarg = ?MASK_ERROR(Module:longest_common_suffix([[<<>>]])),
+ ?line badarg = ?MASK_ERROR(Module:longest_common_suffix([[<<0>>,
+ <<1:9>>]])),
+ ?line 0 = Module:longest_common_prefix([<<>>]),
+ ?line badarg = ?MASK_ERROR(Module:longest_common_prefix([])),
+ ?line badarg = ?MASK_ERROR(Module:longest_common_prefix([apa])),
+ ?line badarg = ?MASK_ERROR(Module:longest_common_prefix([[<<>>]])),
+ ?line badarg = ?MASK_ERROR(Module:longest_common_prefix([[<<0>>,
+ <<1:9>>]])),
+
+ ?line <<1:6,Bin:3/binary,_:2>> = <<1:6,1,2,3,1:2>>,
+ ?line <<1,2,3>> = Bin,
+ ?line 1 = Module:first(Bin),
+ ?line 1 = Module:first(<<1>>),
+ ?line 1 = Module:first(<<1,2,3>>),
+ ?line badarg = ?MASK_ERROR(Module:first(<<>>)),
+ ?line badarg = ?MASK_ERROR(Module:first(apa)),
+ ?line 3 = Module:last(Bin),
+ ?line 1 = Module:last(<<1>>),
+ ?line 3 = Module:last(<<1,2,3>>),
+ ?line badarg = ?MASK_ERROR(Module:last(<<>>)),
+ ?line badarg = ?MASK_ERROR(Module:last(apa)),
+ ?line 1 = Module:at(Bin,0),
+ ?line 1 = Module:at(<<1>>,0),
+ ?line 1 = Module:at(<<1,2,3>>,0),
+ ?line 2 = Module:at(<<1,2,3>>,1),
+ ?line 3 = Module:at(<<1,2,3>>,2),
+ ?line badarg = ?MASK_ERROR(Module:at(<<1,2,3>>,3)),
+ ?line badarg = ?MASK_ERROR(Module:at(<<1,2,3>>,-1)),
+ ?line badarg = ?MASK_ERROR(Module:at(<<1,2,3>>,apa)),
+ ?line "hejsan" = [ Module:at(<<"hejsan">>,I) || I <- lists:seq(0,5) ],
+
+ ?line badarg = ?MASK_ERROR(Module:bin_to_list(<<1,2,3>>,3,-4)),
+ ?line [1,2,3] = ?MASK_ERROR(Module:bin_to_list(<<1,2,3>>,3,-3)),
+
+ ?line badarg = ?MASK_ERROR(Module:decode_unsigned(<<1,2,1:2>>,big)),
+ ?line badarg = ?MASK_ERROR(Module:decode_unsigned(<<1,2,1:2>>,little)),
+ ?line badarg = ?MASK_ERROR(Module:decode_unsigned(apa)),
+ ?line badarg = ?MASK_ERROR(Module:decode_unsigned(125,little)),
+ ?line 0 = ?MASK_ERROR(Module:decode_unsigned(<<>>,little)),
+ ?line 0 = ?MASK_ERROR(Module:decode_unsigned(<<>>,big)),
+ ?line 0 = ?MASK_ERROR(Module:decode_unsigned(<<0>>,little)),
+ ?line 0 = ?MASK_ERROR(Module:decode_unsigned(<<0>>,big)),
+ ?line 0 = ?MASK_ERROR(Module:decode_unsigned(make_unaligned(<<0>>),
+ little)),
+ ?line 0 = ?MASK_ERROR(Module:decode_unsigned(make_unaligned(<<0>>),big)),
+ ?line badarg = ?MASK_ERROR(Module:encode_unsigned(apa)),
+ ?line badarg = ?MASK_ERROR(Module:encode_unsigned(125.3,little)),
+ ?line badarg = ?MASK_ERROR(Module:encode_unsigned({1},little)),
+ ?line badarg = ?MASK_ERROR(Module:encode_unsigned([1],little)),
+ ?line <<0>> = ?MASK_ERROR(Module:encode_unsigned(0,little)),
+ ?line <<0>> = ?MASK_ERROR(Module:encode_unsigned(0,big)),
+ ok.
+
+encode_decode(doc) ->
+ ["test binary:encode_unsigned/1,2 and binary:decode_unsigned/1,2"];
+encode_decode(Config) when is_list(Config) ->
+ ?line random:seed({1271,769940,559934}),
+ ?line ok = encode_decode_loop({1,200},1000), % Need to be long enough
+ % to create offheap binaries
+ ok.
+
+encode_decode_loop(_Range,0) ->
+ ok;
+encode_decode_loop(Range, X) ->
+ ?line N = random_number(Range),
+ ?line A = binary:encode_unsigned(N),
+ ?line B = binary:encode_unsigned(N,big),
+ ?line C = binref:encode_unsigned(N),
+ ?line D = binref:encode_unsigned(N,big),
+ ?line E = binary:encode_unsigned(N,little),
+ ?line F = binref:encode_unsigned(N,little),
+ ?line G = binary:decode_unsigned(A),
+ ?line H = binary:decode_unsigned(A,big),
+ ?line I = binref:decode_unsigned(A),
+ ?line J = binary:decode_unsigned(E,little),
+ ?line K = binref:decode_unsigned(E,little),
+ ?line L = binary:decode_unsigned(make_unaligned(A)),
+ ?line M = binary:decode_unsigned(make_unaligned(E),little),
+ ?line PaddedBig = <<0:48,A/binary>>,
+ ?line PaddedLittle = <<E/binary,0:48>>,
+ ?line O = binary:decode_unsigned(PaddedBig),
+ ?line P = binary:decode_unsigned(make_unaligned(PaddedBig)),
+ ?line Q = binary:decode_unsigned(PaddedLittle,little),
+ ?line R = binary:decode_unsigned(make_unaligned(PaddedLittle),little),
+ ?line S = binref:decode_unsigned(PaddedLittle,little),
+ ?line T = binref:decode_unsigned(PaddedBig),
+ case (((A =:= B) and (B =:= C) and (C =:= D)) and
+ ((E =:= F)) and
+ ((N =:= G) and (G =:= H) and (H =:= I) and
+ (I =:= J) and (J =:= K) and (K =:= L) and (L =:= M)) and
+ ((M =:= O) and (O =:= P) and (P =:= Q) and (Q =:= R) and
+ (R =:= S) and (S =:= T)))of
+ true ->
+ encode_decode_loop(Range,X-1);
+ _ ->
+ io:format("Failed to encode/decode ~w~n(Results ~p)~n",
+ [N,[A,B,C,D,E,F,G,H,I,J,K,L,M,x,O,P,Q,R,S,T]]),
+ exit(mismatch)
+ end.
+
+guard(doc) ->
+ ["Smoke test of the guard BIFs binary_part/2,3"];
+guard(Config) when is_list(Config) ->
+ {comment, "Guard tests are run in emulator test suite"}.
+
+referenced(doc) ->
+ ["Test refernced_byte_size/1 bif."];
+referenced(Config) when is_list(Config) ->
+ ?line badarg = ?MASK_ERROR(binary:referenced_byte_size([])),
+ ?line badarg = ?MASK_ERROR(binary:referenced_byte_size(apa)),
+ ?line badarg = ?MASK_ERROR(binary:referenced_byte_size({})),
+ ?line badarg = ?MASK_ERROR(binary:referenced_byte_size(1)),
+ ?line A = <<1,2,3>>,
+ ?line B = binary:copy(A,1000),
+ ?line 3 = binary:referenced_byte_size(A),
+ ?line 3000 = binary:referenced_byte_size(B),
+ ?line <<_:8,C:2/binary>> = A,
+ ?line 3 = binary:referenced_byte_size(C),
+ ?line 2 = binary:referenced_byte_size(binary:copy(C)),
+ ?line <<_:7,D:2/binary,_:1>> = A,
+ ?line 2 = binary:referenced_byte_size(binary:copy(D)),
+ ?line 3 = binary:referenced_byte_size(D),
+ ?line <<_:8,E:2/binary,_/binary>> = B,
+ ?line 3000 = binary:referenced_byte_size(E),
+ ?line 2 = binary:referenced_byte_size(binary:copy(E)),
+ ?line <<_:7,F:2/binary,_:1,_/binary>> = B,
+ ?line 2 = binary:referenced_byte_size(binary:copy(F)),
+ ?line 3000 = binary:referenced_byte_size(F),
+ ok.
+
+
+
+list_to_bin(doc) ->
+ ["Test list_to_bin/1 bif"];
+list_to_bin(Config) when is_list(Config) ->
+ %% Just some smoke_tests first, then go nuts with random cases
+ ?line badarg = ?MASK_ERROR(binary:list_to_bin({})),
+ ?line badarg = ?MASK_ERROR(binary:list_to_bin(apa)),
+ ?line badarg = ?MASK_ERROR(binary:list_to_bin(<<"apa">>)),
+ F1 = fun(L) ->
+ ?MASK_ERROR(binref:list_to_bin(L))
+ end,
+ F2 = fun(L) ->
+ ?MASK_ERROR(binary:list_to_bin(L))
+ end,
+ ?line random_iolist:run(1000,F1,F2),
+ ok.
+
+copy(doc) ->
+ ["Test copy/1,2 bif's"];
+copy(Config) when is_list(Config) ->
+ ?line <<1,2,3>> = binary:copy(<<1,2,3>>),
+ ?line RS = random_string({1,10000}),
+ ?line RS = RS2 = binary:copy(RS),
+ ?line false = erts_debug:same(RS,RS2),
+ ?line <<>> = ?MASK_ERROR(binary:copy(<<1,2,3>>,0)),
+ ?line badarg = ?MASK_ERROR(binary:copy(<<1,2,3:3>>,2)),
+ ?line badarg = ?MASK_ERROR(binary:copy([],0)),
+ ?line <<>> = ?MASK_ERROR(binary:copy(<<>>,0)),
+ ?line badarg = ?MASK_ERROR(binary:copy(<<1,2,3>>,1.0)),
+ ?line badarg = ?MASK_ERROR(binary:copy(<<1,2,3>>,
+ 16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)),
+ ?line <<>> = binary:copy(<<>>,10000),
+ ?line random:seed({1271,769940,559934}),
+ ?line ok = random_copy(3000),
+ ?line erts_debug:set_internal_state(available_internal_state,true),
+ ?line io:format("oldlimit: ~p~n",
+ [erts_debug:set_internal_state(binary_loop_limit,10)]),
+ ?line Subj = subj(),
+ ?line XX = binary:copy(Subj,1000),
+ ?line XX = binref:copy(Subj,1000),
+ ?line ok = random_copy(1000),
+ ?line kill_copy_loop(1000),
+ ?line io:format("limit was: ~p~n",
+ [erts_debug:set_internal_state(binary_loop_limit,
+ default)]),
+ ?line erts_debug:set_internal_state(available_internal_state,false),
+ ok.
+
+kill_copy_loop(0) ->
+ ok;
+kill_copy_loop(N) ->
+ {Pid,Ref} = spawn_monitor(fun() ->
+ ok = random_copy(1000)
+ end),
+ receive
+ after 10 ->
+ ok
+ end,
+ exit(Pid,kill),
+ receive
+ {'DOWN',Ref,process,Pid,_} ->
+ kill_copy_loop(N-1)
+ after 1000 ->
+ exit(did_not_die)
+ end.
+
+random_copy(0) ->
+ ok;
+random_copy(N) ->
+ Str = random_string({0,N}),
+ Num = random:uniform(N div 10+1),
+ A = ?MASK_ERROR(binary:copy(Str,Num)),
+ B = ?MASK_ERROR(binref:copy(Str,Num)),
+ C = ?MASK_ERROR(binary:copy(make_unaligned(Str),Num)),
+ case {(A =:= B), (B =:= C)} of
+ {true,true} ->
+ random_copy(N-1);
+ _ ->
+ io:format("Failed to pick copy ~s ~p times~n",
+ [Str,Num]),
+ io:format("A:~p,~nB:~p,~n,C:~p.~n",
+ [A,B,C]),
+ exit(mismatch)
+ end.
+
+bin_to_list(doc) ->
+ ["Test bin_to_list/1,2,3 bif's"];
+bin_to_list(Config) when is_list(Config) ->
+ %% Just some smoke_tests first, then go nuts with random cases
+ ?line X = <<1,2,3,4,0:1000000,5>>,
+ ?line Y = make_unaligned(X),
+ ?line LX = binary:bin_to_list(X),
+ ?line LX = binary:bin_to_list(X,0,byte_size(X)),
+ ?line LX = binary:bin_to_list(X,byte_size(X),-byte_size(X)),
+ ?line LX = binary:bin_to_list(X,{0,byte_size(X)}),
+ ?line LX = binary:bin_to_list(X,{byte_size(X),-byte_size(X)}),
+ ?line LY = binary:bin_to_list(Y),
+ ?line LY = binary:bin_to_list(Y,0,byte_size(Y)),
+ ?line LY = binary:bin_to_list(Y,byte_size(Y),-byte_size(Y)),
+ ?line LY = binary:bin_to_list(Y,{0,byte_size(Y)}),
+ ?line LY = binary:bin_to_list(Y,{byte_size(Y),-byte_size(Y)}),
+ ?line 1 = hd(LX),
+ ?line 5 = lists:last(LX),
+ ?line 1 = hd(LY),
+ ?line 5 = lists:last(LY),
+ ?line X = list_to_binary(LY),
+ ?line Y = list_to_binary(LY),
+ ?line X = list_to_binary(LY),
+ ?line [5] = lists:nthtail(byte_size(X)-1,LX),
+ ?line [0,5] = lists:nthtail(byte_size(X)-2,LX),
+ ?line [0,5] = lists:nthtail(byte_size(Y)-2,LY),
+ ?line random:seed({1271,769940,559934}),
+ ?line ok = random_bin_to_list(5000),
+ ok.
+
+random_bin_to_list(0) ->
+ ok;
+random_bin_to_list(N) ->
+ Str = random_string({1,N}),
+ Parts0 = random_parts(10,N),
+ Parts1 = Parts0 ++ [ {X+Y,-Y} || {X,Y} <- Parts0 ],
+ [ begin
+ try
+ true = ?MASK_ERROR(binary:bin_to_list(Str,Z)) =:=
+ ?MASK_ERROR(binref:bin_to_list(Str,Z)),
+ true = ?MASK_ERROR(binary:bin_to_list(Str,Z)) =:=
+ ?MASK_ERROR(binary:bin_to_list(make_unaligned(Str),Z))
+ catch
+ _:_ ->
+ io:format("Error, Str = <<\"~s\">>.~nZ = ~p.~n",
+ [Str,Z]),
+ exit(badresult)
+ end
+ end || Z <- Parts1 ],
+ [ begin
+ try
+ true = ?MASK_ERROR(binary:bin_to_list(Str,A,B)) =:=
+ ?MASK_ERROR(binref:bin_to_list(Str,A,B)),
+ true = ?MASK_ERROR(binary:bin_to_list(Str,A,B)) =:=
+ ?MASK_ERROR(binary:bin_to_list(make_unaligned(Str),A,B))
+ catch
+ _:_ ->
+ io:format("Error, Str = <<\"~s\">>.~nA = ~p.~nB = ~p.~n",
+ [Str,A,B]),
+ exit(badresult)
+ end
+ end || {A,B} <- Parts1 ],
+ random_bin_to_list(N-1).
+
+parts(doc) ->
+ ["Test the part/2,3 bif's"];
+parts(Config) when is_list(Config) ->
+ %% Some simple smoke tests to begin with
+ ?line Simple = <<1,2,3,4,5,6,7,8>>,
+ ?line <<1,2>> = binary:part(Simple,0,2),
+ ?line <<1,2>> = binary:part(Simple,{0,2}),
+ ?line Simple = binary:part(Simple,0,8),
+ ?line Simple = binary:part(Simple,{0,8}),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,0,9)),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{0,9})),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,1,8)),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{1,8})),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{3,-4})),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{3.0,1})),
+ ?line badarg = ?MASK_ERROR(
+ binary:part(Simple,{16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+ ,1})),
+ ?line <<2,3,4,5,6,7,8>> = binary:part(Simple,{1,7}),
+ ?line <<2,3,4,5,6,7,8>> = binary:part(Simple,{8,-7}),
+ ?line Simple = binary:part(Simple,{8,-8}),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{1,-8})),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{8,-9})),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{0,-1})),
+ ?line <<>> = binary:part(Simple,{8,0}),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{9,0})),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{-1,0})),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{7,2})),
+ ?line <<8>> = binary:part(Simple,{7,1}),
+ ?line random:seed({1271,769940,559934}),
+ ?line random_parts(5000),
+ ok.
+
+
+random_parts(0) ->
+ ok;
+random_parts(N) ->
+ Str = random_string({1,N}),
+ Parts0 = random_parts(10,N),
+ Parts1 = Parts0 ++ [ {X+Y,-Y} || {X,Y} <- Parts0 ],
+ [ begin
+ true = ?MASK_ERROR(binary:part(Str,Z)) =:=
+ ?MASK_ERROR(binref:part(Str,Z)),
+ true = ?MASK_ERROR(binary:part(Str,Z)) =:=
+ ?MASK_ERROR(erlang:binary_part(Str,Z)),
+ true = ?MASK_ERROR(binary:part(Str,Z)) =:=
+ ?MASK_ERROR(binary:part(make_unaligned(Str),Z))
+ end || Z <- Parts1 ],
+ random_parts(N-1).
+
+random_parts(0,_) ->
+ [];
+random_parts(X,N) ->
+ Pos = random:uniform(N),
+ Len = random:uniform((Pos * 12) div 10),
+ [{Pos,Len} | random_parts(X-1,N)].
+
+random_ref_comp(doc) ->
+ ["Test pseudorandomly generated cases against reference imlementation"];
+random_ref_comp(Config) when is_list(Config) ->
+ ?line put(success_counter,0),
+ ?line random:seed({1271,769940,559934}),
+ ?line do_random_match_comp(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ?line do_random_match_comp2(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ?line do_random_match_comp3(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ?line do_random_match_comp4(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ?line do_random_matches_comp(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ?line do_random_matches_comp2(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ?line do_random_matches_comp3(5,{1,40},{30,1000}),
+ ?line erts_debug:set_internal_state(available_internal_state,true),
+ ?line io:format("oldlimit: ~p~n",[ erts_debug:set_internal_state(binary_loop_limit,100)]),
+ ?line do_random_match_comp(5000,{1,40},{30,1000}),
+ ?line do_random_matches_comp3(5,{1,40},{30,1000}),
+ ?line io:format("limit was: ~p~n",[ erts_debug:set_internal_state(binary_loop_limit,default)]),
+ ?line erts_debug:set_internal_state(available_internal_state,false),
+ ok.
+
+random_ref_sr_comp(doc) ->
+ ["Test pseudorandomly generated cases against reference imlementation of split and replace"];
+random_ref_sr_comp(Config) when is_list(Config) ->
+ ?line put(success_counter,0),
+ ?line random:seed({1271,769940,559934}),
+ ?line do_random_split_comp(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ?line do_random_replace_comp(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ?line do_random_split_comp2(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ?line do_random_replace_comp2(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ok.
+random_ref_fla_comp(doc) ->
+ ["Test pseudorandomly generated cases against reference imlementation of split and replace"];
+random_ref_fla_comp(Config) when is_list(Config) ->
+ ?line put(success_counter,0),
+ ?line random:seed({1271,769940,559934}),
+ ?line do_random_first_comp(5000,{1,1000}),
+ ?line do_random_last_comp(5000,{1,1000}),
+ ?line do_random_at_comp(5000,{1,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ok.
+
+do_random_first_comp(0,_) ->
+ ok;
+do_random_first_comp(N,Range) ->
+ S = random_string(Range),
+ A = ?MASK_ERROR(binref:first(S)),
+ B = ?MASK_ERROR(binary:first(S)),
+ C = ?MASK_ERROR(binary:first(make_unaligned(S))),
+ case {(A =:= B), (B =:= C)} of
+ {true,true} ->
+ do_random_first_comp(N-1,Range);
+ _ ->
+ io:format("Failed to pick first of ~s~n",
+ [S]),
+ io:format("A:~p,~nB:~p,~n,C:~p.~n",
+ [A,B,C]),
+ exit(mismatch)
+ end.
+
+do_random_last_comp(0,_) ->
+ ok;
+do_random_last_comp(N,Range) ->
+ S = random_string(Range),
+ A = ?MASK_ERROR(binref:last(S)),
+ B = ?MASK_ERROR(binary:last(S)),
+ C = ?MASK_ERROR(binary:last(make_unaligned(S))),
+ case {(A =:= B), (B =:= C)} of
+ {true,true} ->
+ do_random_last_comp(N-1,Range);
+ _ ->
+ io:format("Failed to pick last of ~s~n",
+ [S]),
+ io:format("A:~p,~nB:~p,~n,C:~p.~n",
+ [A,B,C]),
+ exit(mismatch)
+ end.
+do_random_at_comp(0,_) ->
+ ok;
+do_random_at_comp(N,{Min,Max}=Range) ->
+ S = random_string(Range),
+ XMax = Min + ((Max - Min) * 3) div 4,
+ Pos = random_length({Min,XMax}), %% some out of range
+ A = ?MASK_ERROR(binref:at(S,Pos)),
+ B = ?MASK_ERROR(binary:at(S,Pos)),
+ C = ?MASK_ERROR(binary:at(make_unaligned(S),Pos)),
+ if
+ A =/= badarg ->
+ put(success_counter,get(success_counter)+1);
+ true ->
+ ok
+ end,
+ case {(A =:= B), (B =:= C)} of
+ {true,true} ->
+ do_random_at_comp(N-1,Range);
+ _ ->
+ io:format("Failed to pick last of ~s~n",
+ [S]),
+ io:format("A:~p,~nB:~p,~n,C:~p.~n",
+ [A,B,C]),
+ exit(mismatch)
+ end.
+
+do_random_matches_comp(0,_,_) ->
+ ok;
+do_random_matches_comp(N,NeedleRange,HaystackRange) ->
+ NumNeedles = element(2,HaystackRange) div element(2,NeedleRange),
+ Needles = [random_string(NeedleRange) ||
+ _ <- lists:duplicate(NumNeedles,a)],
+ Haystack = random_string(HaystackRange),
+ true = do_matches_comp(Needles,Haystack),
+ do_random_matches_comp(N-1,NeedleRange,HaystackRange).
+
+do_random_matches_comp2(0,_,_) ->
+ ok;
+do_random_matches_comp2(N,NeedleRange,HaystackRange) ->
+ NumNeedles = element(2,HaystackRange) div element(2,NeedleRange),
+ Haystack = random_string(HaystackRange),
+ Needles = [random_substring(NeedleRange,Haystack) ||
+ _ <- lists:duplicate(NumNeedles,a)],
+ true = do_matches_comp(Needles,Haystack),
+ do_random_matches_comp2(N-1,NeedleRange,HaystackRange).
+
+do_random_matches_comp3(0,_,_) ->
+ ok;
+do_random_matches_comp3(N,NeedleRange,HaystackRange) ->
+ NumNeedles = element(2,HaystackRange) div element(2,NeedleRange),
+ Haystack = random_string(HaystackRange),
+ Needles = [random_substring(NeedleRange,Haystack) ||
+ _ <- lists:duplicate(NumNeedles,a)],
+ RefRes = binref:matches(Haystack,Needles),
+ true = do_matches_comp_loop(10000,Needles,Haystack, RefRes),
+ do_random_matches_comp3(N-1,NeedleRange,HaystackRange).
+
+do_matches_comp_loop(0,_,_,_) ->
+ true;
+do_matches_comp_loop(N, Needles, Haystack0,RR) ->
+ DummySize=N*8,
+ Haystack1 = <<0:DummySize,Haystack0/binary>>,
+ RR1=[{X+N,Y} || {X,Y} <- RR],
+ true = do_matches_comp2(Needles,Haystack1,RR1),
+ Haystack2 = <<Haystack0/binary,Haystack1/binary>>,
+ RR2 = RR ++ [{X2+N+byte_size(Haystack0),Y2} || {X2,Y2} <- RR],
+ true = do_matches_comp2(Needles,Haystack2,RR2),
+ do_matches_comp_loop(N-1, Needles, Haystack0,RR).
+
+
+do_matches_comp2(N,H,A) ->
+ C = ?MASK_ERROR(binary:matches(H,N)),
+ case (A =:= C) of
+ true ->
+ true;
+ _ ->
+ io:format("Failed to match ~p (needle) against ~s (haystack)~n",
+ [N,H]),
+ io:format("A:~p,~n,C:~p.~n",
+ [A,C]),
+ exit(mismatch)
+ end.
+do_matches_comp(N,H) ->
+ A = ?MASK_ERROR(binref:matches(H,N)),
+ B = ?MASK_ERROR(binref:matches(H,binref:compile_pattern(N))),
+ C = ?MASK_ERROR(binary:matches(H,N)),
+ D = ?MASK_ERROR(binary:matches(make_unaligned(H),
+ binary:compile_pattern([make_unaligned2(X) || X <- N]))),
+ if
+ A =/= nomatch ->
+ put(success_counter,get(success_counter)+1);
+ true ->
+ ok
+ end,
+ case {(A =:= B), (B =:= C),(C =:= D)} of
+ {true,true,true} ->
+ true;
+ _ ->
+ io:format("Failed to match ~p (needle) against ~s (haystack)~n",
+ [N,H]),
+ io:format("A:~p,~nB:~p,~n,C:~p,~n,D:~p.~n",
+ [A,B,C,D]),
+ exit(mismatch)
+ end.
+
+do_random_match_comp(0,_,_) ->
+ ok;
+do_random_match_comp(N,NeedleRange,HaystackRange) ->
+ Needle = random_string(NeedleRange),
+ Haystack = random_string(HaystackRange),
+ true = do_match_comp(Needle,Haystack),
+ do_random_match_comp(N-1,NeedleRange,HaystackRange).
+
+do_random_match_comp2(0,_,_) ->
+ ok;
+do_random_match_comp2(N,NeedleRange,HaystackRange) ->
+ Haystack = random_string(HaystackRange),
+ Needle = random_substring(NeedleRange,Haystack),
+ true = do_match_comp(Needle,Haystack),
+ do_random_match_comp2(N-1,NeedleRange,HaystackRange).
+
+do_random_match_comp3(0,_,_) ->
+ ok;
+do_random_match_comp3(N,NeedleRange,HaystackRange) ->
+ NumNeedles = element(2,HaystackRange) div element(2,NeedleRange),
+ Haystack = random_string(HaystackRange),
+ Needles = [random_substring(NeedleRange,Haystack) ||
+ _ <- lists:duplicate(NumNeedles,a)],
+ true = do_match_comp3(Needles,Haystack),
+ do_random_match_comp3(N-1,NeedleRange,HaystackRange).
+
+do_random_match_comp4(0,_,_) ->
+ ok;
+do_random_match_comp4(N,NeedleRange,HaystackRange) ->
+ NumNeedles = element(2,HaystackRange) div element(2,NeedleRange),
+ Haystack = random_string(HaystackRange),
+ Needles = [random_string(NeedleRange) ||
+ _ <- lists:duplicate(NumNeedles,a)],
+ true = do_match_comp3(Needles,Haystack),
+ do_random_match_comp4(N-1,NeedleRange,HaystackRange).
+
+do_match_comp(N,H) ->
+ A = ?MASK_ERROR(binref:match(H,N)),
+ B = ?MASK_ERROR(binref:match(H,binref:compile_pattern([N]))),
+ C = ?MASK_ERROR(binary:match(make_unaligned(H),N)),
+ D = ?MASK_ERROR(binary:match(H,binary:compile_pattern([N]))),
+ E = ?MASK_ERROR(binary:match(H,binary:compile_pattern(make_unaligned(N)))),
+ if
+ A =/= nomatch ->
+ put(success_counter,get(success_counter)+1);
+ true ->
+ ok
+ end,
+ case {(A =:= B), (B =:= C),(C =:= D),(D =:= E)} of
+ {true,true,true,true} ->
+ true;
+ _ ->
+ io:format("Failed to match ~s (needle) against ~s (haystack)~n",
+ [N,H]),
+ io:format("A:~p,~nB:~p,~n,C:~p,~n,D:~p,E:~p.~n",
+ [A,B,C,D,E]),
+ exit(mismatch)
+ end.
+
+do_match_comp3(N,H) ->
+ A = ?MASK_ERROR(binref:match(H,N)),
+ B = ?MASK_ERROR(binref:match(H,binref:compile_pattern(N))),
+ C = ?MASK_ERROR(binary:match(H,N)),
+ D = ?MASK_ERROR(binary:match(H,binary:compile_pattern(N))),
+ if
+ A =/= nomatch ->
+ put(success_counter,get(success_counter)+1);
+ true ->
+ ok
+ end,
+ case {(A =:= B), (B =:= C),(C =:= D)} of
+ {true,true,true} ->
+ true;
+ _ ->
+ io:format("Failed to match ~s (needle) against ~s (haystack)~n",
+ [N,H]),
+ io:format("A:~p,~nB:~p,~n,C:~p,~n,D:~p.~n",
+ [A,B,C,D]),
+ exit(mismatch)
+ end.
+
+do_random_split_comp(0,_,_) ->
+ ok;
+do_random_split_comp(N,NeedleRange,HaystackRange) ->
+ Haystack = random_string(HaystackRange),
+ Needle = random_substring(NeedleRange,Haystack),
+ true = do_split_comp(Needle,Haystack,[]),
+ true = do_split_comp(Needle,Haystack,[global]),
+ true = do_split_comp(Needle,Haystack,[global,trim]),
+ do_random_split_comp(N-1,NeedleRange,HaystackRange).
+do_random_split_comp2(0,_,_) ->
+ ok;
+do_random_split_comp2(N,NeedleRange,HaystackRange) ->
+ NumNeedles = element(2,HaystackRange) div element(2,NeedleRange),
+ Haystack = random_string(HaystackRange),
+ Needles = [random_substring(NeedleRange,Haystack) ||
+ _ <- lists:duplicate(NumNeedles,a)],
+ true = do_split_comp(Needles,Haystack,[]),
+ true = do_split_comp(Needles,Haystack,[global]),
+ do_random_split_comp2(N-1,NeedleRange,HaystackRange).
+
+do_split_comp(N,H,Opts) ->
+ A = ?MASK_ERROR(binref:split(H,N,Opts)),
+ D = ?MASK_ERROR(binary:split(H,binary:compile_pattern(N),Opts)),
+ if
+ (A =/= [N]) and is_list(A) ->
+ put(success_counter,get(success_counter)+1);
+ true ->
+ ok
+ end,
+ case (A =:= D) of
+ true ->
+ true;
+ _ ->
+ io:format("Failed to split ~n~p ~n(haystack) with ~n~p ~n(needle) "
+ "~nand options ~p~n",
+ [H,N,Opts]),
+ io:format("A:~p,D:~p.~n",
+ [A,D]),
+ exit(mismatch)
+ end.
+
+do_random_replace_comp(0,_,_) ->
+ ok;
+do_random_replace_comp(N,NeedleRange,HaystackRange) ->
+ Haystack = random_string(HaystackRange),
+ Needle = random_substring(NeedleRange,Haystack),
+ Repl = random_string(NeedleRange),
+ Insertat = random_length(NeedleRange), %Sometimes larger than Repl
+ true = do_replace_comp(Needle,Haystack,Repl,[]),
+ true = do_replace_comp(Needle,Haystack,Repl,[global]),
+ true = do_replace_comp(Needle,Haystack,Repl,
+ [global,{insert_replaced,Insertat}]),
+ do_random_replace_comp(N-1,NeedleRange,HaystackRange).
+do_random_replace_comp2(0,_,_) ->
+ ok;
+do_random_replace_comp2(N,NeedleRange,HaystackRange) ->
+ NumNeedles = element(2,HaystackRange) div element(2,NeedleRange),
+ Haystack = random_string(HaystackRange),
+ Needles = [random_substring(NeedleRange,Haystack) ||
+ _ <- lists:duplicate(NumNeedles,a)],
+ Repl = random_string(NeedleRange),
+ Insertat = random_length(NeedleRange), %Sometimes larger than Repl
+ true = do_replace_comp(Needles,Haystack,Repl,[]),
+ true = do_replace_comp(Needles,Haystack,Repl,[global]),
+ true = do_replace_comp(Needles,Haystack,Repl,
+ [global,{insert_replaced,Insertat}]),
+ do_random_replace_comp2(N-1,NeedleRange,HaystackRange).
+
+do_replace_comp(N,H,R,Opts) ->
+ A = ?MASK_ERROR(binref:replace(H,N,R,Opts)),
+ D = ?MASK_ERROR(binary:replace(H,binary:compile_pattern(N),R,Opts)),
+ if
+ (A =/= N) and is_binary(A) ->
+ put(success_counter,get(success_counter)+1);
+ true ->
+ ok
+ end,
+ case (A =:= D) of
+ true ->
+ true;
+ _ ->
+ io:format("Failed to replace ~s (haystack) by ~s (needle) "
+ "inserting ~s (replacement) and options ~p~n",
+ [H,N,R,Opts]),
+ io:format("A:~p,D:~p.~n",
+ [A,D]),
+ exit(mismatch)
+ end.
+
+one_random_number(N) ->
+ M = ((N - 1) rem 10) + 1,
+ element(M,{$0,$1,$2,$3,$4,$5,$6,$7,$8,$9}).
+
+one_random(N) ->
+ M = ((N - 1) rem 68) + 1,
+ element(M,{$a,$b,$c,$d,$e,$f,$g,$h,$i,$j,$k,$l,$m,$n,$o,$p,$q,$r,$s,$t,
+ $u,$v,$w,$x,$y,$z,$�,$�,$�,$A,$B,$C,$D,$E,$F,$G,$H,
+ $I,$J,$K,$L,$M,$N,$O,$P,$Q,$R,$S,$T,$U,$V,$W,$X,$Y,$Z,$�,
+ $�,$�,$0,$1,$2,$3,$4,$5,$6,$7,$8,$9}).
+
+random_number({Min,Max}) -> % Min and Max are *length* of number in
+ % decimal positions
+ X = random:uniform(Max - Min + 1) + Min - 1,
+ list_to_integer([one_random_number(random:uniform(10)) || _ <- lists:seq(1,X)]).
+
+
+random_length({Min,Max}) ->
+ random:uniform(Max - Min + 1) + Min - 1.
+random_string({Min,Max}) ->
+ X = random:uniform(Max - Min + 1) + Min - 1,
+ list_to_binary([one_random(random:uniform(68)) || _ <- lists:seq(1,X)]).
+random_substring({Min,Max},Hay) ->
+ X = random:uniform(Max - Min + 1) + Min - 1,
+ Y = byte_size(Hay),
+ Z = if
+ X > Y -> Y;
+ true -> X
+ end,
+ PMax = Y - Z,
+ Pos = random:uniform(PMax + 1) - 1,
+ <<_:Pos/binary,Res:Z/binary,_/binary>> = Hay,
+ Res.
+
+mask_error({'EXIT',{Err,_}}) ->
+ Err;
+mask_error(Else) ->
+ Else.
+
+make_unaligned(Bin0) when is_binary(Bin0) ->
+ Bin1 = <<0:3,Bin0/binary,31:5>>,
+ Sz = byte_size(Bin0),
+ <<0:3,Bin:Sz/binary,31:5>> = id(Bin1),
+ Bin.
+make_unaligned2(Bin0) when is_binary(Bin0) ->
+ Bin1 = <<31:5,Bin0/binary,0:3>>,
+ Sz = byte_size(Bin0),
+ <<31:5,Bin:Sz/binary,0:3>> = id(Bin1),
+ Bin.
+
+id(I) -> I.
diff --git a/lib/stdlib/test/binref.erl b/lib/stdlib/test/binref.erl
new file mode 100644
index 0000000000..6d96736ef3
--- /dev/null
+++ b/lib/stdlib/test/binref.erl
@@ -0,0 +1,588 @@
+-module(binref).
+
+-export([compile_pattern/1,match/2,match/3,matches/2,matches/3,
+ split/2,split/3,replace/3,replace/4,first/1,last/1,at/2,
+ part/2,part/3,copy/1,copy/2,encode_unsigned/1,encode_unsigned/2,
+ decode_unsigned/1,decode_unsigned/2,referenced_byte_size/1,
+ longest_common_prefix/1,longest_common_suffix/1,bin_to_list/1,
+ bin_to_list/2,bin_to_list/3,list_to_bin/1]).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% compile_pattern, a dummy
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+compile_pattern(Pattern) when is_binary(Pattern) ->
+ {[Pattern]};
+compile_pattern(Pattern) ->
+ try
+ [ true = is_binary(P) || P <- Pattern ],
+ {Pattern}
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% match and matches
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+match(H,N) ->
+ match(H,N,[]).
+match(Haystack,Needle,Options) when is_binary(Needle) ->
+ match(Haystack,[Needle],Options);
+match(Haystack,{Needles},Options) ->
+ match(Haystack,Needles,Options);
+match(Haystack,Needles,Options) ->
+ try
+ true = is_binary(Haystack) and is_list(Needles), % badarg, not function_clause
+ case get_opts_match(Options,nomatch) of
+ nomatch ->
+ mloop(Haystack,Needles);
+ {A,B} when B > 0 ->
+ <<_:A/binary,SubStack:B/binary,_/binary>> = Haystack,
+ mloop(SubStack,Needles,A,B+A);
+ {A,B} when B < 0 ->
+ Start = A + B,
+ Len = -B,
+ <<_:Start/binary,SubStack:Len/binary,_/binary>> = Haystack,
+ mloop(SubStack,Needles,Start,Len+Start);
+ _ ->
+ nomatch
+ end
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+matches(H,N) ->
+ matches(H,N,[]).
+matches(Haystack,Needle,Options) when is_binary(Needle) ->
+ matches(Haystack,[Needle],Options);
+matches(Haystack,{Needles},Options) ->
+ matches(Haystack,Needles,Options);
+matches(Haystack,Needles,Options) ->
+ try
+ true = is_binary(Haystack) and is_list(Needles), % badarg, not function_clause
+ case get_opts_match(Options,nomatch) of
+ nomatch ->
+ msloop(Haystack,Needles);
+ {A,B} when B > 0 ->
+ <<_:A/binary,SubStack:B/binary,_/binary>> = Haystack,
+ msloop(SubStack,Needles,A,B+A);
+ {A,B} when B < 0 ->
+ Start = A + B,
+ Len = -B,
+ <<_:Start/binary,SubStack:Len/binary,_/binary>> = Haystack,
+ msloop(SubStack,Needles,Start,Len+Start);
+ _ ->
+ []
+ end
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+mloop(Haystack,Needles) ->
+ mloop(Haystack,Needles,0,byte_size(Haystack)).
+
+mloop(_Haystack,_Needles,N,M) when N >= M ->
+ nomatch;
+mloop(Haystack,Needles,N,M) ->
+ case mloop2(Haystack,Needles,N,nomatch) of
+ nomatch ->
+ % Not found
+ <<_:8,NewStack/binary>> = Haystack,
+ mloop(NewStack,Needles,N+1,M);
+ {N,Len} ->
+ {N,Len}
+ end.
+
+msloop(Haystack,Needles) ->
+ msloop(Haystack,Needles,0,byte_size(Haystack)).
+
+msloop(_Haystack,_Needles,N,M) when N >= M ->
+ [];
+msloop(Haystack,Needles,N,M) ->
+ case mloop2(Haystack,Needles,N,nomatch) of
+ nomatch ->
+ % Not found
+ <<_:8,NewStack/binary>> = Haystack,
+ msloop(NewStack,Needles,N+1,M);
+ {N,Len} ->
+ NewN = N+Len,
+ if
+ NewN >= M ->
+ [{N,Len}];
+ true ->
+ <<_:Len/binary,NewStack/binary>> = Haystack,
+ [{N,Len} | msloop(NewStack,Needles,NewN,M)]
+ end
+ end.
+
+mloop2(_Haystack,[],_N,Res) ->
+ Res;
+mloop2(Haystack,[Needle|Tail],N,Candidate) ->
+ NS = byte_size(Needle),
+ case Haystack of
+ <<Needle:NS/binary,_/binary>> ->
+ NewCandidate = case Candidate of
+ nomatch ->
+ {N,NS};
+ {N,ONS} when ONS < NS ->
+ {N,NS};
+ Better ->
+ Better
+ end,
+ mloop2(Haystack,Tail,N,NewCandidate);
+ _ ->
+ mloop2(Haystack,Tail,N,Candidate)
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% split
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+split(H,N) ->
+ split(H,N,[]).
+split(Haystack,{Needles},Options) ->
+ split(Haystack, Needles, Options);
+split(Haystack,Needles0,Options) ->
+ try
+ Needles = if
+ is_list(Needles0) ->
+ Needles0;
+ is_binary(Needles0) ->
+ [Needles0];
+ true ->
+ exit(badtype)
+ end,
+ {Part,Global,Trim} = get_opts_split(Options,{nomatch,false,false}),
+ {Start,End,NewStack} =
+ case Part of
+ nomatch ->
+ {0,byte_size(Haystack),Haystack};
+ {A,B} when B >= 0 ->
+ <<_:A/binary,SubStack:B/binary,_/binary>> = Haystack,
+ {A,A+B,SubStack};
+ {A,B} when B < 0 ->
+ S = A + B,
+ L = -B,
+ <<_:S/binary,SubStack:L/binary,_/binary>> = Haystack,
+ {S,S+L,SubStack}
+ end,
+ MList = if
+ Global ->
+ msloop(NewStack,Needles,Start,End);
+ true ->
+ case mloop(NewStack,Needles,Start,End) of
+ nomatch ->
+ [];
+ X ->
+ [X]
+ end
+ end,
+ do_split(Haystack,MList,0,Trim)
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+do_split(H,[],N,true) when N >= byte_size(H) ->
+ [];
+do_split(H,[],N,_) ->
+ [part(H,{N,byte_size(H)-N})];
+do_split(H,[{A,B}|T],N,Trim) ->
+ case part(H,{N,A-N}) of
+ <<>> ->
+ Rest = do_split(H,T,A+B,Trim),
+ case {Trim, Rest} of
+ {true,[]} ->
+ [];
+ _ ->
+ [<<>> | Rest]
+ end;
+ Oth ->
+ [Oth | do_split(H,T,A+B,Trim)]
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% replace
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+replace(H,N,R) ->
+ replace(H,N,R,[]).
+replace(Haystack,{Needles},Replacement,Options) ->
+ replace(Haystack,Needles,Replacement,Options);
+
+replace(Haystack,Needles0,Replacement,Options) ->
+ try
+ Needles = if
+ is_list(Needles0) ->
+ Needles0;
+ is_binary(Needles0) ->
+ [Needles0];
+ true ->
+ exit(badtype)
+ end,
+ true = is_binary(Replacement), % Make badarg instead of function clause
+ {Part,Global,Insert} = get_opts_replace(Options,{nomatch,false,[]}),
+ {Start,End,NewStack} =
+ case Part of
+ nomatch ->
+ {0,byte_size(Haystack),Haystack};
+ {A,B} when B >= 0 ->
+ <<_:A/binary,SubStack:B/binary,_/binary>> = Haystack,
+ {A,A+B,SubStack};
+ {A,B} when B < 0 ->
+ S = A + B,
+ L = -B,
+ <<_:S/binary,SubStack:L/binary,_/binary>> = Haystack,
+ {S,S+L,SubStack}
+ end,
+ MList = if
+ Global ->
+ msloop(NewStack,Needles,Start,End);
+ true ->
+ case mloop(NewStack,Needles,Start,End) of
+ nomatch ->
+ [];
+ X ->
+ [X]
+ end
+ end,
+ ReplList = case Insert of
+ [] ->
+ Replacement;
+ Y when is_integer(Y) ->
+ splitat(Replacement,0,[Y]);
+ Li when is_list(Li) ->
+ splitat(Replacement,0,lists:sort(Li))
+ end,
+ erlang:iolist_to_binary(do_replace(Haystack,MList,ReplList,0))
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+
+do_replace(H,[],_,N) ->
+ [part(H,{N,byte_size(H)-N})];
+do_replace(H,[{A,B}|T],Replacement,N) ->
+ [part(H,{N,A-N}),
+ if
+ is_list(Replacement) ->
+ do_insert(Replacement, part(H,{A,B}));
+ true ->
+ Replacement
+ end
+ | do_replace(H,T,Replacement,A+B)].
+
+do_insert([X],_) ->
+ [X];
+do_insert([H|T],R) ->
+ [H,R|do_insert(T,R)].
+
+splitat(H,N,[]) ->
+ [part(H,{N,byte_size(H)-N})];
+splitat(H,N,[I|T]) ->
+ [part(H,{N,I-N})|splitat(H,I,T)].
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% first, last and at
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+first(Subject) ->
+ try
+ <<A:8,_/binary>> = Subject,
+ A
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+last(Subject) ->
+ try
+ N = byte_size(Subject) - 1,
+ <<_:N/binary,A:8>> = Subject,
+ A
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+at(Subject,X) ->
+ try
+ <<_:X/binary,A:8,_/binary>> = Subject,
+ A
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% bin_to_list
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+bin_to_list(Subject) ->
+ try
+ binary_to_list(Subject)
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+bin_to_list(Subject,T) ->
+ try
+ {A0,B0} = T,
+ {A,B} = if
+ B0 < 0 ->
+ {A0+B0,-B0};
+ true ->
+ {A0,B0}
+ end,
+ binary_to_list(Subject,A+1,A+B)
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+bin_to_list(Subject,A,B) ->
+ try
+ bin_to_list(Subject,{A,B})
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% list_to_bin
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+list_to_bin(List) ->
+ try
+ erlang:list_to_binary(List)
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% longest_common_prefix
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+longest_common_prefix(LB) ->
+ try
+ true = is_list(LB) and (length(LB) > 0), % Make badarg instead of function clause
+ do_longest_common_prefix(LB,0)
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+do_longest_common_prefix(LB,X) ->
+ case do_lcp(LB,X,no) of
+ true ->
+ do_longest_common_prefix(LB,X+1);
+ false ->
+ X
+ end.
+do_lcp([],_,_) ->
+ true;
+do_lcp([Bin|_],X,_) when byte_size(Bin) =< X ->
+ false;
+do_lcp([Bin|T],X,no) ->
+ Ch = at(Bin,X),
+ do_lcp(T,X,Ch);
+do_lcp([Bin|T],X,Ch) ->
+ Ch2 = at(Bin,X),
+ if
+ Ch =:= Ch2 ->
+ do_lcp(T,X,Ch);
+ true ->
+ false
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% longest_common_suffix
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+longest_common_suffix(LB) ->
+ try
+ true = is_list(LB) and (length(LB) > 0), % Make badarg instead of function clause
+ do_longest_common_suffix(LB,0)
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+do_longest_common_suffix(LB,X) ->
+ case do_lcs(LB,X,no) of
+ true ->
+ do_longest_common_suffix(LB,X+1);
+ false ->
+ X
+ end.
+do_lcs([],_,_) ->
+ true;
+do_lcs([Bin|_],X,_) when byte_size(Bin) =< X ->
+ false;
+do_lcs([Bin|T],X,no) ->
+ Ch = at(Bin,byte_size(Bin) - 1 - X),
+ do_lcs(T,X,Ch);
+do_lcs([Bin|T],X,Ch) ->
+ Ch2 = at(Bin,byte_size(Bin) - 1 - X),
+ if
+ Ch =:= Ch2 ->
+ do_lcs(T,X,Ch);
+ true ->
+ false
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% part
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+part(Subject,Part) ->
+ try
+ do_part(Subject,Part)
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+part(Subject,Pos,Len) ->
+ part(Subject,{Pos,Len}).
+
+do_part(Bin,{A,B}) when B >= 0 ->
+ <<_:A/binary,Sub:B/binary,_/binary>> = Bin,
+ Sub;
+do_part(Bin,{A,B}) when B < 0 ->
+ S = A + B,
+ L = -B,
+ <<_:S/binary,Sub:L/binary,_/binary>> = Bin,
+ Sub.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% copy
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+copy(Subject) ->
+ copy(Subject,1).
+copy(Subject,N) ->
+ try
+ true = is_integer(N) and (N >= 0) and is_binary(Subject), % Badarg, not function clause
+ erlang:list_to_binary(lists:duplicate(N,Subject))
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_unsigned
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+encode_unsigned(Unsigned) ->
+ encode_unsigned(Unsigned,big).
+encode_unsigned(Unsigned,Endian) ->
+ try
+ true = is_integer(Unsigned) and (Unsigned >= 0),
+ if
+ Unsigned =:= 0 ->
+ <<0>>;
+ true ->
+ case Endian of
+ big ->
+ list_to_binary(do_encode(Unsigned,[]));
+ little ->
+ list_to_binary(do_encode_r(Unsigned))
+ end
+ end
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+do_encode(0,L) ->
+ L;
+do_encode(N,L) ->
+ Byte = N band 255,
+ NewN = N bsr 8,
+ do_encode(NewN,[Byte|L]).
+
+do_encode_r(0) ->
+ [];
+do_encode_r(N) ->
+ Byte = N band 255,
+ NewN = N bsr 8,
+ [Byte|do_encode_r(NewN)].
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_unsigned
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+decode_unsigned(Subject) ->
+ decode_unsigned(Subject,big).
+
+decode_unsigned(Subject,Endian) ->
+ try
+ true = is_binary(Subject),
+ case Endian of
+ big ->
+ do_decode(Subject,0);
+ little ->
+ do_decode_r(Subject,0)
+ end
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+do_decode(<<>>,N) ->
+ N;
+do_decode(<<X:8,Bin/binary>>,N) ->
+ do_decode(Bin,(N bsl 8) bor X).
+
+do_decode_r(<<>>,N) ->
+ N;
+do_decode_r(Bin,N) ->
+ Sz = byte_size(Bin) - 1,
+ <<NewBin:Sz/binary,X>> = Bin,
+ do_decode_r(NewBin, (N bsl 8) bor X).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% referenced_byte_size cannot
+%% be implemented in pure
+%% erlang
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+referenced_byte_size(Bin) when is_binary(Bin) ->
+ erlang:error(not_implemented);
+referenced_byte_size(_) ->
+ erlang:error(badarg).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Simple helper functions
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Option "parsing"
+get_opts_match([],Part) ->
+ Part;
+get_opts_match([{scope,{A,B}} | T],_Part) ->
+ get_opts_match(T,{A,B});
+get_opts_match(_,_) ->
+ throw(badopt).
+
+get_opts_split([],{Part,Global,Trim}) ->
+ {Part,Global,Trim};
+get_opts_split([{scope,{A,B}} | T],{_Part,Global,Trim}) ->
+ get_opts_split(T,{{A,B},Global,Trim});
+get_opts_split([global | T],{Part,_Global,Trim}) ->
+ get_opts_split(T,{Part,true,Trim});
+get_opts_split([trim | T],{Part,Global,_Trim}) ->
+ get_opts_split(T,{Part,Global,true});
+get_opts_split(_,_) ->
+ throw(badopt).
+
+get_opts_replace([],{Part,Global,Insert}) ->
+ {Part,Global,Insert};
+get_opts_replace([{scope,{A,B}} | T],{_Part,Global,Insert}) ->
+ get_opts_replace(T,{{A,B},Global,Insert});
+get_opts_replace([global | T],{Part,_Global,Insert}) ->
+ get_opts_replace(T,{Part,true,Insert});
+get_opts_replace([{insert_replaced,N} | T],{Part,Global,_Insert}) ->
+ get_opts_replace(T,{Part,Global,N});
+get_opts_replace(_,_) ->
+ throw(badopt).
diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl
index 760e610e00..8b18ef5664 100644
--- a/lib/stdlib/test/dets_SUITE.erl
+++ b/lib/stdlib/test/dets_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -50,7 +50,8 @@
otp_4208/1, otp_4989/1, many_clients/1, otp_4906/1, otp_5402/1,
simultaneous_open/1, insert_new/1, repair_continuation/1,
otp_5487/1, otp_6206/1, otp_6359/1, otp_4738/1, otp_7146/1,
- otp_8070/1]).
+ otp_8070/1, otp_8856/1, otp_8898/1, otp_8899/1, otp_8903/1,
+ otp_8923/1]).
-export([dets_dirty_loop/0]).
@@ -108,7 +109,8 @@ all(suite) ->
cache_duplicate_bags_v9, otp_4208, otp_4989, many_clients,
otp_4906, otp_5402, simultaneous_open, insert_new,
repair_continuation, otp_5487, otp_6206, otp_6359, otp_4738,
- otp_7146, otp_8070]}
+ otp_7146, otp_8070, otp_8856, otp_8898, otp_8899, otp_8903,
+ otp_8923]}
end.
not_run(suite) -> [];
@@ -2935,6 +2937,57 @@ ets_init(Tab, N) ->
ets:insert(Tab, {N,N}),
ets_init(Tab, N - 1).
+otp_8898(doc) ->
+ ["OTP-8898. Truncated Dets file."];
+otp_8898(suite) ->
+ [];
+otp_8898(Config) when is_list(Config) ->
+ Tab = otp_8898,
+ ?line FName = filename(Tab, Config),
+
+ Server = self(),
+
+ ?line file:delete(FName),
+ ?line {ok, _} = dets:open_file(Tab,[{file, FName}]),
+ ?line [P1,P2,P3] = new_clients(3, Tab),
+
+ Seq = [{P1,[sync]},{P2,[{lookup,1,[]}]},{P3,[{insert,{1,b}}]}],
+ ?line atomic_requests(Server, Tab, [[]], Seq),
+ ?line true = get_replies([{P1,ok},{P2,ok},{P3,ok}]),
+ ?line ok = dets:close(Tab),
+ ?line {ok, _} = dets:open_file(Tab,[{file, FName}]),
+ ?line file:delete(FName),
+
+ ok.
+
+otp_8899(doc) ->
+ ["OTP-8899. Several clients. Updated Head was ignored."];
+otp_8899(suite) ->
+ [];
+otp_8899(Config) when is_list(Config) ->
+ Tab = many_clients,
+ ?line FName = filename(Tab, Config),
+
+ Server = self(),
+
+ ?line file:delete(FName),
+ ?line {ok, _} = dets:open_file(Tab,[{file, FName},{version,9}]),
+ ?line [P1,P2,P3,P4] = new_clients(4, Tab),
+
+ MC = [Tab],
+ Seq6a = [{P1,[{insert,[{used_to_be_skipped_by,match}]},
+ {lookup,1,[{1,a}]}]},
+ {P2,[{verbose,true,MC}]},
+ {P3,[{lookup,1,[{1,a}]}]}, {P4,[{verbose,true,MC}]}],
+ ?line atomic_requests(Server, Tab, [[{1,a},{2,b},{3,c}]], Seq6a),
+ ?line true = get_replies([{P1,ok}, {P2,ok}, {P3,ok}, {P4,ok}]),
+ ?line [{1,a},{2,b},{3,c},{used_to_be_skipped_by,match}] =
+ lists:sort(dets:match_object(Tab, '_')),
+ ?line _ = dets:close(Tab),
+ ?line file:delete(FName),
+
+ ok.
+
many_clients(doc) ->
["Several clients accessing a table simultaneously."];
many_clients(suite) ->
@@ -3071,6 +3124,11 @@ client(S, Tab) ->
eval([], _Tab) ->
ok;
+eval([{verbose,Bool,Expected} | L], Tab) ->
+ ?line case dets:verbose(Bool) of
+ Expected -> eval(L, Tab);
+ Error -> {error, {verbose,Error}}
+ end;
eval([sync | L], Tab) ->
?line case dets:sync(Tab) of
ok -> eval(L, Tab);
@@ -3701,6 +3759,87 @@ otp_8070(Config) when is_list(Config) ->
file:delete(File),
ok.
+otp_8856(doc) ->
+ ["OTP-8856. insert_new() bug."];
+otp_8856(suite) ->
+ [];
+otp_8856(Config) when is_list(Config) ->
+ Tab = otp_8856,
+ File = filename(Tab, Config),
+ file:delete(File),
+ Me = self(),
+ ?line {ok, _} = dets:open_file(Tab, [{type, bag}, {file, File}]),
+ spawn(fun()-> Me ! {1, dets:insert(Tab, [])} end),
+ spawn(fun()-> Me ! {2, dets:insert_new(Tab, [])} end),
+ ?line ok = dets:close(Tab),
+ ?line receive {1, ok} -> ok end,
+ ?line receive {2, true} -> ok end,
+ file:delete(File),
+
+ ?line {ok, _} = dets:open_file(Tab, [{type, set}, {file, File}]),
+ spawn(fun() -> dets:delete(Tab, 0) end),
+ spawn(fun() -> Me ! {3, dets:insert_new(Tab, {0,0})} end),
+ ?line ok = dets:close(Tab),
+ ?line receive {3, true} -> ok end,
+ file:delete(File),
+ ok.
+
+otp_8903(doc) ->
+ ["OTP-8903. bchunk/match/select bug."];
+otp_8903(suite) ->
+ [];
+otp_8903(Config) when is_list(Config) ->
+ Tab = otp_8903,
+ File = filename(Tab, Config),
+ ?line {ok,T} = dets:open_file(bug, [{file,File}]),
+ ?line ok = dets:insert(T, [{1,a},{2,b},{3,c}]),
+ ?line dets:safe_fixtable(T, true),
+ ?line {[_],C1} = dets:match_object(T, '_', 1),
+ ?line {BC1,_D} = dets:bchunk(T, start),
+ ?line ok = dets:close(T),
+ ?line {'EXIT', {badarg, _}} = (catch {foo,dets:match_object(C1)}),
+ ?line {'EXIT', {badarg, _}} = (catch {foo,dets:bchunk(T, BC1)}),
+ ?line {ok,T} = dets:open_file(bug, [{file,File}]),
+ ?line false = dets:info(T, safe_fixed),
+ ?line {'EXIT', {badarg, _}} = (catch {foo,dets:match_object(C1)}),
+ ?line {'EXIT', {badarg, _}} = (catch {foo,dets:bchunk(T, BC1)}),
+ ?line ok = dets:close(T),
+ file:delete(File),
+ ok.
+
+otp_8923(doc) ->
+ ["OTP-8923. rehash due to lookup after initialization."];
+otp_8923(suite) ->
+ [];
+otp_8923(Config) when is_list(Config) ->
+ Tab = otp_8923,
+ File = filename(Tab, Config),
+ %% Create a file with more than 256 keys:
+ file:delete(File),
+ Bin = list_to_binary([ 0 || _ <- lists:seq(1, 400) ]),
+ BigBin = list_to_binary([ 0 ||_ <- lists:seq(1, 4000)]),
+ Ets = ets:new(temp, [{keypos,1}]),
+ ?line [ true = ets:insert(Ets, {C,Bin}) || C <- lists:seq(1, 700) ],
+ ?line true = ets:insert(Ets, {helper_data,BigBin}),
+ ?line true = ets:insert(Ets, {prim_btree,BigBin}),
+ ?line true = ets:insert(Ets, {sec_btree,BigBin}),
+ %% Note: too few slots; re-hash will take place
+ ?line {ok, Tab} = dets:open_file(Tab, [{file,File}]),
+ ?line Tab = ets:to_dets(Ets, Tab),
+ ?line ok = dets:close(Tab),
+ ?line true = ets:delete(Ets),
+
+ ?line {ok,Ref} = dets:open_file(File),
+ ?line [{1,_}] = dets:lookup(Ref, 1),
+ ?line ok = dets:close(Ref),
+
+ ?line {ok,Ref2} = dets:open_file(File),
+ ?line [{helper_data,_}] = dets:lookup(Ref2, helper_data),
+ ?line ok = dets:close(Ref2),
+
+ file:delete(File),
+ ok.
+
%%
%% Parts common to several test cases
%%
diff --git a/lib/stdlib/test/dummy1_h.erl b/lib/stdlib/test/dummy1_h.erl
index 4377d774a3..5b503d5984 100644
--- a/lib/stdlib/test/dummy1_h.erl
+++ b/lib/stdlib/test/dummy1_h.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(dummy1_h).
@@ -21,7 +21,7 @@
%% Test event handler for gen_event_SUITE.erl
-export([init/1, handle_event/2, handle_call/2, handle_info/2,
- terminate/2]).
+ terminate/2, format_status/2]).
init(make_error) ->
{error, my_error};
@@ -67,4 +67,5 @@ terminate(remove_handler, Parent) ->
terminate(_Reason, _State) ->
ok.
-
+format_status(_Opt, [_PDict, _State]) ->
+ "dummy1_h handler state".
diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl
index 9a3ae0baf5..e9fb932632 100644
--- a/lib/stdlib/test/epp_SUITE.erl
+++ b/lib/stdlib/test/epp_SUITE.erl
@@ -19,11 +19,12 @@
-module(epp_SUITE).
-export([all/1]).
--export([rec_1/1, predef_mac/1,
+-export([rec_1/1, predef_mac/1,
upcase_mac/1, upcase_mac_1/1, upcase_mac_2/1,
variable/1, variable_1/1, otp_4870/1, otp_4871/1, otp_5362/1,
pmod/1, not_circular/1, skip_header/1, otp_6277/1, otp_7702/1,
- otp_8130/1, overload_mac/1, otp_8388/1]).
+ otp_8130/1, overload_mac/1, otp_8388/1, otp_8470/1, otp_8503/1,
+ otp_8562/1, otp_8665/1, otp_8911/1]).
-export([epp_parse_erl_form/2]).
@@ -38,7 +39,7 @@
-define(config(A,B),config(A,B)).
%% -define(t, test_server).
-define(t, io).
-config(priv_dir, _) ->
+config(priv_dir, _) ->
filename:absname("./epp_SUITE_priv");
config(data_dir, _) ->
filename:absname("./epp_SUITE_data").
@@ -63,7 +64,7 @@ all(doc) ->
all(suite) ->
[rec_1, upcase_mac, predef_mac, variable, otp_4870, otp_4871, otp_5362,
pmod, not_circular, skip_header, otp_6277, otp_7702, otp_8130,
- overload_mac, otp_8388].
+ overload_mac, otp_8388, otp_8470, otp_8503, otp_8562, otp_8665, otp_8911].
rec_1(doc) ->
["Recursive macros hang or crash epp (OTP-1398)."];
@@ -191,7 +192,7 @@ variable_1(Config) when is_list(Config) ->
%% variable_1.erl includes variable_1_include.hrl and
%% variable_1_include_dir.hrl.
?line {ok, List} = epp:parse_file(File, [], []),
- ?line {value, {attribute,_,a,{value1,value2}}} =
+ ?line {value, {attribute,_,a,{value1,value2}}} =
lists:keysearch(a,3,List),
ok.
@@ -218,13 +219,13 @@ otp_4871(Config) when is_list(Config) ->
%% Testing crash in erl_scan. Unfortunately there currently is
%% no known way to crash erl_scan so it is emulated by killing the
%% file io server. This assumes lots of things about how
- %% the processes are started and how monitors are set up,
+ %% the processes are started and how monitors are set up,
%% so there are some sanity checks before killing.
?line {ok,Epp} = epp:open(File, []),
timer:sleep(1),
?line {current_function,{epp,_,_}} = process_info(Epp, current_function),
?line {monitored_by,[Io]} = process_info(Epp, monitored_by),
- ?line {current_function,{file_io_server,_,_}} =
+ ?line {current_function,{file_io_server,_,_}} =
process_info(Io, current_function),
?line exit(Io, emulate_crash),
timer:sleep(1),
@@ -301,7 +302,7 @@ otp_5362(Config) when is_list(Config) ->
Back_hrl = [<<"
-file(\"">>,File_Back,<<"\", 2).
">>],
-
+
?line ok = file:write_file(File_Back, Back),
?line ok = file:write_file(File_Back_hrl, list_to_binary(Back_hrl)),
@@ -332,7 +333,7 @@ otp_5362(Config) when is_list(Config) ->
?line ok = file:write_file(File_Change, list_to_binary(Change)),
- ?line {ok, change_5362, ChangeWarnings} =
+ ?line {ok, change_5362, ChangeWarnings} =
compile:file(File_Change, Copts),
?line true = message_compare(
[{File_Change,[{{1002,21},erl_lint,{unused_var,'B'}}]},
@@ -440,9 +441,9 @@ skip_header(Config) when is_list(Config) ->
that should be skipped
-module(epp_test_skip_header).
-export([main/1]).
-
+
main(_) -> ?MODULE.
-
+
">>),
?line {ok, Fd} = file:open(File, [read]),
?line io:get_line(Fd, ''),
@@ -493,9 +494,9 @@ otp_7702(Config) when is_list(Config) ->
t() ->
?RECEIVE(foo, bar).">>,
?line ok = file:write_file(File, Contents),
- ?line {ok, file_7702, []} =
+ ?line {ok, file_7702, []} =
compile:file(File, [debug_info,return,{outdir,Dir}]),
-
+
BeamFile = filename:join(Dir, "file_7702.beam"),
{ok, AC} = beam_lib:chunks(BeamFile, [abstract_code]),
@@ -505,7 +506,7 @@ otp_7702(Config) when is_list(Config) ->
L
end,
Forms2 = [erl_lint:modify_line(Form, Fun) || Form <- Forms],
- ?line
+ ?line
[{attribute,1,file,_},
_,
_,
@@ -616,9 +617,9 @@ otp_8130(Config) when is_list(Config) ->
"t() -> 14 = (#file_info{size = 14})#file_info.size, ok.\n">>,
ok},
- {otp_8130_7,
+ {otp_8130_7_new,
<<"-record(b, {b}).\n"
- "-define(A, {{a,#b.b.\n"
+ "-define(A, {{a,#b.b).\n"
"t() -> {{a,2}} = ?A}}, ok.">>,
ok},
@@ -636,7 +637,7 @@ otp_8130(Config) when is_list(Config) ->
],
?line [] = run(Config, Ts),
-
+
Cs = [{otp_8130_c1,
<<"-define(M1(A), if\n"
"A =:= 1 -> B;\n"
@@ -680,7 +681,7 @@ otp_8130(Config) when is_list(Config) ->
<<"\n-include_lib(\"$apa/foo.hrl\").\n">>,
{errors,[{{2,2},epp,{include,lib,"$apa/foo.hrl"}}],[]}},
-
+
{otp_8130_c9,
<<"-define(S, ?S).\n"
"t() -> ?S.\n">>,
@@ -750,7 +751,14 @@ otp_8130(Config) when is_list(Config) ->
{otp_8130_c24,
<<"\n-include(\"no such file.erl\").\n">>,
- {errors,[{{2,2},epp,{include,file,"no such file.erl"}}],[]}}
+ {errors,[{{2,2},epp,{include,file,"no such file.erl"}}],[]}},
+
+ {otp_8130_7,
+ <<"-record(b, {b}).\n"
+ "-define(A, {{a,#b.b.\n"
+ "t() -> {{a,2}} = ?A}}, ok.">>,
+ {errors,[{{2,20},epp,missing_parenthesis},
+ {{3,19},epp,{undefined,'A',none}}],[]}}
],
?line [] = compile(Config, Cs),
@@ -767,7 +775,7 @@ otp_8130(Config) when is_list(Config) ->
?line Dir = ?config(priv_dir, Config),
?line File = filename:join(Dir, "otp_8130.erl"),
- ?line ok = file:write_file(File,
+ ?line ok = file:write_file(File,
"-module(otp_8130).\n"
"-define(a, 3.14).\n"
"t() -> ?a.\n"),
@@ -780,7 +788,7 @@ otp_8130(Config) when is_list(Config) ->
?line {eof,_} = epp:scan_erl_form(Epp),
?line ['BASE_MODULE','BASE_MODULE_STRING','BEAM','FILE','LINE',
'MACHINE','MODULE','MODULE_STRING',a] = macs(Epp),
- ?line epp:close(Epp),
+ ?line epp:close(Epp),
%% escript
ModuleStr = "any_name",
@@ -807,7 +815,7 @@ otp_8130(Config) when is_list(Config) ->
PreDefMacros = [{a,1},a],
?line {error,{redefine,a}} = epp:open(File, [], PreDefMacros)
end(),
-
+
?line {error,enoent} = epp:open("no such file", []),
?line {error,enoent} = epp:parse_file("no such file", [], []),
@@ -933,7 +941,7 @@ ifdef(Config) ->
<<"\n-if.\n"
"-endif.\n">>,
{errors,[{{2,2},epp,{'NYI','if'}}],[]}},
-
+
{define_c7,
<<"-ifndef(a).\n"
"-elif.\n"
@@ -1047,13 +1055,13 @@ overload_mac(Config) when is_list(Config) ->
"-undef(A).\n"
"t1() -> ?A.\n",
"t2() -> ?A(1).">>,
- {errors,[{{4,9},epp,{undefined,'A', none}},
- {{5,9},epp,{undefined,'A', 1}}],[]}},
+ {errors,[{{4,10},epp,{undefined,'A', none}},
+ {{5,10},epp,{undefined,'A', 1}}],[]}},
%% cannot overload predefined macros
{overload_mac_c2,
<<"-define(MODULE(X), X).">>,
- {errors,[{{1,9},epp,{redefine_predef,'MODULE'}}],[]}},
+ {errors,[{{1,50},epp,{redefine_predef,'MODULE'}}],[]}},
%% cannot overload macros with same arity
{overload_mac_c3,
@@ -1120,25 +1128,121 @@ otp_8388(Config) when is_list(Config) ->
{macro_1,
<<"-define(m(A), A).\n"
"t() -> ?m(,).\n">>,
- {errors,[{{2,11},epp,{arg_error,m}}],[]}},
+ {errors,[{{2,9},epp,{arg_error,m}}],[]}},
{macro_2,
<<"-define(m(A), A).\n"
"t() -> ?m(a,).\n">>,
- {errors,[{{2,12},epp,{arg_error,m}}],[]}},
+ {errors,[{{2,9},epp,{arg_error,m}}],[]}},
{macro_3,
<<"-define(LINE, a).\n">>,
- {errors,[{{1,9},epp,{redefine_predef,'LINE'}}],[]}},
+ {errors,[{{1,50},epp,{redefine_predef,'LINE'}}],[]}},
{macro_4,
<<"-define(A(B, C, D), {B,C,D}).\n"
"t() -> ?A(a,,3).\n">>,
- {errors,[{{2,8},epp,{mismatch,'A'}}],[]}},
+ {errors,[{{2,9},epp,{mismatch,'A'}}],[]}},
{macro_5,
<<"-define(Q, {?F0(), ?F1(,,4)}).\n">>,
- {errors,[{{1,24},epp,{arg_error,'F1'}}],[]}}
+ {errors,[{{1,62},epp,{arg_error,'F1'}}],[]}},
+ {macro_6,
+ <<"-define(FOO(X), ?BAR(X)).\n"
+ "-define(BAR(X), ?FOO(X)).\n"
+ "-undef(FOO).\n"
+ "test() -> ?BAR(1).\n">>,
+ {errors,[{{4,12},epp,{undefined,'FOO',1}}],[]}}
],
?line [] = compile(Config, Ts),
ok.
+otp_8470(doc) ->
+ ["OTP-8470. Bugfix (one request - two replies)."];
+otp_8470(suite) ->
+ [];
+otp_8470(Config) when is_list(Config) ->
+ Dir = ?config(priv_dir, Config),
+ C = <<"-file(\"erl_parse.yrl\", 486).\n"
+ "-file(\"erl_parse.yrl\", 488).\n">>,
+ ?line File = filename:join(Dir, "otp_8470.erl"),
+ ?line ok = file:write_file(File, C),
+ ?line {ok, _List} = epp:parse_file(File, [], []),
+ file:delete(File),
+ ?line receive _ -> fail() after 0 -> ok end,
+ ok.
+
+otp_8503(doc) ->
+ ["OTP-8503. Record with no fields is considered typed."];
+otp_8503(suite) ->
+ [];
+otp_8503(Config) when is_list(Config) ->
+ Dir = ?config(priv_dir, Config),
+ C = <<"-record(r, {}).">>,
+ ?line File = filename:join(Dir, "otp_8503.erl"),
+ ?line ok = file:write_file(File, C),
+ ?line {ok, List} = epp:parse_file(File, [], []),
+ ?line [_] = [F || {attribute,_,type,{{record,r},[],[]}}=F <- List],
+ file:delete(File),
+ ?line receive _ -> fail() after 0 -> ok end,
+ ok.
+
+otp_8562(doc) ->
+ ["OTP-8503. Record with no fields is considered typed."];
+otp_8562(suite) ->
+ [];
+otp_8562(Config) when is_list(Config) ->
+ Cs = [{otp_8562,
+ <<"-define(P(), {a,b}.\n"
+ "-define(P3, .\n">>,
+ {errors,[{{1,60},epp,missing_parenthesis},
+ {{2,13},epp,missing_parenthesis}], []}}
+ ],
+ ?line [] = compile(Config, Cs),
+ ok.
+
+otp_8911(doc) ->
+ ["OTP-8911. -file and file inclusion bug"];
+otp_8911(suite) ->
+ [];
+otp_8911(Config) when is_list(Config) ->
+ ?line {ok, CWD} = file:get_cwd(),
+ ?line ok = file:set_cwd(?config(priv_dir, Config)),
+
+ File = "i.erl",
+ Cont = <<"-module(i).
+ -compile(export_all).
+ -file(\"fil1\", 100).
+ -include(\"i1.erl\").
+ t() ->
+ a.
+ ">>,
+ ?line ok = file:write_file(File, Cont),
+ Incl = <<"-file(\"fil2\", 35).
+ t1() ->
+ b.
+ ">>,
+ File1 = "i1.erl",
+ ?line ok = file:write_file(File1, Incl),
+
+ ?line {ok, i} = cover:compile(File),
+ ?line a = i:t(),
+ ?line {ok,[{{i,6},1}]} = cover:analyse(i, calls, line),
+ ?line cover:stop(),
+
+ file:delete(File),
+ file:delete(File1),
+ ?line file:set_cwd(CWD),
+ ok.
+
+otp_8665(doc) ->
+ ["OTP-8665. Bugfix premature end."];
+otp_8665(suite) ->
+ [];
+otp_8665(Config) when is_list(Config) ->
+ Cs = [{otp_8562,
+ <<"-define(A, a)\n">>,
+ {errors,[{{1,54},epp,premature_end}],[]}}
+ ],
+ ?line [] = compile(Config, Cs),
+ ok.
+
check(Config, Tests) ->
eval_tests(Config, fun check_test/2, Tests).
@@ -1155,7 +1259,7 @@ eval_tests(Config, Fun, Tests) ->
case message_compare(E, Return) of
true ->
BadL;
- false ->
+ false ->
?t:format("~nTest ~p failed. Expected~n ~p~n"
"but got~n ~p~n", [N, E, Return]),
fail()
@@ -1170,9 +1274,9 @@ check_test(Config, Test) ->
?line File = filename:join(PrivDir, Filename),
?line ok = file:write_file(File, Test),
?line case epp:parse_file(File, [PrivDir], []) of
- {ok,Forms} ->
+ {ok,Forms} ->
[E || E={error,_} <- Forms];
- {error,Error} ->
+ {error,Error} ->
Error
end.
@@ -1187,7 +1291,7 @@ compile_test(Config, Test0) ->
{ok, Ws} -> warnings(File, Ws);
Else -> Else
end.
-
+
warnings(File, Ws) ->
case lists:append([W || {F, W} <- Ws, F =:= File]) of
[] -> [];
@@ -1231,7 +1335,7 @@ message_compare(T, T) ->
message_compare(T1, T2) ->
ln(T1) =:= T2.
-%% Replaces locations like {Line,Column} with Line.
+%% Replaces locations like {Line,Column} with Line.
ln({warnings,L}) ->
{warnings,ln0(L)};
ln({errors,EL,WL}) ->
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index 8581b496aa..d0c0d68b4a 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -1784,6 +1784,9 @@ otp_5362(Config) when is_list(Config) ->
{15,erl_lint,{undefined_field,ok,nix}},
{16,erl_lint,{field_name_is_variable,ok,'Var'}}]}},
+ %% Nowarn_bif_clash has changed behaviour as local functions
+ %% nowdays supersede auto-imported BIFs, why nowarn_bif_clash in itself generates an error
+ %% (OTP-8579) /PaN
{otp_5362_4,
<<"-compile(nowarn_deprecated_function).
-compile(nowarn_bif_clash).
@@ -1795,9 +1798,8 @@ otp_5362(Config) when is_list(Config) ->
warn_deprecated_function,
warn_bif_clash]},
{error,
- [{5,erl_lint,{call_to_redefined_bif,{spawn,1}}}],
- [{3,erl_lint,{redefine_bif,{spawn,1}}},
- {4,erl_lint,{deprecated,{erlang,hash,2},{erlang,phash2,2},
+ [{5,erl_lint,{call_to_redefined_old_bif,{spawn,1}}}],
+ [{4,erl_lint,{deprecated,{erlang,hash,2},{erlang,phash2,2},
"in a future release"}}]}},
{otp_5362_5,
@@ -1808,8 +1810,8 @@ otp_5362(Config) when is_list(Config) ->
spawn(A).
">>,
{[nowarn_unused_function]},
- {warnings,
- [{3,erl_lint,{redefine_bif,{spawn,1}}}]}},
+ {errors,
+ [{2,erl_lint,disallowed_nowarn_bif_clash}],[]}},
%% The special nowarn_X are not affected by general warn_X.
{otp_5362_6,
@@ -1822,8 +1824,8 @@ otp_5362(Config) when is_list(Config) ->
{[nowarn_unused_function,
warn_deprecated_function,
warn_bif_clash]},
- {warnings,
- [{3,erl_lint,{redefine_bif,{spawn,1}}}]}},
+ {errors,
+ [{2,erl_lint,disallowed_nowarn_bif_clash}],[]}},
{otp_5362_7,
<<"-export([spawn/1]).
@@ -1838,7 +1840,9 @@ otp_5362(Config) when is_list(Config) ->
spawn(A).
">>,
{[nowarn_unused_function]},
- {error,[{4,erl_lint,{bad_nowarn_bif_clash,{spawn,2}}}],
+ {error,[{3,erl_lint,disallowed_nowarn_bif_clash},
+ {4,erl_lint,disallowed_nowarn_bif_clash},
+ {4,erl_lint,{bad_nowarn_bif_clash,{spawn,2}}}],
[{5,erl_lint,{bad_nowarn_deprecated_function,{3,hash,-1}}},
{5,erl_lint,{bad_nowarn_deprecated_function,{erlang,hash,-1}}},
{5,erl_lint,{bad_nowarn_deprecated_function,{{a,b,c},hash,-1}}}]}
@@ -1865,7 +1869,21 @@ otp_5362(Config) when is_list(Config) ->
t() -> #a{}.
">>,
{[]},
- []}
+ []},
+
+ {otp_5362_10,
+ <<"-compile({nowarn_deprecated_function,{erlang,hash,2}}).
+ -compile({nowarn_bif_clash,{spawn,1}}).
+ -import(x,[spawn/1]).
+ spin(A) ->
+ erlang:hash(A, 3000),
+ spawn(A).
+ ">>,
+ {[nowarn_unused_function,
+ warn_deprecated_function,
+ warn_bif_clash]},
+ {errors,
+ [{2,erl_lint,disallowed_nowarn_bif_clash}],[]}}
],
@@ -2234,7 +2252,7 @@ otp_5878(Config) when is_list(Config) ->
{15,erl_lint,{undefined_field,r3,q}},
{17,erl_lint,{undefined_field,r,q}},
{21,erl_lint,illegal_guard_expr},
- {23,erl_lint,illegal_guard_expr}],
+ {23,erl_lint,{illegal_guard_local_call,{l,0}}}],
[]} =
run_test2(Config, Ill1, [warn_unused_record]),
@@ -2389,9 +2407,9 @@ bif_clash(Config) when is_list(Config) ->
N.
">>,
[],
- {errors,[{2,erl_lint,{call_to_redefined_bif,{size,1}}}],[]}},
+ {errors,[{2,erl_lint,{call_to_redefined_old_bif,{size,1}}}],[]}},
- %% Verify that (some) warnings can be turned off.
+ %% Verify that warnings can not be turned off in the old way.
{clash2,
<<"-export([t/1,size/1]).
t(X) ->
@@ -2400,17 +2418,198 @@ bif_clash(Config) when is_list(Config) ->
size({N,_}) ->
N.
- %% My own abs/1 function works on lists too.
- %% Unfortunately, it is not exported, so there will
- %% be a warning that can't be turned off.
+ %% My own abs/1 function works on lists too. From R14 this really works.
abs([H|T]) when $a =< H, H =< $z -> [H-($a-$A)|abs(T)];
abs([H|T]) -> [H|abs(T)];
abs([]) -> [];
abs(X) -> erlang:abs(X).
">>,
- {[nowarn_bif_clash]},
- {warnings,[{11,erl_lint,{redefine_bif,{abs,1}}},
- {11,erl_lint,{unused_function,{abs,1}}}]}}],
+ {[nowarn_unused_function,nowarn_bif_clash]},
+ {errors,[{erl_lint,disallowed_nowarn_bif_clash}],[]}},
+ %% As long as noone calls an overridden BIF, it's totally OK
+ {clash3,
+ <<"-export([size/1]).
+ size({N,_}) ->
+ N;
+ size(X) ->
+ erlang:size(X).
+ ">>,
+ [],
+ []},
+ %% But this is totally wrong - meaning of the program changed in R14, so this is an error
+ {clash4,
+ <<"-export([size/1]).
+ size({N,_}) ->
+ N;
+ size(X) ->
+ size(X).
+ ">>,
+ [],
+ {errors,[{5,erl_lint,{call_to_redefined_old_bif,{size,1}}}],[]}},
+ %% For a post R14 bif, its only a warning
+ {clash5,
+ <<"-export([binary_part/2]).
+ binary_part({B,_},{X,Y}) ->
+ binary_part(B,{X,Y});
+ binary_part(B,{X,Y}) ->
+ binary:part(B,X,Y).
+ ">>,
+ [],
+ {warnings,[{3,erl_lint,{call_to_redefined_bif,{binary_part,2}}}]}},
+ %% If you really mean to call yourself here, you can "unimport" size/1
+ {clash6,
+ <<"-export([size/1]).
+ -compile({no_auto_import,[size/1]}).
+ size([]) ->
+ 0;
+ size({N,_}) ->
+ N;
+ size([_|T]) ->
+ 1+size(T).
+ ">>,
+ [],
+ []},
+ %% Same for the post R14 autoimport warning
+ {clash7,
+ <<"-export([binary_part/2]).
+ -compile({no_auto_import,[binary_part/2]}).
+ binary_part({B,_},{X,Y}) ->
+ binary_part(B,{X,Y});
+ binary_part(B,{X,Y}) ->
+ binary:part(B,X,Y).
+ ">>,
+ [],
+ []},
+ %% but this doesn't mean the local function is allowed in a guard...
+ {clash8,
+ <<"-export([x/1]).
+ -compile({no_auto_import,[binary_part/2]}).
+ x(X) when binary_part(X,{1,2}) =:= <<1,2>> ->
+ hej.
+ binary_part({B,_},{X,Y}) ->
+ binary_part(B,{X,Y});
+ binary_part(B,{X,Y}) ->
+ binary:part(B,X,Y).
+ ">>,
+ [],
+ {errors,[{3,erl_lint,{illegal_guard_local_call,{binary_part,2}}}],[]}},
+ %% no_auto_import is not like nowarn_bif_clash, it actually removes the autoimport
+ {clash9,
+ <<"-export([x/1]).
+ -compile({no_auto_import,[binary_part/2]}).
+ x(X) ->
+ binary_part(X,{1,2}) =:= <<1,2>>.
+ ">>,
+ [],
+ {errors,[{4,erl_lint,{undefined_function,{binary_part,2}}}],[]}},
+ %% but we could import it again...
+ {clash10,
+ <<"-export([x/1]).
+ -compile({no_auto_import,[binary_part/2]}).
+ -import(erlang,[binary_part/2]).
+ x(X) ->
+ binary_part(X,{1,2}) =:= <<1,2>>.
+ ">>,
+ [],
+ []},
+ %% and actually use it in a guard...
+ {clash11,
+ <<"-export([x/1]).
+ -compile({no_auto_import,[binary_part/2]}).
+ -import(erlang,[binary_part/2]).
+ x(X) when binary_part(X,{0,1}) =:= <<0>> ->
+ binary_part(X,{1,2}) =:= <<1,2>>.
+ ">>,
+ [],
+ []},
+ %% but for non-obvious historical reasons, imported functions cannot be used in
+ %% fun construction without the module name...
+ {clash12,
+ <<"-export([x/1]).
+ -compile({no_auto_import,[binary_part/2]}).
+ -import(erlang,[binary_part/2]).
+ x(X) when binary_part(X,{0,1}) =:= <<0>> ->
+ binary_part(X,{1,2}) =:= fun binary_part/2.
+ ">>,
+ [],
+ {errors,[{5,erl_lint,{undefined_function,{binary_part,2}}}],[]}},
+ %% Not from erlang and not from anywhere else
+ {clash13,
+ <<"-export([x/1]).
+ -compile({no_auto_import,[binary_part/2]}).
+ -import(x,[binary_part/2]).
+ x(X) ->
+ binary_part(X,{1,2}) =:= fun binary_part/2.
+ ">>,
+ [],
+ {errors,[{5,erl_lint,{undefined_function,{binary_part,2}}}],[]}},
+ %% ...while real auto-import is OK.
+ {clash14,
+ <<"-export([x/1]).
+ x(X) when binary_part(X,{0,1}) =:= <<0>> ->
+ binary_part(X,{1,2}) =:= fun binary_part/2.
+ ">>,
+ [],
+ []},
+ %% Import directive clashing with old bif is an error, regardless of if it's called or not
+ {clash15,
+ <<"-export([x/1]).
+ -import(x,[abs/1]).
+ x(X) ->
+ binary_part(X,{1,2}).
+ ">>,
+ [],
+ {errors,[{2,erl_lint,{redefine_old_bif_import,{abs,1}}}],[]}},
+ %% For a new BIF, it's only a warning
+ {clash16,
+ <<"-export([x/1]).
+ -import(x,[binary_part/3]).
+ x(X) ->
+ abs(X).
+ ">>,
+ [],
+ {warnings,[{2,erl_lint,{redefine_bif_import,{binary_part,3}}}]}},
+ %% And, you cannot redefine already imported things that aren't auto-imported
+ {clash17,
+ <<"-export([x/1]).
+ -import(x,[binary_port/3]).
+ -import(y,[binary_port/3]).
+ x(X) ->
+ abs(X).
+ ">>,
+ [],
+ {errors,[{3,erl_lint,{redefine_import,{{binary_port,3},x}}}],[]}},
+ %% Not with local functions either
+ {clash18,
+ <<"-export([x/1]).
+ -import(x,[binary_port/3]).
+ binary_port(A,B,C) ->
+ binary_part(A,B,C).
+ x(X) ->
+ abs(X).
+ ">>,
+ [],
+ {errors,[{3,erl_lint,{define_import,{binary_port,3}}}],[]}},
+ %% Like clash8: Dont accept a guard if it's explicitly module-name called either
+ {clash19,
+ <<"-export([binary_port/3]).
+ -compile({no_auto_import,[binary_part/3]}).
+ -import(x,[binary_part/3]).
+ binary_port(A,B,C) when x:binary_part(A,B,C) ->
+ binary_part(A,B,C+1).
+ ">>,
+ [],
+ {errors,[{4,erl_lint,illegal_guard_expr}],[]}},
+ %% Not with local functions either
+ {clash20,
+ <<"-export([binary_port/3]).
+ -import(x,[binary_part/3]).
+ binary_port(A,B,C) ->
+ binary_part(A,B,C).
+ ">>,
+ [warn_unused_import],
+ {warnings,[{2,erl_lint,{redefine_bif_import,{binary_part,3}}}]}}
+ ],
?line [] = run(Config, Ts),
ok.
diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl
index 0a119d1e38..c57541fba9 100644
--- a/lib/stdlib/test/erl_pp_SUITE.erl
+++ b/lib/stdlib/test/erl_pp_SUITE.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%%----------------------------------------------------------------
@@ -45,7 +45,8 @@
hook/1,
neg_indent/1,
tickets/1,
- otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1]).
+ otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1,
+ otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1]).
%% Internal export.
-export([ehook/6]).
@@ -149,15 +150,15 @@ recs(Config) when is_list(Config) ->
(A#r1.a)#r.a > 3 -> 3
end(#r1{a = #r{a = 4}}),
7 = fun(A) when record(A#r3.a, r1) -> 7 end(#r3{}),
- [#r1{a = 2,b = 1}] =
+ [#r1{a = 2,b = 1}] =
fun() ->
- [A || A <- [#r1{a = 1, b = 3},
- #r2{a = 2,b = 1},
+ [A || A <- [#r1{a = 1, b = 3},
+ #r2{a = 2,b = 1},
#r1{a = 2, b = 1}],
- A#r1.a >
+ A#r1.a >
A#r1.b]
end(),
- {[_],b} =
+ {[_],b} =
fun(L) ->
%% A is checked only once:
R1 = [{A,B} || A <- L, A#r1.a, B <- L, A#r1.b],
@@ -176,7 +177,7 @@ recs(Config) when is_list(Config) ->
end(#r1{a = 2}),
%% The test done twice (an effect of doing the test as soon as possible).
- 3 = fun(A) when A#r1.a > 3,
+ 3 = fun(A) when A#r1.a > 3,
record(A, r1) -> 3
end(#r1{a = 5}),
@@ -250,18 +251,18 @@ recs(Config) when is_list(Config) ->
ok
end(),
- both = fun(A) when A#r.a, A#r.b -> both
+ both = fun(A) when A#r.a, A#r.b -> both
end(#r{a = true, b = true}),
ok = fun() ->
- F = fun(A, B) when ((A#r1.a) orelse (B#r2.a))
+ F = fun(A, B) when ((A#r1.a) orelse (B#r2.a))
or (B#r2.b) or (A#r1.b) ->
true;
(_, _) -> false
end,
- true = F(#r1{a = false, b = false},
+ true = F(#r1{a = false, b = false},
#r2{a = false, b = true}),
- false = F(#r1{a = true, b = true},
+ false = F(#r1{a = true, b = true},
#r1{a = false, b = true}),
ok
end(),
@@ -272,7 +273,7 @@ recs(Config) when is_list(Config) ->
<<"-record(r1, {a, b = foo:bar(kljlfjsdlf, kjlksdjf)}).
-record(r2, {c = #r1{}, d = #r1{a = bar:foo(kljklsjdf)}}).
- t() ->
+ t() ->
R = #r2{},
R#r2{c = R, d = #r1{}}.">>}
],
@@ -303,10 +304,10 @@ try_catch(Config) when is_list(Config) ->
{try_6,
<<"t() -> try 1=2 catch throw:{badmatch,2} -> 3 end.">>},
{try_7,
- <<"t() -> try 1=2 of 3 -> 4
+ <<"t() -> try 1=2 of 3 -> 4
catch error:{badmatch,2} -> 5 end.">>},
{try_8,
- <<"t() -> try 1=2
+ <<"t() -> try 1=2
catch error:{badmatch,2} -> 3
after put(try_catch, 4) end.">>},
{try_9,
@@ -370,7 +371,7 @@ receive_after(Config) when is_list(Config) ->
{X,Y};
Z ->
Z
- after
+ after
foo:bar() ->
{3,4}
end.">>}
@@ -428,7 +429,7 @@ head_tail(Config) when is_list(Config) ->
{list_4,
<<"t() -> [a].">>},
{list_5,
- <<"t() ->
+ <<"t() ->
[foo:bar(lkjljlskdfj, klsdajflds, sdafkljsdlfkjdas, kjlsdadjl),
bar:foo(kljlkjsdf, lkjsdlfj, [kljsfj, sdfdsfsad])].">>}
],
@@ -461,7 +462,7 @@ cond1(Config) when is_list(Config) ->
" true ->\n"
" {x,y}\n"
"end" = CChars,
-% ?line ok = pp_expr(<<"cond
+% ?line ok = pp_expr(<<"cond
% {foo,bar} ->
% [a,b];
% true ->
@@ -543,7 +544,7 @@ old_mnemosyne_syntax(Config) when is_list(Config) ->
" X <- table(tab),\n"
" X.foo = bar\n"
" ]\n"
- "end" =
+ "end" =
lists:flatten(erl_pp:expr(Q)),
R = {rule,12,sales,2,
@@ -558,7 +559,7 @@ old_mnemosyne_syntax(Config) when is_list(Config) ->
{atom,14,sales}}]}]},
?line "sales(E, employee) :-\n"
" E <- table(employee),\n"
- " E.salary = sales.\n" =
+ " E.salary = sales.\n" =
lists:flatten(erl_pp:form(R)),
ok.
@@ -659,7 +660,7 @@ hook(Config) when is_list(Config) ->
?line "INVALID-FORM:{foo,bar}:" = lists:flatten(erl_pp:expr({foo,bar})),
- %% A list (as before R6), not a list of lists.
+ %% A list (as before R6), not a list of lists.
G = [{op,1,'>',{atom,1,a},{foo,{atom,1,b}}}], % not a proper guard
GChars = lists:flatten(erl_pp:guard(G, H)),
G2 = [{op,1,'>',{atom,1,a},
@@ -676,23 +677,23 @@ hook(Config) when is_list(Config) ->
%% Note: no leading spaces before "begin".
Block = {block,0,[{match,0,{var,0,'A'},{integer,0,3}},
{atom,0,true}]},
- ?line "begin\n A =" ++ _ =
+ ?line "begin\n A =" ++ _ =
lists:flatten(erl_pp:expr(Block, 17, none)),
%% Special...
- ?line true =
+ ?line true =
"{some,value}" =:= lists:flatten(erl_pp:expr({value,0,{some,value}})),
%% Silly...
?line true =
- "if true -> 0 end" =:=
+ "if true -> 0 end" =:=
flat_expr({'if',0,[{clause,0,[],[],[{atom,0,0}]}]}),
%% More compatibility: before R6
OldIf = {'if',0,[{clause,0,[],[{atom,0,true}],[{atom,0,b}]}]},
NewIf = {'if',0,[{clause,0,[],[[{atom,0,true}]],[{atom,0,b}]}]},
OldIfChars = lists:flatten(erl_pp:expr(OldIf)),
- NewIfChars = lists:flatten(erl_pp:expr(NewIf)),
+ NewIfChars = lists:flatten(erl_pp:expr(NewIf)),
?line true = OldIfChars =:= NewIfChars,
ok.
@@ -705,7 +706,7 @@ remove_indentation(S) ->
ehook(HE, I, P, H, foo, bar) ->
hook(HE, I, P, H).
-hook({foo,E}, I, P, H) ->
+hook({foo,E}, I, P, H) ->
erl_pp:expr({call,0,{atom,0,foo},[E]}, I, P, H).
neg_indent(suite) ->
@@ -721,14 +722,14 @@ neg_indent(Config) when is_list(Config) ->
end">>),
?line ok = pp_expr(
<<"fun() ->
- F = fun(A, B) when ((A#r1.a) orelse (B#r2.a))
+ F = fun(A, B) when ((A#r1.a) orelse (B#r2.a))
or (B#r2.b) or (A#r1.b) ->
true;
(_, _) -> false
end,
- true = F(#r1{a = false, b = false},
+ true = F(#r1{a = false, b = false},
#r2{a = false, b = true}),
- false = F(#r1{a = true, b = true},
+ false = F(#r1{a = true, b = true},
#r1{a = false, b = true}),
ok
end()">>),
@@ -763,7 +764,8 @@ neg_indent(Config) when is_list(Config) ->
ok.
tickets(suite) ->
- [otp_6321, otp_6911, otp_6914, otp_8150, otp_8238].
+ [otp_6321, otp_6911, otp_6914, otp_8150, otp_8238, otp_8473, otp_8522,
+ otp_8567, otp_8664].
otp_6321(doc) ->
"OTP_6321. Bug fix of exprs().";
@@ -812,7 +814,7 @@ otp_8150(doc) ->
"OTP_8150. Types.";
otp_8150(suite) -> [];
otp_8150(Config) when is_list(Config) ->
- ?line _ = [{N,ok} = {N,pp_forms(B)} ||
+ ?line _ = [{N,ok} = {N,pp_forms(B)} ||
{N,B} <- type_examples()
],
ok.
@@ -846,7 +848,7 @@ type_examples() ->
{ex4,<<"-type t1() :: atom(). ">>},
{ex5,<<"-type t2() :: [t1()]. ">>},
{ex6,<<"-type t3(Atom) :: integer(Atom). ">>},
- {ex7,<<"-type t4() :: t3(foobar). ">>},
+ {ex7,<<"-type '\\'t::4'() :: t3('\\'foobar'). ">>},
{ex8,<<"-type t5() :: {t1(), t3(foo)}. ">>},
{ex9,<<"-type t6() :: 1 | 2 | 3 | 'foo' | 'bar'. ">>},
{ex10,<<"-type t7() :: []. ">>},
@@ -882,16 +884,16 @@ type_examples() ->
"1|2|3|4|a|b|c|d| "
"nonempty_maybe_improper_list(integer, any())]}. ">>},
{ex30,<<"-type t99() ::"
- "{t2(),t4(),t5(),t6(),t7(),t8(),t10(),t14(),"
+ "{t2(),'\\'t::4'(),t5(),t6(),t7(),t8(),t10(),t14(),"
"t15(),t20(),t21(), t22(),t25()}. ">>},
{ex31,<<"-spec t1(FooBar :: t99()) -> t99();"
"(t2()) -> t2();"
- "(t4()) -> t4() when is_subtype(t4(), t24);"
+ "('\\'t::4'()) -> '\\'t::4'() when is_subtype('\\'t::4'(), t24);"
"(t23()) -> t23() when is_subtype(t23(), atom()),"
" is_subtype(t23(), t14());"
"(t24()) -> t24() when is_subtype(t24(), atom()),"
" is_subtype(t24(), t14()),"
- " is_subtype(t24(), t4()).">>},
+ " is_subtype(t24(), '\\'t::4'()).">>},
{ex32,<<"-spec mod:t2() -> any(). ">>},
{ex33,<<"-opaque attributes_data() :: "
"[{'column', column()} | {'line', info_line()} |"
@@ -911,12 +913,126 @@ type_examples() ->
"f19 = 3 :: integer()|undefined,"
"f5 = 3 :: undefined|integer()}). ">>}].
+otp_8473(doc) ->
+ "OTP_8473. Bugfix abstract type 'fun'.";
+otp_8473(suite) -> [];
+otp_8473(Config) when is_list(Config) ->
+ Ex = [{ex1,<<"-type 'fun'(A) :: A.\n"
+ "-type funkar() :: 'fun'(fun((integer()) -> atom())).\n">>}],
+ ?line _ = [{N,ok} = {N,pp_forms(B)} ||
+ {N,B} <- Ex],
+ ok.
+
+otp_8522(doc) ->
+ "OTP_8522. Avoid duplicated 'undefined' in record field types.";
+otp_8522(suite) -> [];
+otp_8522(Config) when is_list(Config) ->
+ FileName = filename('otp_8522.erl', Config),
+ C = <<"-module(otp_8522).\n"
+ "-record(r, {f1 :: undefined,\n"
+ " f2 :: A :: undefined,\n"
+ " f3 :: (undefined),\n"
+ " f4 :: x | y | undefined | z,\n"
+ " f5 :: a}).\n">>,
+ ?line ok = file:write_file(FileName, C),
+ ?line {ok, _} = compile:file(FileName, [{outdir,?privdir},debug_info]),
+ BF = filename("otp_8522", Config),
+ ?line {ok, A} = beam_lib:chunks(BF, [abstract_code]),
+ ?line 5 = count_atom(A, undefined),
+ ok.
+
+count_atom(A, A) ->
+ 1;
+count_atom(T, A) when is_tuple(T) ->
+ count_atom(tuple_to_list(T), A);
+count_atom(L, A) when is_list(L) ->
+ lists:sum([count_atom(T, A) || T <- L]);
+count_atom(_, _) ->
+ 0.
+
+otp_8567(doc) ->
+ "OTP_8567. Avoid duplicated 'undefined' in record field types.";
+otp_8567(suite) -> [];
+otp_8567(Config) when is_list(Config) ->
+ FileName = filename('otp_8567.erl', Config),
+ C = <<"-module otp_8567.\n"
+ "-compile export_all.\n"
+ "-spec(a).\n"
+ "-record r, {a}.\n"
+ "-record s, {a :: integer()}.\n"
+ "-type t() :: {#r{},#s{}}.\n">>,
+ ?line ok = file:write_file(FileName, C),
+ ?line {error,[{_,[{3,erl_parse,["syntax error before: ","')'"]}]}],_} =
+ compile:file(FileName, [return]),
+
+ F = <<"-module(otp_8567).\n"
+ "-compile(export_all).\n"
+ "-record(t, {a}).\n"
+ "-record(u, {a :: integer()}).\n"
+ "-opaque ot() :: {#t{}, #u{}}.\n"
+ "-opaque(ot1() :: atom()).\n"
+ "-type a() :: integer().\n"
+ "-spec t() -> a().\n"
+ "t() ->\n"
+ " 3.\n"
+ "\n"
+ "-spec(t1/1 :: (ot()) -> ot1()).\n"
+ "t1(A) ->\n"
+ " A.\n"
+ "\n"
+ "-spec(t2 (ot()) -> ot1()).\n"
+ "t2(A) ->\n"
+ " A.\n"
+ "\n"
+ "-spec(otp_8567:t3/1 :: (ot()) -> ot1()).\n"
+ "t3(A) ->\n"
+ " A.\n"
+ "\n"
+ "-spec(otp_8567:t4 (ot()) -> ot1()).\n"
+ "t4(A) ->\n"
+ " A.\n">>,
+ ?line ok = pp_forms(F),
+
+ ok.
+
+otp_8664(doc) ->
+ "OTP_8664. Types with integer expressions.";
+otp_8664(suite) -> [];
+otp_8664(Config) when is_list(Config) ->
+ FileName = filename('otp_8664.erl', Config),
+ C1 = <<"-module(otp_8664).\n"
+ "-export([t/0]).\n"
+ "-define(A, -3).\n"
+ "-define(B, (?A*(-1 band (((2)))))).\n"
+ "-type t1() :: ?B | ?A.\n"
+ "-type t2() :: ?B-1 .. -?B.\n"
+ "-type t3() :: 9 band (8 - 3) | 1+2 | 5 band 3.\n"
+ "-type b1() :: <<_:_*(3-(-1))>>\n"
+ " | <<_:(-(?B))>>\n"
+ " | <<_:4>>.\n"
+ "-type u() :: 1 .. 2 | 3.. 4 | (8-3) ..6 | 5+0..6.\n"
+ "-type t() :: t1() | t2() | t3() | b1() | u().\n"
+ "-spec t() -> t().\n"
+ "t() -> 3.\n">>,
+ ?line ok = file:write_file(FileName, C1),
+ ?line {ok, _, []} = compile:file(FileName, [return]),
+
+ C2 = <<"-module(otp_8664).\n"
+ "-export([t/0]).\n"
+ "-spec t() -> 9 and 4.\n"
+ "t() -> 0.\n">>,
+ ?line ok = file:write_file(FileName, C2),
+ ?line {error,[{_,[{3,erl_lint,{type_syntax,integer}}]}],_} =
+ compile:file(FileName, [return]),
+
+ ok.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
compile(Config, Tests) ->
F = fun({N,P}, BadL) ->
case catch compile_file(Config, P) of
- ok ->
+ ok ->
case pp_forms(P) of
ok ->
BadL;
@@ -924,8 +1040,8 @@ compile(Config, Tests) ->
?t:format("~nTest ~p failed.~n", [N]),
fail()
end;
- Bad ->
- ?t:format("~nTest ~p failed. got~n ~p~n",
+ Bad ->
+ ?t:format("~nTest ~p failed. got~n ~p~n",
[N, Bad]),
fail()
end
@@ -955,7 +1071,7 @@ compile_file(Config, Test0) ->
Error ->
Error
end.
-
+
compile_file(Config, Test0, Opts0) ->
FileName = filename('erl_pp_test.erl', Config),
Test = list_to_binary(["-module(erl_pp_test). "
@@ -964,7 +1080,7 @@ compile_file(Config, Test0, Opts0) ->
Opts = [export_all,return,nowarn_unused_record,{outdir,?privdir} | Opts0],
ok = file:write_file(FileName, Test),
case compile:file(FileName, Opts) of
- {ok, _M, _Ws} ->
+ {ok, _M, _Ws} ->
{ok, filename:rootname(FileName)};
Error -> Error
end.
@@ -991,7 +1107,7 @@ pp_forms(Bin, Hook) ->
end.
parse_and_pp_forms(String, Hook) ->
- lists:append(lists:map(fun(AF) -> erl_pp:form(AF, Hook)
+ lists:append(lists:map(fun(AF) -> erl_pp:form(AF, Hook)
end, parse_forms(String))).
parse_forms(Chars) ->
@@ -999,13 +1115,13 @@ parse_forms(Chars) ->
parse_forms2(String, [], 1, []).
parse_forms2([], _Cont, _Line, Forms) ->
- lists:reverse(Forms);
+ lists:reverse(Forms);
parse_forms2(String, Cont0, Line, Forms) ->
case erl_scan:tokens(Cont0, String, Line) of
{done, {ok, Tokens, EndLine}, Chars} ->
{ok, Form} = erl_parse:parse_form(Tokens),
parse_forms2(Chars, [], EndLine, [Form | Forms]);
- {more, Cont} when element(3, Cont) =:= [] ->
+ {more, Cont} when element(3, Cont) =:= [] ->
%% extra spaces after forms...
parse_forms2([], Cont, Line, Forms);
{more, Cont} ->
@@ -1023,10 +1139,10 @@ pp_expr(Bin, Hook) ->
PP2 = (catch parse_and_pp_expr(PPneg, 0, Hook)),
if
PP1 =:= PP2 -> % same line numbers
- case
+ case
(test_max_line(PP1) =:= ok) and (test_new_line(PPneg) =:= ok)
of
- true ->
+ true ->
ok;
false ->
not_ok
@@ -1059,7 +1175,7 @@ test_max_line(String) ->
end.
max_line(String) ->
- lists:max([0 | [length(Sub) ||
+ lists:max([0 | [length(Sub) ||
Sub <- string:tokens(String, "\n"),
string:substr(Sub, 1, 5) =/= "-file"]]).
diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl
index 32a06d15c7..32eb97bc92 100644
--- a/lib/stdlib/test/erl_scan_SUITE.erl
+++ b/lib/stdlib/test/erl_scan_SUITE.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
-module(erl_scan_SUITE).
@@ -34,7 +34,7 @@
-define(line, put(line, ?LINE), ).
-define(config(A,B),config(A,B)).
-define(t, test_server).
-%% config(priv_dir, _) ->
+%% config(priv_dir, _) ->
%% ".";
%% config(data_dir, _) ->
%% ".".
@@ -45,7 +45,7 @@
init_per_testcase(_Case, Config) when is_list(Config) ->
?line Dog=test_server:timetrap(test_server:seconds(1200)),
[{watchdog, Dog}|Config].
-
+
fin_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
@@ -97,7 +97,7 @@ assert_type(N, integer) when is_integer(N) ->
ok;
assert_type(N, atom) when is_atom(N) ->
ok.
-
+
check(String) ->
Error = erl_scan:string(String),
check_error(Error, erl_scan).
@@ -146,7 +146,7 @@ iso88591(Config) when is_list(Config) ->
ok -> ok %Aok
end.
-otp_7810(doc) ->
+otp_7810(doc) ->
["OTP-7810. White spaces, comments, and more.."];
otp_7810(suite) ->
[];
@@ -185,7 +185,7 @@ reserved_words() ->
'andalso', 'orelse', 'end', 'fun', 'if', 'let', 'of',
'query', 'receive', 'when', 'bnot', 'not', 'div',
'rem', 'band', 'and', 'bor', 'bxor', 'bsl', 'bsr',
- 'or', 'xor', 'spec'] , % 'spec' shouldn't be there...
+ 'or', 'xor'],
[begin
?line {RW, true} = {RW, erl_scan:reserved_word(RW)},
S = atom_to_list(RW),
@@ -203,7 +203,7 @@ atoms() ->
?line test_string("a@2", [{atom,1,a@2}]),
?line test_string([39,65,200,39], [{atom,1,'A�'}]),
?line test_string("�rlig �sten", [{atom,1,�rlig},{atom,1,�sten}]),
- ?line {ok,[{atom,_,'$a'}],{1,6}} =
+ ?line {ok,[{atom,_,'$a'}],{1,6}} =
erl_scan:string("'$\\a'", {1,1}),
?line test("'$\\a'"),
ok.
@@ -221,8 +221,8 @@ punctuations() ->
Three = ["/=:=", "<=:=", "==:=", ">=:="], % three tokens...
No = Three ++ L,
SL0 = [{S1++S2,{-length(S1),S1,S2}} ||
- S1 <- L,
- S2 <- L,
+ S1 <- L,
+ S2 <- L,
not lists:member(S1++S2, No)],
SL = family_list(SL0),
%% Two tokens. When there are several answers, the one with
@@ -244,21 +244,24 @@ punctuations() ->
{'\\',1},{'^',1},{'`',1},{'~',1}],
?line test_string("#&*+/:<>?@\\^`~", PTs2),
+ ?line test_string(".. ", [{'..',1}]),
+ ?line test("1 .. 2"),
+ ?line test_string("...", [{'...',1}]),
ok.
comments() ->
?line test("a %%\n b"),
?line {ok,[],1} = erl_scan:string("%"),
?line test("a %%\n b"),
- ?line {ok,[{atom,_,a},{atom,_,b}],{2,3}} =
+ ?line {ok,[{atom,_,a},{atom,_,b}],{2,3}} =
erl_scan:string("a %%\n b",{1,1}),
- ?line {ok,[{atom,_,a},{comment,_,"%%"},{atom,_,b}],{2,3}} =
+ ?line {ok,[{atom,_,a},{comment,_,"%%"},{atom,_,b}],{2,3}} =
erl_scan:string("a %%\n b",{1,1}, [return_comments]),
?line {ok,[{atom,_,a},
{white_space,_," "},
{white_space,_,"\n "},
{atom,_,b}],
- {2,3}} =
+ {2,3}} =
erl_scan:string("a %%\n b",{1,1},[return_white_spaces]),
?line {ok,[{atom,_,a},
{white_space,_," "},
@@ -275,14 +278,14 @@ errors() ->
?line {error,{1,erl_scan,char},1} = erl_scan:string("$"),
?line test_string([34,65,200,34], [{string,1,"A�"}]),
?line test_string("\\", [{'\\',1}]),
- ?line {'EXIT',_} =
+ ?line {'EXIT',_} =
(catch {foo, erl_scan:string('$\\a', {1,1})}), % type error
- ?line {'EXIT',_} =
+ ?line {'EXIT',_} =
(catch {foo, erl_scan:tokens([], '$\\a', {1,1})}), % type error
?line "{a,tuple}" = erl_scan:format_error({a,tuple}),
ok.
-
+
integers() ->
[begin
I = list_to_integer(S),
@@ -299,14 +302,14 @@ base_integers() ->
?line test_string(BS++"#"++S, Ts)
end || {BS,S} <- [{"2","11"}, {"5","23234"}, {"12","05a"},
{"16","abcdef"}, {"16","ABCDEF"}] ],
-
+
?line {error,{1,erl_scan,{base,1}},1} = erl_scan:string("1#000"),
-
+
?line test_string("12#bc", [{integer,1,11},{atom,1,c}]),
-
+
[begin
Str = BS ++ "#" ++ S,
- ?line {error,{1,erl_scan,{illegal,integer}},1} =
+ ?line {error,{1,erl_scan,{illegal,integer}},1} =
erl_scan:string(Str)
end || {BS,S} <- [{"3","3"},{"15","f"}, {"12","c"}] ],
@@ -323,8 +326,8 @@ floats() ->
end || FS <- ["1.0","001.17","3.31200","1.0e0","1.0E17",
"34.21E-18", "17.0E+14"]],
?line test_string("1.e2", [{integer,1,1},{'.',1},{atom,1,e2}]),
-
- ?line {error,{1,erl_scan,{illegal,float}},1} =
+
+ ?line {error,{1,erl_scan,{illegal,float}},1} =
erl_scan:string("1.0e400"),
[begin
?line {error,{1,erl_scan,{illegal,float}},1} = erl_scan:string(S)
@@ -345,16 +348,16 @@ dots() ->
{".a", {ok,[{'.',1},{atom,1,a}],1}}
],
?line [R = erl_scan:string(S) || {S, R} <- Dot],
-
+
?line {ok,[{dot,_}=T1],{1,2}} = erl_scan:string(".", {1,1}, text),
- ?line [{column,1},{length,1},{line,1},{text,"."}] =
+ ?line [{column,1},{length,1},{line,1},{text,"."}] =
erl_scan:token_info(T1, [column, length, line, text]),
?line {ok,[{dot,_}=T2],{1,3}} = erl_scan:string(".%", {1,1}, text),
- ?line [{column,1},{length,1},{line,1},{text,"."}] =
+ ?line [{column,1},{length,1},{line,1},{text,"."}] =
erl_scan:token_info(T2, [column, length, line, text]),
?line {ok,[{dot,_}=T3],{1,6}} =
erl_scan:string(".% �h", {1,1}, text),
- ?line [{column,1},{length,1},{line,1},{text,"."}] =
+ ?line [{column,1},{length,1},{line,1},{text,"."}] =
erl_scan:token_info(T3, [column, length, line, text]),
?line {error,{{1,2},erl_scan,char},{1,3}} =
erl_scan:string(".$", {1,1}),
@@ -376,10 +379,10 @@ dots() ->
?line {done,{ok,[{comment,_,"%. "},
{white_space,_,"\n"},
{dot,_}],
- {2,3}}, ""} =
+ {2,3}}, ""} =
erl_scan:tokens(C, "\n. ", {1,1}, return), % any loc, any options
- ?line [test_string(S, R) ||
+ ?line [test_string(S, R) ||
{S, R} <- [{".$\n", [{'.',1},{char,1,$\n}]},
{"$\\\n", [{char,1,$\n}]},
{"'\\\n'", [{atom,1,'\n'}]},
@@ -392,7 +395,7 @@ chars() ->
Ts = [{char,1,C}],
?line test_string(L, Ts)
end || C <- lists:seq(0, 255)],
-
+
%% Leading zeroes...
[begin
L = lists:flatten(io_lib:format("$\\~3.8.0b", [C])),
@@ -406,13 +409,13 @@ chars() ->
Ts = [{char,1,C band 2#11111}],
?line test_string(L, Ts)
end || C <- lists:seq(0, 255)],
-
+
[begin
L = "$\\" ++ [C],
Ts = [{char,1,V}],
?line test_string(L, Ts)
- end || {C,V} <- [{$n,$\n}, {$r,$\r}, {$t,$\t}, {$v,$\v},
- {$b,$\b}, {$f,$\f}, {$e,$\e}, {$s,$\s},
+ end || {C,V} <- [{$n,$\n}, {$r,$\r}, {$t,$\t}, {$v,$\v},
+ {$b,$\b}, {$f,$\f}, {$e,$\e}, {$s,$\s},
{$d,$\d}]],
EC = [$\n,$\r,$\t,$\v,$\b,$\f,$\e,$\s,$\d],
@@ -445,7 +448,7 @@ chars() ->
end || C <- lists:seq(0, 255) -- (No ++ [$\\])],
?line test_string("$\n", [{char,1,$\n}]),
- ?line {error,{{1,1},erl_scan,char},{1,4}} =
+ ?line {error,{{1,1},erl_scan,char},{1,4}} =
erl_scan:string("$\\^",{1,1}),
?line test_string("$\\\n", [{char,1,$\n}]),
%% Robert's scanner returns line 1:
@@ -453,7 +456,7 @@ chars() ->
?line test_string("$\n\n", [{char,1,$\n}]),
?line test("$\n\n"),
ok.
-
+
variables() ->
?line test_string(" \237_Aou�eiy��", [{var,1,'_Aou�eiy��'}]),
@@ -469,8 +472,8 @@ eof() ->
?line {done,{eof,2},eof} = erl_scan:tokens(C1, eof, 1),
{more, C2} = erl_scan:tokens([], "abra", 1),
%% An error before R13A.
- %% ?line {done,Err={error,{1,erl_scan,scan},1},eof} =
- ?line {done,{ok,[{atom,1,abra}],1},eof} =
+ %% ?line {done,Err={error,{1,erl_scan,scan},1},eof} =
+ ?line {done,{ok,[{atom,1,abra}],1},eof} =
erl_scan:tokens(C2, eof, 1),
%% With column.
@@ -478,7 +481,7 @@ eof() ->
?line {done,{eof,{2,1}},eof} = erl_scan:tokens(C3, eof, 1),
{more, C4} = erl_scan:tokens([], "abra", {1,1}),
%% An error before R13A.
- %% ?line {done,{error,{{1,1},erl_scan,scan},{1,5}},eof} =
+ %% ?line {done,{error,{{1,1},erl_scan,scan},{1,5}},eof} =
?line {done,{ok,[{atom,_,abra}],{1,5}},eof} =
erl_scan:tokens(C4, eof, 1),
@@ -486,7 +489,7 @@ eof() ->
%% the R12B scanner returns eof as LeftoverChars: (eof is correct)
?line {more, C5} = erl_scan:tokens([], "a", 1),
%% An error before R13A.
- %% ?line {done,{error,{1,erl_scan,scan},1},eof} =
+ %% ?line {done,{error,{1,erl_scan,scan},1},eof} =
?line {done,{ok,[{atom,1,a}],1},eof} =
erl_scan:tokens(C5,eof,1),
@@ -528,7 +531,7 @@ illegal() ->
erl_scan:tokens([], "foo "++Atom++". ", {1,1}),
?line {error,{{1,1},erl_scan,{illegal,atom}},{1,1003}} =
erl_scan:string(QAtom, {1,1}),
- ?line {done,{error,{{1,5},erl_scan,{illegal,atom}},{1,1007}},". "} =
+ ?line {done,{error,{{1,5},erl_scan,{illegal,atom}},{1,1007}},". "} =
erl_scan:tokens([], "foo "++QAtom++". ", {1,1}),
?line {error,{{1,1},erl_scan,{illegal,var}},{1,1001}} =
erl_scan:string(Var, {1,1}),
@@ -553,7 +556,7 @@ crashes() ->
?line {'EXIT',_} = (catch {foo, erl_scan:string("\"\\v"++[-1,$"])}), %$"
?line {'EXIT',_} = (catch {foo, erl_scan:string([$",-1,$"])}),
?line {'EXIT',_} = (catch {foo, erl_scan:string("% foo"++[-1])}),
- ?line {'EXIT',_} =
+ ?line {'EXIT',_} =
(catch {foo, erl_scan:string("% foo"++[-1],{1,1})}),
?line {'EXIT',_} = (catch {foo, erl_scan:string([a])}), % type error
@@ -564,7 +567,7 @@ crashes() ->
?line {'EXIT',_} = (catch {foo, erl_scan:string("\"\\v"++[a,$"])}), %$"
?line {'EXIT',_} = (catch {foo, erl_scan:string([$",a,$"])}),
?line {'EXIT',_} = (catch {foo, erl_scan:string("% foo"++[a])}),
- ?line {'EXIT',_} =
+ ?line {'EXIT',_} =
(catch {foo, erl_scan:string("% foo"++[a],{1,1})}),
?line {'EXIT',_} = (catch {foo, erl_scan:string([3.0])}), % type error
@@ -573,26 +576,26 @@ crashes() ->
options() ->
%% line and column are not options, but tested here
- ?line {ok,[{atom,1,foo},{white_space,1," "},{comment,1,"% bar"}], 1} =
+ ?line {ok,[{atom,1,foo},{white_space,1," "},{comment,1,"% bar"}], 1} =
erl_scan:string("foo % bar", 1, return),
- ?line {ok,[{atom,1,foo},{white_space,1," "}],1} =
+ ?line {ok,[{atom,1,foo},{white_space,1," "}],1} =
erl_scan:string("foo % bar", 1, return_white_spaces),
- ?line {ok,[{atom,1,foo},{comment,1,"% bar"}],1} =
+ ?line {ok,[{atom,1,foo},{comment,1,"% bar"}],1} =
erl_scan:string("foo % bar", 1, return_comments),
- ?line {ok,[{atom,17,foo}],17} =
+ ?line {ok,[{atom,17,foo}],17} =
erl_scan:string("foo % bar", 17),
- ?line {'EXIT',{function_clause,_}} =
- (catch {foo,
+ ?line {'EXIT',{function_clause,_}} =
+ (catch {foo,
erl_scan:string("foo % bar", {a,1}, [])}), % type error
- ?line {ok,[{atom,_,foo}],{17,18}} =
+ ?line {ok,[{atom,_,foo}],{17,18}} =
erl_scan:string("foo % bar", {17,9}, []),
- ?line {'EXIT',{function_clause,_}} =
+ ?line {'EXIT',{function_clause,_}} =
(catch {foo,
erl_scan:string("foo % bar", {1,0}, [])}), % type error
- ?line {ok,[{foo,1}],1} =
+ ?line {ok,[{foo,1}],1} =
erl_scan:string("foo % bar",1, [{reserved_word_fun,
fun(W) -> W =:= foo end}]),
- ?line {'EXIT',{badarg,_}} =
+ ?line {'EXIT',{badarg,_}} =
(catch {foo,
erl_scan:string("foo % bar",1, % type error
[{reserved_word_fun,
@@ -618,14 +621,14 @@ more_options() ->
token_info() ->
?line {ok,[T1],_} = erl_scan:string("foo", {1,18}, [text]),
- {'EXIT',{badarg,_}} =
+ {'EXIT',{badarg,_}} =
(catch {foo, erl_scan:token_info(T1, foo)}), % type error
?line {line,1} = erl_scan:token_info(T1, line),
?line {column,18} = erl_scan:token_info(T1, column),
?line {length,3} = erl_scan:token_info(T1, length),
?line {text,"foo"} = erl_scan:token_info(T1, text),
?line [{category,atom},{column,18},{length,3},{line,1},
- {symbol,foo},{text,"foo"}] =
+ {symbol,foo},{text,"foo"}] =
erl_scan:token_info(T1),
?line [{length,3},{column,18}] =
erl_scan:token_info(T1, [length, column]),
@@ -648,9 +651,9 @@ token_info() ->
?line {category,'='} = erl_scan:token_info(T3, category),
?line [{symbol,'='}] = erl_scan:token_info(T3, [symbol]),
ok.
-
+
attributes_info() ->
- ?line {'EXIT',_} =
+ ?line {'EXIT',_} =
(catch {foo,erl_scan:attributes_info(foo)}), % type error
?line [{line,18}] = erl_scan:attributes_info(18),
?line {location,19} = erl_scan:attributes_info(19, location),
@@ -717,9 +720,9 @@ set_attribute() ->
?line [{line,{17,11}},{text,"foo"}] =
erl_scan:attributes_info(A7, [line,column,text]),
- ?line {'EXIT',_} =
+ ?line {'EXIT',_} =
(catch {foo, erl_scan:set_attribute(line, [], F2)}), % type error
- ?line {'EXIT',{badarg,_}} =
+ ?line {'EXIT',{badarg,_}} =
(catch {foo, erl_scan:set_attribute(column, [], F2)}), % type error
ok.
@@ -790,14 +793,14 @@ unicode() ->
?line {ok,[{char,1,83},{integer,1,45}],1} =
erl_scan:string("$\\12345"), % not unicode
- ?line {error,{1,erl_scan,{illegal,character}},1} =
+ ?line {error,{1,erl_scan,{illegal,character}},1} =
erl_scan:string([1089]),
- ?line {error,{{1,1},erl_scan,{illegal,character}},{1,2}} =
+ ?line {error,{{1,1},erl_scan,{illegal,character}},{1,2}} =
erl_scan:string([1089], {1,1}),
?line {error,{1,erl_scan,{illegal,character}},1} =
- %% ?line {error,{1,erl_scan,{illegal,atom}},1} =
+ %% ?line {error,{1,erl_scan,{illegal,atom}},1} =
erl_scan:string("'a"++[1089]++"b'"),
- ?line {error,{{1,3},erl_scan,{illegal,character}},{1,4}} =
+ ?line {error,{{1,3},erl_scan,{illegal,character}},{1,4}} =
erl_scan:string("'a"++[1089]++"b'", {1,1}),
?line test("\"a"++[1089]++"b\""),
?line {ok,[{char,1,1}],1} = erl_scan:string([$$,$\\,$^,1089]),
@@ -822,7 +825,7 @@ unicode() ->
?line {ok,[{integer,1,16#aaa}],1} = erl_scan:string(Qs),
?line {ok,[Q2],{1,9}} = erl_scan:string("$\\x{aaa}", {1,1}, text),
?line [{category,integer},{column,1},{length,8},
- {line,1},{symbol,16#aaa},{text,Qs}] =
+ {line,1},{symbol,16#aaa},{text,Qs}] =
erl_scan:token_info(Q2),
U1 = "\"\\x{aaa}\"",
@@ -830,11 +833,11 @@ unicode() ->
?line [{category,'['},{column,1},{length,1},{line,1},
{symbol,'['},{text,"\""}] = erl_scan:token_info(T1, Tags),
?line [{category,integer},{column,2},{length,7},
- {line,1},{symbol,16#aaa},{text,"\\x{aaa}"}] =
+ {line,1},{symbol,16#aaa},{text,"\\x{aaa}"}] =
erl_scan:token_info(T2, Tags),
?line [{category,']'},{column,9},{length,1},{line,1},
{symbol,']'},{text,"\""}] = erl_scan:token_info(T3, Tags),
- ?line {ok,[{'[',1},{integer,1,16#aaa},{']',1}],1} =
+ ?line {ok,[{'[',1},{integer,1,16#aaa},{']',1}],1} =
erl_scan:string(U1, 1),
U2 = "\"\\x41\\x{fff}\\x42\"",
@@ -844,7 +847,7 @@ unicode() ->
U3 = "\"a\n\\x{fff}\n\"",
?line {ok,[{'[',1},{char,1,$a},{',',1},{char,1,$\n},
{',',2},{integer,2,16#fff},{',',2},{char,2,$\n},
- {']',3}],3} =
+ {']',3}],3} =
erl_scan:string(U3, 1),
U4 = "\"\\^\n\\x{aaa}\\^\n\"",
@@ -867,10 +870,10 @@ unicode() ->
{char,_,$d},{']',_}],{1,8}} = erl_scan:string(Str1, {1,1}),
?line test(Str1),
Comment = "%% "++[1089],
- ?line {ok,[{comment,1,[$%,$%,$\s,1089]}],1} =
+ ?line {ok,[{comment,1,[$%,$%,$\s,1089]}],1} =
erl_scan:string(Comment, 1, return),
- ?line {ok,[{comment,_,[$%,$%,$\s,1089]}],{1,5}} =
- erl_scan:string(Comment, {1,1}, return),
+ ?line {ok,[{comment,_,[$%,$%,$\s,1089]}],{1,5}} =
+ erl_scan:string(Comment, {1,1}, return),
ok.
more_chars() ->
@@ -885,7 +888,7 @@ more_chars() ->
erl_scan:tokens(C1, eof, 1),
?line {ok,[{char,1,123},{atom,1,a},{'}',1}],1} =
erl_scan:string("$\\{a}"),
-
+
?line {error,{{1,1},erl_scan,char},{1,4}} =
erl_scan:string("$\\x", {1,1}),
?line {error,{{1,1},erl_scan,char},{1,5}} =
@@ -893,12 +896,12 @@ more_chars() ->
?line {more, C3} = erl_scan:tokens([], "$\\x", {1,1}),
?line {done,{error,{{1,1},erl_scan,char},{1,4}},eof} =
erl_scan:tokens(C3, eof, 1),
- ?line {error,{{1,1},erl_scan,char},{1,5}} =
+ ?line {error,{{1,1},erl_scan,char},{1,5}} =
erl_scan:string("$\\x{",{1,1}),
?line {more, C2} = erl_scan:tokens([], "$\\x{", {1,1}),
- ?line {done,{error,{{1,1},erl_scan,char},{1,5}},eof} =
+ ?line {done,{error,{{1,1},erl_scan,char},{1,5}},eof} =
erl_scan:tokens(C2, eof, 1),
- ?line {error,{1,erl_scan,{illegal,character}},1} =
+ ?line {error,{1,erl_scan,{illegal,character}},1} =
erl_scan:string("$\\x{g}"),
?line {error,{{1,1},erl_scan,{illegal,character}},{1,5}} =
erl_scan:string("$\\x{g}", {1,1}),
@@ -924,12 +927,12 @@ more_chars() ->
?line test("$\\x{10FFFF}"),
?line test("$\\x{10ffff}"),
?line test("\"$\n \\{1}\""),
- ?line {error,{1,erl_scan,{illegal,character}},1} =
+ ?line {error,{1,erl_scan,{illegal,character}},1} =
erl_scan:string("$\\x{110000}"),
- ?line {error,{{1,1},erl_scan,{illegal,character}},{1,12}} =
+ ?line {error,{{1,1},erl_scan,{illegal,character}},{1,12}} =
erl_scan:string("$\\x{110000}", {1,1}),
- ?line {error,{{1,1},erl_scan,{illegal,character}},{1,4}} =
+ ?line {error,{{1,1},erl_scan,{illegal,character}},{1,4}} =
erl_scan:string("$\\xfg", {1,1}),
?line test("$\\xffg"),
@@ -953,11 +956,11 @@ test(String) ->
{Wtokens, Wend},
{Ctokens, Cend},
{CWtokens, CWend},
- {CWtokens2, _}] =
+ {CWtokens2, _}] =
[scan_string_with_column(String, X) ||
- X <- [[],
- [return_white_spaces],
- [return_comments],
+ X <- [[],
+ [return_white_spaces],
+ [return_comments],
[return],
[return]]], % for white space compaction test
@@ -969,7 +972,7 @@ test(String) ->
{none,Tokens} = {none, filter_tokens(CWtokens, [white_space,comment])},
{comments,Ctokens} =
{comments,filter_tokens(CWtokens, [white_space])},
- {white_spaces,Wtokens} =
+ {white_spaces,Wtokens} =
{white_spaces,filter_tokens(CWtokens, [comment])},
%% Use token attributes to extract parts from the original string,
@@ -991,9 +994,9 @@ test(String) ->
%% Line attribute only:
[Simple,Wsimple,Csimple,WCsimple] = Simples =
[element(2, erl_scan:string(String, 1, Opts)) ||
- Opts <- [[],
- [return_white_spaces],
- [return_comments],
+ Opts <- [[],
+ [return_white_spaces],
+ [return_comments],
[return]]],
{consistent,true} = {consistent,consistent_attributes(Simples)},
{simple_wc,WCsimple} = {simple_wc,simplify(CWtokens)},
@@ -1004,19 +1007,19 @@ test(String) ->
%% Line attribute only, with text:
[SimpleTxt,WsimpleTxt,CsimpleTxt,WCsimpleTxt] = SimplesTxt =
[element(2, erl_scan:string(String, 1, [text|Opts])) ||
- Opts <- [[],
- [return_white_spaces],
- [return_comments],
+ Opts <- [[],
+ [return_white_spaces],
+ [return_comments],
[return]]],
TextTxt = get_text(WCsimpleTxt),
{text_txt,TextTxt,String} = {text_txt,String,TextTxt},
- {consistent_txt,true} =
+ {consistent_txt,true} =
{consistent_txt,consistent_attributes(SimplesTxt)},
- {simple_txt,SimpleTxt} =
+ {simple_txt,SimpleTxt} =
{simple_txt,filter_tokens(WCsimpleTxt, [white_space,comment])},
- {simple_c_txt,CsimpleTxt} =
+ {simple_c_txt,CsimpleTxt} =
{simple_c_txt,filter_tokens(WCsimpleTxt, [white_space])},
- {simple_w_txt,WsimpleTxt} =
+ {simple_w_txt,WsimpleTxt} =
{simple_w_txt,filter_tokens(WCsimpleTxt, [comment])},
ok.
@@ -1024,18 +1027,18 @@ test(String) ->
test_white_space_compaction(Tokens, Tokens2) when Tokens =:= Tokens2 ->
[WS, WS2] = [select_tokens(Ts, [white_space]) || Ts <- [Tokens, Tokens2]],
test_wsc(WS, WS2).
-
+
test_wsc([], []) ->
ok;
test_wsc([Token|Tokens], [Token2|Tokens2]) ->
- [Text, Text2] = [Text ||
- {text, Text} <-
+ [Text, Text2] = [Text ||
+ {text, Text} <-
[erl_scan:token_info(T, text) ||
T <- [Token, Token2]]],
Sz = erts_debug:size(Text),
Sz2 = erts_debug:size({Text, Text2}),
IsCompacted = Sz2 < 2*Sz+erts_debug:size({a,a}),
- ToBeCompacted = is_compacted(Text),
+ ToBeCompacted = is_compacted(Text),
if
IsCompacted =:= ToBeCompacted ->
test_wsc(Tokens, Tokens2);
@@ -1050,14 +1053,14 @@ is_compacted("\n\r") ->
is_compacted("\n\f") ->
true;
is_compacted([$\n|String]) ->
- all_spaces(String)
+ all_spaces(String)
orelse
all_tabs(String);
is_compacted(String) ->
all_spaces(String)
orelse
all_tabs(String).
-
+
all_spaces(L) ->
all_same(L, $\s).
@@ -1078,7 +1081,7 @@ newlines_first([Token|Tokens]) ->
_ ->
Nnls =:= 0
end,
- if
+ if
OK -> newlines_first(Tokens);
true -> OK
end.
@@ -1097,7 +1100,7 @@ simplify([]) ->
get_text(Tokens) ->
lists:flatten(
- [T ||
+ [T ||
Token <- Tokens,
({text,T} = erl_scan:token_info(Token, text)) =/= []]).
@@ -1108,7 +1111,7 @@ test_decorated_tokens(String, Tokens) ->
token_attrs(Tokens) ->
[{L,C,Len,T} ||
Token <- Tokens,
- ([{line,L},{column,C},{length,Len},{text,T}] =
+ ([{line,L},{column,C},{length,Len},{text,T}] =
erl_scan:token_info(Token, [line,column,length,text])) =/= []].
test_strings([], _S, Line, Column) ->
@@ -1150,7 +1153,7 @@ scan_string_with_column(String, Options0) ->
{ok, Ts1, End1} = erl_scan:string(String, StartLoc, Options),
TString = String ++ ". ",
{ok,Ts2,End2} = scan_tokens(TString, Options, [], StartLoc),
- {ok, Ts3, End3} =
+ {ok, Ts3, End3} =
scan_tokens_1({more, []}, TString, Options, [], StartLoc),
{end_2,End2,End3} = {end_2,End3,End2},
{EndLine1,EndColumn1} = End1,
@@ -1190,8 +1193,8 @@ consistent_attributes([Ts | TsL]) ->
L = [T || T <- Ts, is_integer(element(2, T))],
case L of
[] ->
- TagsL = [[Tag || {Tag,_} <-
- erl_scan:attributes_info(element(2, T))] ||
+ TagsL = [[Tag || {Tag,_} <-
+ erl_scan:attributes_info(element(2, T))] ||
T <- Ts],
case lists:usort(TagsL) of
[_] ->
diff --git a/lib/stdlib/test/escript_SUITE.erl b/lib/stdlib/test/escript_SUITE.erl
index 70aae54d62..162ca6006f 100644
--- a/lib/stdlib/test/escript_SUITE.erl
+++ b/lib/stdlib/test/escript_SUITE.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
-module(escript_SUITE).
@@ -22,16 +22,21 @@
init_per_testcase/2,
fin_per_testcase/2,
basic/1,
- errors/1,
+ errors/1,
strange_name/1,
emulator_flags/1,
module_script/1,
beam_script/1,
archive_script/1,
- epp/1
+ epp/1,
+ create_and_extract/1,
+ foldl/1,
+ overflow/1,
+ verify_sections/3
]).
-include("test_server.hrl").
+-include_lib("kernel/include/file.hrl").
all(suite) ->
[
@@ -42,7 +47,10 @@ all(suite) ->
module_script,
beam_script,
archive_script,
- epp
+ epp,
+ create_and_extract,
+ foldl,
+ overflow
].
init_per_testcase(_Case, Config) ->
@@ -68,11 +76,11 @@ basic(Config) when is_list(Config) ->
?line run(Dir, "factorial_warning 20",
[data_dir,<<"factorial_warning:12: Warning: function bar/0 is unused\n"
"factorial 20 = 2432902008176640000\nExitCode:0">>]),
- ?line run(Dir, "-s", "factorial_warning",
+ ?line run_with_opts(Dir, "-s", "factorial_warning",
[data_dir,<<"factorial_warning:12: Warning: function bar/0 is unused\nExitCode:0">>]),
- ?line run(Dir, "-s -i", "factorial_warning",
+ ?line run_with_opts(Dir, "-s -i", "factorial_warning",
[data_dir,<<"factorial_warning:12: Warning: function bar/0 is unused\nExitCode:0">>]),
- ?line run(Dir, "-c -s", "factorial_warning",
+ ?line run_with_opts(Dir, "-c -s", "factorial_warning",
[data_dir,<<"factorial_warning:12: Warning: function bar/0 is unused\nExitCode:0">>]),
?line run(Dir, "filesize "++filename:join(?config(data_dir, Config),"filesize"),
[data_dir,<<"filesize:11: Warning: function id/1 is unused\n324\nExitCode:0">>]),
@@ -100,7 +108,7 @@ errors(Config) when is_list(Config) ->
[data_dir,<<"lint_error:6: function main/1 already defined\n">>,
data_dir,"lint_error:8: variable 'ExitCode' is unbound\n",
<<"escript: There were compilation errors.\nExitCode:127">>]),
- ?line run(Dir, "-s", "lint_error",
+ ?line run_with_opts(Dir, "-s", "lint_error",
[data_dir,<<"lint_error:6: function main/1 already defined\n">>,
data_dir,"lint_error:8: variable 'ExitCode' is unbound\n",
<<"escript: There were compilation errors.\nExitCode:127">>]),
@@ -140,31 +148,31 @@ module_script(Config) when is_list(Config) ->
OrigFile = filename:join([Data,"emulator_flags"]),
{ok, OrigBin} = file:read_file(OrigFile),
?line [Shebang, Mode, Flags | Source] = string:tokens(binary_to_list(OrigBin), "\n"),
- ?line {ok, OrigFI} = file:read_file_info(OrigFile),
+ ?line {ok, OrigFI} = file:read_file_info(OrigFile),
%% Write source file
Priv = ?config(priv_dir, Config),
Dir = filename:absname(Priv), % Get rid of trailing slash.
Base = "module_script",
ErlFile = filename:join([Priv, Base ++ ".erl"]),
- ErlCode = ["-module(", Base, ").\n",
+ ErlCode = ["\n-module(", Base, ").\n",
"-export([main/1]).\n\n",
string:join(Source, "\n"),
"\n"],
?line ok = file:write_file(ErlFile, ErlCode),
-
+
%%%%%%%
%% Create and run scripts without emulator flags
%% With shebang
NoArgsBase = Base ++ "_no_args_with_shebang",
NoArgsFile = filename:join([Priv, NoArgsBase]),
- ?line ok = file:write_file(NoArgsFile,
+ ?line ok = file:write_file(NoArgsFile,
[Shebang, "\n",
ErlCode]),
?line ok = file:write_file_info(NoArgsFile, OrigFI),
-
- ?line run(Dir, NoArgsBase ++ " -arg1 arg2 arg3",
+
+ ?line run(Dir, NoArgsBase ++ " -arg1 arg2 arg3",
[<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
"nostick:[]\n"
"mnesia:[]\n"
@@ -172,7 +180,7 @@ module_script(Config) when is_list(Config) ->
"unknown:[]\n"
"ExitCode:0">>]),
- ?line run(Dir, "", NoArgsBase ++ " -arg1 arg2 arg3",
+ ?line run_with_opts(Dir, "", NoArgsBase ++ " -arg1 arg2 arg3",
[<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
"nostick:[]\n"
"mnesia:[]\n"
@@ -183,33 +191,33 @@ module_script(Config) when is_list(Config) ->
%% Without shebang
NoArgsBase2 = Base ++ "_no_args_without_shebang",
NoArgsFile2 = filename:join([Priv, NoArgsBase2]),
- ?line ok = file:write_file(NoArgsFile2,
+ ?line ok = file:write_file(NoArgsFile2,
["Something else than shebang!!!", "\n",
ErlCode]),
?line ok = file:write_file_info(NoArgsFile2, OrigFI),
-
- ?line run(Dir, "", NoArgsBase2 ++ " -arg1 arg2 arg3",
+
+ ?line run_with_opts(Dir, "", NoArgsBase2 ++ " -arg1 arg2 arg3",
[<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
"nostick:[]\n"
"mnesia:[]\n"
"ERL_FLAGS=false\n"
"unknown:[]\n"
"ExitCode:0">>]),
-
+
%% Plain module without header
NoArgsBase3 = Base ++ "_no_args_without_header",
NoArgsFile3 = filename:join([Priv, NoArgsBase3]),
?line ok = file:write_file(NoArgsFile3, [ErlCode]),
?line ok = file:write_file_info(NoArgsFile3, OrigFI),
-
- ?line run(Dir, "", NoArgsBase3 ++ " -arg1 arg2 arg3",
+
+ ?line run_with_opts(Dir, "", NoArgsBase3 ++ " -arg1 arg2 arg3",
[<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
"nostick:[]\n"
"mnesia:[]\n"
"ERL_FLAGS=false\n"
"unknown:[]\n"
"ExitCode:0">>]),
-
+
%%%%%%%
%% Create and run scripts with emulator flags
@@ -217,12 +225,12 @@ module_script(Config) when is_list(Config) ->
ArgsBase = Base ++ "_args_with_shebang",
ArgsFile = filename:join([Priv, ArgsBase]),
?line ok = file:write_file(ArgsFile,
- [Shebang, "\n",
+ [Shebang, "\n",
Mode, "\n",
Flags, "\n",
ErlCode]),
- ?line ok = file:write_file_info(ArgsFile, OrigFI),
-
+ ?line ok = file:write_file_info(ArgsFile, OrigFI),
+
?line run(Dir, ArgsBase ++ " -arg1 arg2 arg3",
[<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
"nostick:[{nostick,[]}]\n"
@@ -242,32 +250,32 @@ beam_script(Config) when is_list(Config) ->
OrigFile = filename:join([Data,"emulator_flags"]),
{ok, OrigBin} = file:read_file(OrigFile),
?line [Shebang, Mode, Flags | Source] = string:tokens(binary_to_list(OrigBin), "\n"),
- ?line {ok, OrigFI} = file:read_file_info(OrigFile),
+ ?line {ok, OrigFI} = file:read_file_info(OrigFile),
%% Write source file
Priv = ?config(priv_dir, Config),
Dir = filename:absname(Priv), % Get rid of trailing slash.
Base = "beam_script",
ErlFile = filename:join([Priv, Base ++ ".erl"]),
- ?line ok = file:write_file(ErlFile,
- ["-module(", Base, ").\n",
+ ?line ok = file:write_file(ErlFile,
+ ["\n-module(", Base, ").\n",
"-export([main/1]).\n\n",
string:join(Source, "\n"),
"\n"]),
%% Compile the code
?line {ok, _Mod, BeamCode} = compile:file(ErlFile, [binary]),
-
+
%%%%%%%
%% Create and run scripts without emulator flags
%% With shebang
NoArgsBase = Base ++ "_no_args_with_shebang",
NoArgsFile = filename:join([Priv, NoArgsBase]),
- ?line ok = file:write_file(NoArgsFile,
+ ?line ok = file:write_file(NoArgsFile,
[Shebang, "\n",
BeamCode]),
- ?line ok = file:write_file_info(NoArgsFile, OrigFI),
+ ?line ok = file:write_file_info(NoArgsFile, OrigFI),
?line run(Dir, NoArgsBase ++ " -arg1 arg2 arg3",
[<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
@@ -277,7 +285,7 @@ beam_script(Config) when is_list(Config) ->
"unknown:[]\n"
"ExitCode:0">>]),
- ?line run(Dir, "", NoArgsBase ++ " -arg1 arg2 arg3",
+ ?line run_with_opts(Dir, "", NoArgsBase ++ " -arg1 arg2 arg3",
[<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
"nostick:[]\n"
"mnesia:[]\n"
@@ -288,12 +296,12 @@ beam_script(Config) when is_list(Config) ->
%% Without shebang
NoArgsBase2 = Base ++ "_no_args_without_shebang",
NoArgsFile2 = filename:join([Priv, NoArgsBase2]),
- ?line ok = file:write_file(NoArgsFile2,
+ ?line ok = file:write_file(NoArgsFile2,
["Something else than shebang!!!", "\n",
BeamCode]),
- ?line ok = file:write_file_info(NoArgsFile2, OrigFI),
+ ?line ok = file:write_file_info(NoArgsFile2, OrigFI),
- ?line run(Dir, "", NoArgsBase2 ++ " -arg1 arg2 arg3",
+ ?line run_with_opts(Dir, "", NoArgsBase2 ++ " -arg1 arg2 arg3",
[<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
"nostick:[]\n"
"mnesia:[]\n"
@@ -305,9 +313,9 @@ beam_script(Config) when is_list(Config) ->
NoArgsBase3 = Base ++ "_no_args_without_header",
NoArgsFile3 = filename:join([Priv, NoArgsBase3]),
?line ok = file:write_file(NoArgsFile3, [BeamCode]),
- ?line ok = file:write_file_info(NoArgsFile3, OrigFI),
+ ?line ok = file:write_file_info(NoArgsFile3, OrigFI),
- ?line run(Dir, "", NoArgsBase3 ++ " -arg1 arg2 arg3",
+ ?line run_with_opts(Dir, "", NoArgsBase3 ++ " -arg1 arg2 arg3",
[<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
"nostick:[]\n"
"mnesia:[]\n"
@@ -322,12 +330,12 @@ beam_script(Config) when is_list(Config) ->
ArgsBase = Base ++ "_args",
ArgsFile = filename:join([Priv, ArgsBase]),
?line ok = file:write_file(ArgsFile,
- [Shebang, "\n",
+ [Shebang, "\n",
Mode, "\n",
Flags, "\n",
BeamCode]),
- ?line ok = file:write_file_info(ArgsFile, OrigFI),
-
+ ?line ok = file:write_file_info(ArgsFile, OrigFI),
+
?line run(Dir, ArgsBase ++ " -arg1 arg2 arg3",
[<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
"nostick:[{nostick,[]}]\n"
@@ -356,13 +364,13 @@ archive_script(Config) when is_list(Config) ->
?line ok = compile_app(TopDir, "archive_script_dict"),
?line ok = compile_app(TopDir, "archive_script_dummy"),
?line {ok, MainFiles} = file:list_dir(TopDir),
- ?line ok = compile_files(MainFiles, TopDir, TopDir),
-
+ ?line ok = compile_files(MainFiles, TopDir, TopDir),
+
%% Create the archive
{ok, TopFiles} = file:list_dir(TopDir),
?line {ok, {_, ArchiveBin}} = zip:create(Archive, TopFiles,
[memory, {compress, []}, {cwd, TopDir}]),
-
+
%% Read the source script
OrigFile = filename:join([DataDir, "emulator_flags"]),
{ok, OrigBin} = file:read_file(OrigFile),
@@ -371,73 +379,73 @@ archive_script(Config) when is_list(Config) ->
Flags = "%%! -archive_script_dict foo bar"
" -archive_script_dict foo"
" -archive_script_dummy bar",
- ?line {ok, OrigFI} = file:read_file_info(OrigFile),
-
+ ?line {ok, OrigFI} = file:read_file_info(OrigFile),
+
%%%%%%%
%% Create and run scripts without emulator flags
- MainBase = "archive_script_main",
- MainScript = filename:join([PrivDir, MainBase]),
+ MainBase = "archive_script_main",
+ MainScript = filename:join([PrivDir, MainBase]),
%% With shebang
- ?line ok = file:write_file(MainScript,
+ ?line ok = file:write_file(MainScript,
[Shebang, "\n",
Flags, "\n",
ArchiveBin]),
- ?line ok = file:write_file_info(MainScript, OrigFI),
-
+ ?line ok = file:write_file_info(MainScript, OrigFI),
+
?line run(PrivDir, MainBase ++ " -arg1 arg2 arg3",
- [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
+ [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
"dict:[{archive_script_dict,[\"foo\",\"bar\"]},{archive_script_dict,[\"foo\"]}]\n"
"dummy:[{archive_script_dummy,[\"bar\"]}]\n"
"priv:{ok,<<\"Some private data...\\n\">>}\n"
"ExitCode:0">>]),
- ?line run(PrivDir, "", MainBase ++ " -arg1 arg2 arg3",
- [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
+ ?line run_with_opts(PrivDir, "", MainBase ++ " -arg1 arg2 arg3",
+ [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
"dict:[{archive_script_dict,[\"foo\",\"bar\"]},{archive_script_dict,[\"foo\"]}]\n"
"dummy:[{archive_script_dummy,[\"bar\"]}]\n"
"priv:{ok,<<\"Some private data...\\n\">>}\n"
"ExitCode:0">>]),
-
+
?line ok = file:rename(MainScript, MainScript ++ "_with_shebang"),
%% Without shebang (no flags)
- ?line ok = file:write_file(MainScript,
+ ?line ok = file:write_file(MainScript,
["Something else than shebang!!!", "\n",
ArchiveBin]),
- ?line ok = file:write_file_info(MainScript, OrigFI),
-
- ?line run(PrivDir, "", MainBase ++ " -arg1 arg2 arg3",
- [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
+ ?line ok = file:write_file_info(MainScript, OrigFI),
+
+ ?line run_with_opts(PrivDir, "", MainBase ++ " -arg1 arg2 arg3",
+ [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
"dict:[]\n"
"dummy:[]\n"
"priv:{ok,<<\"Some private data...\\n\">>}\n"
"ExitCode:0">>]),
?line ok = file:rename(MainScript, MainScript ++ "_without_shebang"),
-
+
%% Plain archive without header (no flags)
-
+
?line ok = file:write_file(MainScript, [ArchiveBin]),
- ?line ok = file:write_file_info(MainScript, OrigFI),
-
- ?line run(PrivDir, "", MainBase ++ " -arg1 arg2 arg3",
- [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
+ ?line ok = file:write_file_info(MainScript, OrigFI),
+
+ ?line run_with_opts(PrivDir, "", MainBase ++ " -arg1 arg2 arg3",
+ [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n"
"dict:[]\n"
"dummy:[]\n"
"priv:{ok,<<\"Some private data...\\n\">>}\n"
"ExitCode:0">>]),
?line ok = file:rename(MainScript, MainScript ++ "_without_header"),
-
+
%%%%%%%
%% Create and run scripts with emulator flags
AltBase = "archive_script_alternate_main",
AltScript = filename:join([PrivDir, AltBase]),
- ?line ok = file:write_file(AltScript,
+ ?line ok = file:write_file(AltScript,
[Shebang, "\n",
Mode, "\n",
Flags, " -escript main archive_script_main2\n",
ArchiveBin]),
- ?line ok = file:write_file_info(AltScript, OrigFI),
+ ?line ok = file:write_file_info(AltScript, OrigFI),
?line run(PrivDir, AltBase ++ " -arg1 arg2 arg3",
[<<"main2:[\"-arg1\",\"arg2\",\"arg3\"]\n"
@@ -445,7 +453,7 @@ archive_script(Config) when is_list(Config) ->
"dummy:[{archive_script_dummy,[\"bar\"]}]\n"
"priv:{ok,<<\"Some private data...\\n\">>}\n"
"ExitCode:0">>]),
-
+
ok.
compile_app(TopDir, AppName) ->
@@ -482,6 +490,265 @@ epp(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+create_and_extract(Config) when is_list(Config) ->
+ {NewFile, FileInfo,
+ EmuArg, Source,
+ _ErlBase, ErlCode,
+ _BeamBase, BeamCode,
+ ArchiveBin} =
+ prepare_creation("create_and_extract", Config),
+
+ Bodies =
+ [[{source, ErlCode}],
+ [{beam, BeamCode}],
+ [{archive, ArchiveBin}]],
+
+ %% Verify all combinations of scripts with shebangs
+ [verify_sections(NewFile, FileInfo, S ++ C ++ E ++ B) ||
+ S <- [[{shebang, default}],
+ [{shebang, "/usr/bin/env escript"}]],
+ C <- [[],
+ [{comment, undefined}],
+ [{comment, default}],
+ [{comment, "This is a nonsense comment"}]],
+ E <- [[],
+ [{emu_args, undefined}],
+ [{emu_args, EmuArg}]],
+ B <- [[{source, Source}] | Bodies]],
+
+ %% Verify all combinations of scripts without shebangs
+ [verify_sections(NewFile, FileInfo, S ++ C ++ E ++ B) ||
+ S <- [[], [{shebang, undefined}]],
+ C <- [[], [{comment, undefined}]],
+ E <- [[], [{emu_args, undefined}]],
+ B <- Bodies],
+
+ %% Verify the compile_source option
+ file:delete(NewFile),
+ ?line ok = escript:create(NewFile, [{source, Source}]),
+ ?line {ok, [_, _, _, {source, Source}]} = escript:extract(NewFile, []),
+ ?line {ok, [_, _, _, {source, BeamCode2}]} =
+ escript:extract(NewFile, [compile_source]),
+ verify_sections(NewFile, FileInfo,
+ [{shebang, default},
+ {comment, default},
+ {beam, BeamCode2}]),
+
+ file:delete(NewFile),
+ ok.
+
+prepare_creation(Base, Config) ->
+ %% Read the source
+ PrivDir = ?config(priv_dir, Config),
+ DataDir = ?config(data_dir, Config),
+ OrigFile = filename:join([DataDir,"emulator_flags"]),
+ ?line {ok, FileInfo} = file:read_file_info(OrigFile),
+ NewFile = filename:join([PrivDir, Base]),
+ ?line {ok, [{shebang, default},
+ {comment, _},
+ {emu_args, EmuArg},
+ {source, Source}]} =
+ escript:extract(OrigFile, []),
+
+ %% Compile the code
+ ErlFile = NewFile ++ ".erl",
+ ErlCode = list_to_binary(["\n-module(", Base, ").\n",
+ "-export([main/1]).\n\n",
+ Source, "\n\n"]),
+ ?line ok = file:write_file(ErlFile, ErlCode),
+
+ %% Compile the code
+ ?line {ok, _Mod, BeamCode} =
+ compile:file(ErlFile, [binary, debug_info]),
+
+ %% Create an archive
+ ?line {ok, {_, ArchiveBin}} =
+ zip:create("dummy_archive_name",
+ [{Base ++ ".erl", ErlCode},
+ {Base ++ ".beam", BeamCode}],
+ [{compress, []}, memory]),
+ {NewFile, FileInfo,
+ EmuArg, Source,
+ Base ++ ".erl", ErlCode,
+ Base ++ ".beam", BeamCode,
+ ArchiveBin}.
+
+verify_sections(File, FileInfo, Sections) ->
+ io:format("~p:verify_sections(\n\t~p,\n\t~p,\n\t~p).\n",
+ [?MODULE, File, FileInfo, Sections]),
+
+ %% Create
+ file:delete(File),
+ ?line ok = escript:create(File, Sections),
+ ?line ok = file:write_file_info(File, FileInfo),
+
+ %% Run
+ Dir = filename:absname(filename:dirname(File)),
+ Base = filename:basename(File),
+
+ HasArg = fun(Tag) ->
+ case lists:keysearch(Tag, 1, Sections) of
+ false -> false;
+ {value, {_, undefined}} -> false;
+ {value, _} -> true
+ end
+ end,
+ ExpectedMain = <<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n">>,
+ ExpectedOutput =
+ case HasArg(emu_args) of
+ true ->
+ <<"nostick:[{nostick,[]}]\n"
+ "mnesia:[{mnesia,[\"dir\",\"a/directory\"]},{mnesia,[\"debug\",\"verbose\"]}]\n"
+ "ERL_FLAGS=false\n"
+ "unknown:[]\n"
+ "ExitCode:0">>;
+ false ->
+ <<"nostick:[]\nmnesia:[]\nERL_FLAGS=false\nunknown:[]\nExitCode:0">>
+ end,
+
+ InputArgs = Base ++ " -arg1 arg2 arg3",
+ Expected = <<ExpectedMain/binary, ExpectedOutput/binary>>,
+ case HasArg(shebang) of
+ true ->
+ ?line run(Dir, InputArgs, [Expected]);
+ false ->
+ ?line run_with_opts(Dir, [], InputArgs, [Expected])
+ end,
+
+ %% Verify
+ ?line {ok, Bin} = escript:create(binary, Sections),
+ ?line {ok, Read} = file:read_file(File),
+ ?line Bin = Read, % Assert
+
+ Normalized = normalize_sections(Sections),
+ ?line {ok, Extracted} = escript:extract(File, []),
+ io:format("Normalized; ~p\n", [Normalized]),
+ io:format("Extracted ; ~p\n", [Extracted]),
+ ?line Normalized = Extracted, % Assert
+ ok.
+
+normalize_sections(Sections) ->
+ AtomToTuple =
+ fun(Val) ->
+ if
+ is_atom(Val) -> {Val, default};
+ true -> Val
+ end
+ end,
+ case lists:map(AtomToTuple, [{K, V} || {K, V} <- Sections, V =/= undefined]) of
+ [{shebang, Shebang} | Rest] ->
+ [{shebang, Shebang} |
+ case Rest of
+ [{comment, Comment} | Rest2] ->
+ [{comment, Comment} |
+ case Rest2 of
+ [{emu_args, EmuArgs}, Body] ->
+ [{emu_args, EmuArgs}, Body];
+ [Body] ->
+ [{emu_args, undefined}, Body]
+ end
+ ];
+ [{emu_args, EmuArgs}, Body] ->
+ [{comment, undefined}, {emu_args, EmuArgs}, Body];
+ [Body] ->
+ [{comment, undefined}, {emu_args, undefined}, Body]
+ end
+ ];
+ [Body] ->
+ [{shebang, undefined}, {comment, undefined}, {emu_args, undefined}, Body]
+ end.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+foldl(Config) when is_list(Config) ->
+ {NewFile, _FileInfo,
+ _EmuArg, _Source,
+ ErlBase, ErlCode,
+ BeamBase, _BeamCode,
+ ArchiveBin} =
+ prepare_creation("foldl", Config),
+
+ Collect = fun(Name, GetInfo, GetBin, Acc) ->
+ [{Name, GetInfo(), GetBin()} | Acc]
+ end,
+
+ %% Get line numbers and the file attribute right
+ SourceFile = NewFile ++ ".erl",
+ <<_:1/binary, ErlCode2/binary>> = ErlCode,
+ ?line ok = file:write_file(SourceFile, ErlCode2),
+ ?line {ok, _Mod, BeamCode} =
+ compile:file(SourceFile, [binary, debug_info]),
+
+ %% Verify source script
+ ?line ok = escript:create(SourceFile, [{source, ErlCode}]),
+ ?line {ok, [{".", _, BeamCode2}]}
+ = escript_foldl(Collect, [], SourceFile),
+
+ ?line {ok, Abstr} = beam_lib:chunks(BeamCode, [abstract_code]),
+ ?line {ok, Abstr2} = beam_lib:chunks(BeamCode2, [abstract_code]),
+ %% io:format("abstr1=~p\n", [Abstr]),
+ %% io:format("abstr2=~p\n", [Abstr2]),
+ ?line Abstr = Abstr2, % Assert
+
+ %% Verify beam script
+ ?line ok = escript:create(NewFile, [{beam, BeamCode}]),
+ ?line {ok, [{".", _, BeamCode}]}
+ = escript_foldl(Collect, [], NewFile),
+
+ %% Verify archive scripts
+ ?line ok = escript:create(NewFile, [{archive, ArchiveBin}]),
+ ?line {ok, [{BeamBase, #file_info{}, _},
+ {ErlBase, #file_info{}, _}]}
+ = escript_foldl(Collect, [], NewFile),
+
+ ArchiveFiles = [{ErlBase, ErlCode}, {BeamBase, BeamCode}],
+ ?line ok = escript:create(NewFile, [{archive, ArchiveFiles, []}]),
+ ?line {ok, [{BeamBase, _, _},
+ {ErlBase, _, _}]}
+ = escript_foldl(Collect, [], NewFile),
+
+ ok.
+
+escript_foldl(Fun, Acc, File) ->
+ code:ensure_loaded(zip),
+ case erlang:function_exported(zip, foldl, 3) of
+ true ->
+ emulate_escript_foldl(Fun, Acc, File);
+ false ->
+ escript:foldl(Fun, Acc, File)
+ end.
+
+emulate_escript_foldl(Fun, Acc, File) ->
+ case escript:extract(File, [compile_source]) of
+ {ok, [_Shebang, _Comment, _EmuArgs, Body]} ->
+ case Body of
+ {source, BeamCode} ->
+ GetInfo = fun() -> file:read_file_info(File) end,
+ GetBin = fun() -> BeamCode end,
+ {ok, Fun(".", GetInfo, GetBin, Acc)};
+ {beam, BeamCode} ->
+ GetInfo = fun() -> file:read_file_info(File) end,
+ GetBin = fun() -> BeamCode end,
+ {ok, Fun(".", GetInfo, GetBin, Acc)};
+ {archive, ArchiveBin} ->
+ zip:foldl(Fun, Acc, {File, ArchiveBin})
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+overflow(Config) when is_list(Config) ->
+ Data = ?config(data_dir, Config),
+ Dir = filename:absname(Data), %Get rid of trailing slash.
+ ?line run(Dir, "arg_overflow",
+ [<<"ExitCode:0">>]),
+ ?line run(Dir, "linebuf_overflow",
+ [<<"ExitCode:0">>]),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
run(Dir, Cmd0, Expected0) ->
Expected = iolist_to_binary(expected_output(Expected0, Dir)),
Cmd = case os:type() of
@@ -490,7 +757,7 @@ run(Dir, Cmd0, Expected0) ->
end,
do_run(Dir, Cmd, Expected).
-run(Dir, Opts, Cmd0, Expected) ->
+run_with_opts(Dir, Opts, Cmd0, Expected) ->
Cmd = case os:type() of
{win32,_} -> "escript " ++ Opts ++ " " ++ filename:nativename(Dir) ++ "\\" ++ Cmd0;
_ -> "escript " ++ Opts ++ " " ++ Dir ++ "/" ++ Cmd0
@@ -533,8 +800,8 @@ expected_output([data_dir|T], Data) ->
[filename:nativename(Data)++Slash|expected_output(T, Data)];
expected_output([H|T], Data) ->
[H|expected_output(T, Data)];
-expected_output([], _) ->
+expected_output([], _) ->
[];
-expected_output(Bin, _) when is_binary(Bin) ->
+expected_output(Bin, _) when is_binary(Bin) ->
Bin.
diff --git a/lib/stdlib/test/escript_SUITE_data/arg_overflow b/lib/stdlib/test/escript_SUITE_data/arg_overflow
new file mode 100755
index 0000000000..dd5accc051
--- /dev/null
+++ b/lib/stdlib/test/escript_SUITE_data/arg_overflow
@@ -0,0 +1,5 @@
+#! /usr/bin/env escript
+%% -*- erlang -*-
+%%!x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x
+main(_) ->
+ halt(0).
diff --git a/lib/stdlib/test/escript_SUITE_data/linebuf_overflow b/lib/stdlib/test/escript_SUITE_data/linebuf_overflow
new file mode 100755
index 0000000000..33133c1ce9
--- /dev/null
+++ b/lib/stdlib/test/escript_SUITE_data/linebuf_overflow
@@ -0,0 +1,5 @@
+#! /usr/bin/env escript
+%% -*- erlang -*-
+%%!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+main(_) ->
+ halt(0).
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 13c87ca005..620848003c 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -31,9 +31,9 @@
-export([slot/1]).
-export([match/1, match1/1, match2/1, match_object/1, match_object2/1]).
-export([misc/1, dups/1, misc1/1, safe_fixtable/1, info/1, tab2list/1]).
--export([files/1, tab2file/1, tab2file2/1, tab2file3/1, tabfile_ext1/1,
+-export([files/1, tab2file/1, tab2file2/1, tabfile_ext1/1,
tabfile_ext2/1, tabfile_ext3/1, tabfile_ext4/1]).
--export([heavy/1, heavy_lookup/1, heavy_lookup_element/1]).
+-export([heavy/1, heavy_lookup/1, heavy_lookup_element/1, heavy_concurrent/1]).
-export([lookup_element/1, lookup_element_mult/1]).
-export([fold/1]).
-export([foldl_ordered/1, foldr_ordered/1, foldl/1, foldr/1, fold_empty/1]).
@@ -63,13 +63,14 @@
meta_lookup_unnamed_read/1, meta_lookup_unnamed_write/1,
meta_lookup_named_read/1, meta_lookup_named_write/1,
meta_newdel_unnamed/1, meta_newdel_named/1]).
--export([smp_insert/1, smp_fixed_delete/1, smp_unfix_fix/1, smp_select_delete/1, otp_8166/1]).
+-export([smp_insert/1, smp_fixed_delete/1, smp_unfix_fix/1, smp_select_delete/1,
+ otp_8166/1, otp_8732/1]).
-export([exit_large_table_owner/1,
exit_many_large_table_owner/1,
exit_many_tables_owner/1,
exit_many_many_tables_owner/1]).
-export([write_concurrency/1, heir/1, give_away/1, setopts/1]).
--export([bad_table/1]).
+-export([bad_table/1, types/1]).
-export([init_per_testcase/2, fin_per_testcase/2, end_per_suite/1]).
%% Convenience for manual testing
@@ -77,7 +78,7 @@
% internal exports
-export([dont_make_worse_sub/0, make_better_sub1/0, make_better_sub2/0]).
--export([t_repair_continuation_do/1, default_do/1, t_bucket_disappears_do/1,
+-export([t_repair_continuation_do/1, t_bucket_disappears_do/1,
select_fail_do/1, whitebox_1/1, whitebox_2/1, t_delete_all_objects_do/1,
t_delete_object_do/1, t_init_table_do/1, t_insert_list_do/1,
update_element_opts/1, update_element_opts/4, update_element/4, update_element_do/4,
@@ -89,9 +90,13 @@
match_delete_do/1, match_delete3_do/1, firstnext_do/1,
slot_do/1, match1_do/1, match2_do/1, match_object_do/1, match_object2_do/1,
misc1_do/1, safe_fixtable_do/1, info_do/1, dups_do/1, heavy_lookup_do/1,
- heavy_lookup_element_do/1, member_do/1, otp_5340_do/1, otp_7665_do/1, meta_wb_do/1
+ heavy_lookup_element_do/1, member_do/1, otp_5340_do/1, otp_7665_do/1, meta_wb_do/1,
+ do_heavy_concurrent/1, tab2file2_do/2, exit_large_table_owner_do/2,
+ types_do/1, sleeper/0, rpc_externals/0, memory_do/1
]).
+-export([t_select_reverse/1]).
+
-include("test_server.hrl").
init_per_testcase(Case, Config) ->
@@ -126,10 +131,10 @@ all(suite) ->
match_heavy, fold, member,
t_delete_object, t_init_table, t_whitebox,
t_delete_all_objects, t_insert_list, t_test_ms,
- t_select_delete, t_ets_dets, memory,
+ t_select_delete, t_ets_dets, memory, t_select_reverse,
t_bucket_disappears,
select_fail,t_insert_new, t_repair_continuation, otp_5340, otp_6338,
- otp_6842_select_1000, otp_7665,
+ otp_6842_select_1000, otp_7665, otp_8732,
meta_wb,
grow_shrink, grow_pseudo_deleted, shrink_pseudo_deleted,
meta_smp,
@@ -139,7 +144,7 @@ all(suite) ->
exit_many_tables_owner,
exit_many_many_tables_owner,
write_concurrency, heir, give_away, setopts,
- bad_table
+ bad_table, types
].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -153,7 +158,7 @@ t_bucket_disappears(Config) when is_list(Config) ->
t_bucket_disappears_do(Opts) ->
?line EtsMem = etsmem(),
- ?line ets:new(abcd, [named_table, public, {keypos, 2} | Opts]),
+ ?line ets_new(abcd, [named_table, public, {keypos, 2} | Opts]),
?line ets:insert(abcd, {abcd,1,2}),
?line ets:insert(abcd, {abcd,2,2}),
?line ets:insert(abcd, {abcd,3,2}),
@@ -209,7 +214,7 @@ t_repair_continuation_do(Opts) ->
?line MS = [{'_',[],[true]}],
?line MS2 = [{{{'$1','_'},'_'},[],['$1']}],
(fun() ->
- ?line T = ets:new(x,[ordered_set|Opts]),
+ ?line T = ets_new(x,[ordered_set|Opts]),
?line F = fun(0,_)->ok;(N,F) -> ets:insert(T,{N,N}), F(N-1,F) end,
?line F(1000,F),
?line {_,C} = ets:select(T,MS,5),
@@ -221,7 +226,7 @@ t_repair_continuation_do(Opts) ->
?line true = ets:delete(T)
end)(),
(fun() ->
- ?line T = ets:new(x,[ordered_set|Opts]),
+ ?line T = ets_new(x,[ordered_set|Opts]),
?line F = fun(0,_)->ok;(N,F) -> ets:insert(T,{N,N}), F(N-1,F) end,
?line F(1000,F),
?line {_,C} = ets:select(T,MS,1001),
@@ -233,7 +238,7 @@ t_repair_continuation_do(Opts) ->
end)(),
(fun() ->
- ?line T = ets:new(x,[ordered_set|Opts]),
+ ?line T = ets_new(x,[ordered_set|Opts]),
?line F = fun(0,_)->ok;(N,F) ->
ets:insert(T,{integer_to_list(N),N}),
F(N-1,F)
@@ -248,7 +253,7 @@ t_repair_continuation_do(Opts) ->
?line true = ets:delete(T)
end)(),
(fun() ->
- ?line T = ets:new(x,[ordered_set|Opts]),
+ ?line T = ets_new(x,[ordered_set|Opts]),
?line F = fun(0,_)->ok;(N,F) ->
ets:insert(T,{{integer_to_list(N),N},N}),
F(N-1,F)
@@ -264,7 +269,7 @@ t_repair_continuation_do(Opts) ->
end)(),
(fun() ->
- ?line T = ets:new(x,[set|Opts]),
+ ?line T = ets_new(x,[set|Opts]),
?line F = fun(0,_)->ok;(N,F) ->
ets:insert(T,{N,N}),
F(N-1,F)
@@ -279,7 +284,7 @@ t_repair_continuation_do(Opts) ->
?line true = ets:delete(T)
end)(),
(fun() ->
- ?line T = ets:new(x,[set|Opts]),
+ ?line T = ets_new(x,[set|Opts]),
?line F = fun(0,_)->ok;(N,F) ->
ets:insert(T,{integer_to_list(N),N}),
F(N-1,F)
@@ -294,7 +299,7 @@ t_repair_continuation_do(Opts) ->
?line true = ets:delete(T)
end)(),
(fun() ->
- ?line T = ets:new(x,[bag|Opts]),
+ ?line T = ets_new(x,[bag|Opts]),
?line F = fun(0,_)->ok;(N,F) ->
ets:insert(T,{integer_to_list(N),N}),
F(N-1,F)
@@ -309,7 +314,7 @@ t_repair_continuation_do(Opts) ->
?line true = ets:delete(T)
end)(),
(fun() ->
- ?line T = ets:new(x,[duplicate_bag|Opts]),
+ ?line T = ets_new(x,[duplicate_bag|Opts]),
?line F = fun(0,_)->ok;(N,F) ->
ets:insert(T,{integer_to_list(N),N}),
F(N-1,F)
@@ -330,18 +335,20 @@ t_repair_continuation_do(Opts) ->
new(suite) -> [default,setbag,badnew,verybadnew,named,keypos2,privacy].
default(doc) ->
- ["Test case to check that a new ets table is defined as a `set' and "
- "`protected'"];
+ ["Check correct default vaules of a new ets table"];
default(suite) -> [];
default(Config) when is_list(Config) ->
%% Default should be set,protected
- repeat_for_opts(default_do).
-
-default_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Def = ets:new(def,Opts),
+ ?line Def = ets_new(def,[]),
?line set = ets:info(Def,type),
?line protected = ets:info(Def,protection),
+ Compressed = erlang:system_info(ets_always_compress),
+ ?line Compressed = ets:info(Def,compressed),
+ Self = self(),
+ ?line Self = ets:info(Def,owner),
+ ?line none = ets:info(Def, heir),
+ ?line false = ets:info(Def,named_table),
?line ets:delete(Def),
?line verify_etsmem(EtsMem).
@@ -355,7 +362,7 @@ select_fail(Config) when is_list(Config) ->
?line verify_etsmem(EtsMem).
select_fail_do(Opts) ->
- ?line T = ets:new(x,Opts),
+ ?line T = ets_new(x,Opts),
?line ets:insert(T,{a,a}),
?line case (catch
ets:select(T,[{{a,'_'},[],[{snuffla}]}])) of
@@ -378,20 +385,27 @@ select_fail_do(Opts) ->
-define(S(T),ets:info(T,memory)).
-define(TAB_STRUCT_SZ, erts_debug:get_internal_state('DbTable_words')).
--define(NORMAL_TAB_STRUCT_SZ, 26). %% SunOS5.8, 32-bit, non smp, private heap
+%%-define(NORMAL_TAB_STRUCT_SZ, 26). %% SunOS5.8, 32-bit, non smp, private heap
%%
%% The hardcoded expected memory sizes (in words) are the ones we expect on:
%% SunOS5.8, 32-bit, non smp, private heap
%%
-memory(doc) ->
- ["Whitebox test of ets:info(X,memory)"];
-memory(suite) ->
- [];
+memory(doc) -> ["Whitebox test of ets:info(X,memory)"];
+memory(suite) -> [];
memory(Config) when is_list(Config) ->
?line erts_debug:set_internal_state(available_internal_state, true),
?line ok = chk_normal_tab_struct_size(),
- ?line L = [T1,T2,T3,T4] = fill_sets_int(1000),
- ?line XRes1 = adjust_xmem(L, {16862,16072,16072,16078}),
+ repeat_for_opts(memory_do,[compressed]),
+ ?line catch erts_debug:set_internal_state(available_internal_state, false).
+
+memory_do(Opts) ->
+ ?line L = [T1,T2,T3,T4] = fill_sets_int(1000,Opts),
+ XR1 = case mem_mode(T1) of
+ {normal,_} -> {13836,13046,13046,13052}; %{13862,13072,13072,13078};
+ {compressed,4} -> {11041,10251,10251,10252}; %{11067,10277,10277,10278};
+ {compressed,8} -> {10050,9260,9260,9260} %{10076,9286,9286,9286}
+ end,
+ ?line XRes1 = adjust_xmem(L, XR1),
?line Res1 = {?S(T1),?S(T2),?S(T3),?S(T4)},
?line lists:foreach(fun(T) ->
Before = ets:info(T,size),
@@ -402,7 +416,12 @@ memory(Config) when is_list(Config) ->
[Key, ets:info(T,type), Before, ets:info(T,size), Objs])
end,
L),
- ?line XRes2 = adjust_xmem(L, {16849,16060,16048,16054}),
+ XR2 = case mem_mode(T1) of
+ {normal,_} -> {13826,13037,13028,13034}; %{13852,13063,13054,13060};
+ {compressed,4} -> {11031,10242,10233,10234}; %{11057,10268,10259,10260};
+ {compressed,8} -> {10040,9251,9242,9242} %10066,9277,9268,9268}
+ end,
+ ?line XRes2 = adjust_xmem(L, XR2),
?line Res2 = {?S(T1),?S(T2),?S(T3),?S(T4)},
?line lists:foreach(fun(T) ->
Before = ets:info(T,size),
@@ -413,13 +432,18 @@ memory(Config) when is_list(Config) ->
[Key, ets:info(T,type), Before, ets:info(T,size), Objs])
end,
L),
- ?line XRes3 = adjust_xmem(L, {16836,16048,16024,16030}),
+ XR3 = case mem_mode(T1) of
+ {normal,_} -> {13816,13028,13010,13016}; %{13842,13054,13036,13042};
+ {compressed,4} -> {11021,10233,10215,10216}; %{11047,10259,10241,10242};
+ {compressed,8} -> {10030,9242,9224,9224} %{10056,9268,9250,9250}
+ end,
+ ?line XRes3 = adjust_xmem(L, XR3),
?line Res3 = {?S(T1),?S(T2),?S(T3),?S(T4)},
?line lists:foreach(fun(T) ->
?line ets:delete_all_objects(T)
end,
L),
- ?line XRes4 = adjust_xmem(L, {76,286,286,286}),
+ ?line XRes4 = adjust_xmem(L, {50,260,260,260}), %{76,286,286,286}),
?line Res4 = {?S(T1),?S(T2),?S(T3),?S(T4)},
lists:foreach(fun(T) ->
?line ets:delete(T)
@@ -430,9 +454,9 @@ memory(Config) when is_list(Config) ->
?line ets:select_delete(T,[{'_',[],[true]}])
end,
L2),
- ?line XRes5 = adjust_xmem(L2, {76,286,286,286}),
+ ?line XRes5 = adjust_xmem(L2, {50,260,260,260}), %{76,286,286,286}),
?line Res5 = {?S(T11),?S(T12),?S(T13),?S(T14)},
- ?line ?t:format("XRes1 = ~p~n"
+ ?line io:format("XRes1 = ~p~n"
" Res1 = ~p~n~n"
"XRes2 = ~p~n"
" Res2 = ~p~n~n"
@@ -452,9 +476,15 @@ memory(Config) when is_list(Config) ->
?line XRes3 = Res3,
?line XRes4 = Res4,
?line XRes5 = Res5,
- ?line catch erts_debug:set_internal_state(available_internal_state, false),
?line ok.
+mem_mode(T) ->
+ {case ets:info(T,compressed) of
+ true -> compressed;
+ false -> normal
+ end,
+ erlang:system_info(wordsize)}.
+
chk_normal_tab_struct_size() ->
?line System = {os:type(),
os:version(),
@@ -462,36 +492,58 @@ chk_normal_tab_struct_size() ->
erlang:system_info(smp_support),
erlang:system_info(heap_type)},
?line ?t:format("System = ~p~n", [System]),
- ?line ?t:format("?NORMAL_TAB_STRUCT_SZ=~p~n", [?NORMAL_TAB_STRUCT_SZ]),
+ %%?line ?t:format("?NORMAL_TAB_STRUCT_SZ=~p~n", [?NORMAL_TAB_STRUCT_SZ]),
?line ?t:format("?TAB_STRUCT_SZ=~p~n", [?TAB_STRUCT_SZ]),
- ?line case System of
- {{unix, sunos}, {5, 8, 0}, 4, false, private} ->
- ?line ?NORMAL_TAB_STRUCT_SZ = ?TAB_STRUCT_SZ,
- ?line ok;
- _ ->
- ?line ok
- end.
-
-adjust_xmem([T1,T2,T3,T4], {A0,B0,C0,D0} = Mem0) ->
+ ok.
+% ?line case System of
+% {{unix, sunos}, {5, 8, 0}, 4, false, private} ->
+% ?line ?NORMAL_TAB_STRUCT_SZ = ?TAB_STRUCT_SZ,
+% ?line ok;
+% _ ->
+% ?line ok
+% end.
+
+-define(DB_TREE_STACK_NEED,50). % The static stack for a tree, in halfword pointers are two internal words
+ % so the stack gets twice as big
+-define(DB_HASH_SIZEOF_EXTSEG,260). % The segment size in words, in halfword this will be twice as large.
+
+adjust_xmem([T1,T2,T3,T4], {A0,B0,C0,D0} = _Mem0) ->
%% Adjust for 64-bit, smp, and os:
%% Table struct size may differ.
- Mem1 = case ?TAB_STRUCT_SZ of
- ?NORMAL_TAB_STRUCT_SZ ->
- Mem0;
- TabStructSz ->
- TabDiff = TabStructSz - ?NORMAL_TAB_STRUCT_SZ,
- {A0+TabDiff, B0+TabDiff, C0+TabDiff, D0+TabDiff}
- end,
+
+% Mem1 = case ?TAB_STRUCT_SZ of
+% ?NORMAL_TAB_STRUCT_SZ ->
+% Mem0;
+% TabStructSz ->
+% TabDiff = TabStructSz - ?NORMAL_TAB_STRUCT_SZ,
+% {A0+TabDiff, B0+TabDiff, C0+TabDiff, D0+TabDiff}
+% end,
+
+ TabDiff = ?TAB_STRUCT_SZ,
+ Mem1 = {A0+TabDiff, B0+TabDiff, C0+TabDiff, D0+TabDiff},
+
+ Mem2 = case {erlang:system_info({wordsize,internal}),erlang:system_info({wordsize,external})} of
+ %% Halfword, corrections for regular pointers occupying two internal words.
+ {4,8} ->
+ {A1,B1,C1,D1} = Mem1,
+ {A1+4*ets:info(T1, size)+?DB_TREE_STACK_NEED,
+ B1+3*ets:info(T2, size)+?DB_HASH_SIZEOF_EXTSEG,
+ C1+3*ets:info(T3, size)+?DB_HASH_SIZEOF_EXTSEG,
+ D1+3*ets:info(T4, size)+?DB_HASH_SIZEOF_EXTSEG};
+ _ ->
+ Mem1
+ end,
+
%% Adjust for hybrid and shared heaps:
%% Each record is one word smaller.
- Mem2 = case erlang:system_info(heap_type) of
- private ->
- Mem1;
- _ ->
- {A1,B1,C1,D1} = Mem1,
- {A1-ets:info(T1, size),B1-ets:info(T2, size),
- C1-ets:info(T3, size),D1-ets:info(T4, size)}
- end,
+ %%Mem2 = case erlang:system_info(heap_type) of
+ %% private ->
+ %% Mem1;
+ %% _ ->
+ %% {A1,B1,C1,D1} = Mem1,
+ %% {A1-ets:info(T1, size),B1-ets:info(T2, size),
+ %% C1-ets:info(T3, size),D1-ets:info(T4, size)}
+ %% end,
%%{Mem2,{ets:info(T1,stats),ets:info(T2,stats),ets:info(T3,stats),ets:info(T4,stats)}}.
Mem2.
@@ -510,7 +562,7 @@ t_whitebox(Config) when is_list(Config) ->
?line verify_etsmem(EtsMem).
whitebox_1(Opts) ->
- ?line T=ets:new(x,[bag | Opts]),
+ ?line T=ets_new(x,[bag | Opts]),
?line ets:insert(T,[{du,glade},{ta,en}]),
?line ets:insert(T,[{hej,hopp2},{du,glade2},{ta,en2}]),
?line {_,C}=ets:match(T,{ta,'$1'},1),
@@ -520,8 +572,8 @@ whitebox_1(Opts) ->
ok.
whitebox_2(Opts) ->
- ?line T=ets:new(x,[ordered_set, {keypos,2} | Opts]),
- ?line T2=ets:new(x,[set, {keypos,2}| Opts]),
+ ?line T=ets_new(x,[ordered_set, {keypos,2} | Opts]),
+ ?line T2=ets_new(x,[set, {keypos,2}| Opts]),
?line 0 = ets:select_delete(T,[{{hej},[],[true]}]),
?line 0 = ets:select_delete(T,[{{hej,hopp},[],[true]}]),
?line 0 = ets:select_delete(T2,[{{hej},[],[true]}]),
@@ -543,7 +595,7 @@ t_ets_dets(Config, Opts) ->
?line (catch file:delete(Fname)),
?line {ok,DTab} = dets:open_file(testdets_1,
[{file, Fname}]),
- ?line ETab = ets:new(x,Opts),
+ ?line ETab = ets_new(x,Opts),
?line filltabint(ETab,3000),
?line DTab = ets:to_dets(ETab,DTab),
?line ets:delete_all_objects(ETab),
@@ -555,7 +607,7 @@ t_ets_dets(Config, Opts) ->
(catch ets:to_dets(ETab,DTab)),
?line {'EXIT',{badarg,[{ets,from_dets,[ETab,DTab]}|_]}} =
(catch ets:from_dets(ETab,DTab)),
- ?line ETab2 = ets:new(x,Opts),
+ ?line ETab2 = ets_new(x,Opts),
?line filltabint(ETab2,3000),
?line dets:close(DTab),
?line {'EXIT',{badarg,[{ets,to_dets,[ETab2,DTab]}|_]}} =
@@ -576,7 +628,7 @@ t_delete_all_objects(Config) when is_list(Config) ->
?line verify_etsmem(EtsMem).
t_delete_all_objects_do(Opts) ->
- ?line T=ets:new(x,Opts),
+ ?line T=ets_new(x,Opts),
?line filltabint(T,4000),
?line O=ets:first(T),
?line ets:next(T,O),
@@ -605,7 +657,7 @@ t_delete_object(Config) when is_list(Config) ->
?line verify_etsmem(EtsMem).
t_delete_object_do(Opts) ->
- ?line T = ets:new(x,Opts),
+ ?line T = ets_new(x,Opts),
?line filltabint(T,4000),
?line del_one_by_one_set(T,1,4001),
?line filltabint(T,4000),
@@ -622,19 +674,19 @@ t_delete_object_do(Opts) ->
?line 3999 = ets:info(T,size),
?line 0 = ets:info(T,kept_objects),
?line ets:delete(T),
- ?line T1 = ets:new(x,[ordered_set | Opts]),
+ ?line T1 = ets_new(x,[ordered_set | Opts]),
?line filltabint(T1,4000),
?line del_one_by_one_set(T1,1,4001),
?line filltabint(T1,4000),
?line del_one_by_one_set(T1,4000,0),
?line ets:delete(T1),
- ?line T2 = ets:new(x,[bag | Opts]),
+ ?line T2 = ets_new(x,[bag | Opts]),
?line filltabint2(T2,4000),
?line del_one_by_one_bag(T2,1,4001),
?line filltabint2(T2,4000),
?line del_one_by_one_bag(T2,4000,0),
?line ets:delete(T2),
- ?line T3 = ets:new(x,[duplicate_bag | Opts]),
+ ?line T3 = ets_new(x,[duplicate_bag | Opts]),
?line filltabint3(T3,4000),
?line del_one_by_one_dbag_1(T3,1,4001),
?line filltabint3(T3,4000),
@@ -681,7 +733,7 @@ t_init_table(Config) when is_list(Config)->
?line verify_etsmem(EtsMem).
t_init_table_do(Opts) ->
- ?line T = ets:new(x,[duplicate_bag | Opts]),
+ ?line T = ets_new(x,[duplicate_bag | Opts]),
?line filltabint(T,4000),
?line ets:init_table(T, make_init_fun(1)),
?line del_one_by_one_dbag_1(T,4000,0),
@@ -763,7 +815,7 @@ t_insert_list(Config) when is_list(Config) ->
?line verify_etsmem(EtsMem).
t_insert_list_do(Opts) ->
- ?line T = ets:new(x,[duplicate_bag | Opts]),
+ ?line T = ets_new(x,[duplicate_bag | Opts]),
?line do_fill_dbag_using_lists(T,4000),
?line del_one_by_one_dbag_2(T,4000,0),
?line ets:delete(T).
@@ -786,6 +838,67 @@ t_test_ms(Config) when is_list(Config) ->
?line true = (if is_list(String) -> true; true -> false end),
?line verify_etsmem(EtsMem).
+t_select_reverse(doc) ->
+ ["Test the select reverse BIF's"];
+t_select_reverse(suite) ->
+ [];
+t_select_reverse(Config) when is_list(Config) ->
+ ?line Table = ets_new(xxx, [ordered_set]),
+ ?line filltabint(Table,1000),
+ ?line A = lists:reverse(ets:select(Table,[{{'$1', '_'},
+ [{'>',
+ {'rem',
+ '$1', 5},
+ 2}],
+ ['$_']}])),
+ ?line A = ets:select_reverse(Table,[{{'$1', '_'},
+ [{'>',
+ {'rem',
+ '$1', 5},
+ 2}],
+ ['$_']}]),
+ ?line A = reverse_chunked(Table,[{{'$1', '_'},
+ [{'>',
+ {'rem',
+ '$1', 5},
+ 2}],
+ ['$_']}],3),
+ % A set/bag/duplicate_bag should get the same result regardless
+ % of select or select_reverse
+ ?line Table2 = ets_new(xxx, [set]),
+ ?line filltabint(Table2,1000),
+ ?line Table3 = ets_new(xxx, [bag]),
+ ?line filltabint(Table3,1000),
+ ?line Table4 = ets_new(xxx, [duplicate_bag]),
+ ?line filltabint(Table4,1000),
+ ?line lists:map(fun(Tab) ->
+ B = ets:select(Tab,[{{'$1', '_'},
+ [{'>',
+ {'rem',
+ '$1', 5},
+ 2}],
+ ['$_']}]),
+ B = ets:select_reverse(Tab,[{{'$1', '_'},
+ [{'>',
+ {'rem',
+ '$1', 5},
+ 2}],
+ ['$_']}])
+ end,[Table2, Table3, Table4]),
+ ok.
+
+
+
+reverse_chunked(T,MS,N) ->
+ do_reverse_chunked(ets:select_reverse(T,MS,N),[]).
+
+do_reverse_chunked('$end_of_table',Acc) ->
+ lists:reverse(Acc);
+do_reverse_chunked({L,C},Acc) ->
+ NewAcc = lists:reverse(L)++Acc,
+ do_reverse_chunked(ets:select_reverse(C), NewAcc).
+
+
t_select_delete(doc) ->
["Test the ets:select_delete/2 and ets:select_count/2 BIF's"];
t_select_delete(suite) ->
@@ -1064,8 +1177,8 @@ random_test() ->
do_random_test() ->
?line EtsMem = etsmem(),
- ?line OrdSet = ets:new(xxx,[ordered_set]),
- ?line Set = ets:new(xxx,[]),
+ ?line OrdSet = ets_new(xxx,[ordered_set]),
+ ?line Set = ets_new(xxx,[]),
?line do_n_times(fun() ->
?line Key = create_random_string(25),
?line Value = create_random_tuple(25),
@@ -1269,8 +1382,8 @@ update_element_opts(Opts) ->
update_element_opts(Tuple,KeyPos,UpdPos,Opts) ->
- Set = ets:new(set,[{keypos,KeyPos} | Opts]),
- OrdSet = ets:new(ordered_set,[ordered_set,{keypos,KeyPos} | Opts]),
+ Set = ets_new(set,[{keypos,KeyPos} | Opts]),
+ OrdSet = ets_new(ordered_set,[ordered_set,{keypos,KeyPos} | Opts]),
update_element(Set,Tuple,KeyPos,UpdPos),
update_element(OrdSet,Tuple,KeyPos,UpdPos),
true = ets:delete(Set),
@@ -1278,7 +1391,8 @@ update_element_opts(Tuple,KeyPos,UpdPos,Opts) ->
ok.
update_element(T,Tuple,KeyPos,UpdPos) ->
- KeyList = [Key || Key <- lists:seq(1,100)],
+ KeyList = [Key || Key <- [17,"seventeen",<<"seventeen">>,{17},list_to_binary(lists:seq(1,100)),
+ make_ref(), self()]],
lists:foreach(fun(Key) ->
TupleWithKey = setelement(KeyPos,Tuple,Key),
update_element_do(T,TupleWithKey,Key,UpdPos)
@@ -1313,21 +1427,13 @@ update_element_do(Tab,Tuple,Key,UpdPos) ->
{Pos, element(ToIx+1,Values)} % single {pos,value} arg
end,
- NewTupleF = fun({Pos,Val}, Tpl, _MeF) ->
- setelement(Pos, Tpl, Val);
- ([{Pos,Val} | Tail], Tpl, MeF) ->
- MeF(Tail,setelement(Pos, Tpl, Val),MeF);
- ([], Tpl, _MeF) ->
- Tpl
- end,
-
UpdateF = fun(ToIx,Rand) ->
PosValArg = PosValArgF(ToIx,[],UpdPos,Rand,PosValArgF),
%%io:format("update_element(~p)~n",[PosValArg]),
ArgHash = erlang:phash2({Tab,Key,PosValArg}),
?line true = ets:update_element(Tab, Key, PosValArg),
?line ArgHash = erlang:phash2({Tab,Key,PosValArg}),
- NewTuple = NewTupleF(PosValArg,Tuple,NewTupleF),
+ NewTuple = update_tuple(PosValArg,Tuple),
?line [NewTuple] = ets:lookup(Tab,Key)
end,
@@ -1355,9 +1461,18 @@ update_element_do(Tab,Tuple,Key,UpdPos) ->
?line Checksum = (Length-1)*Length*(Length+1) div 2, % if Length is a prime
ok.
+update_tuple({Pos,Val}, Tpl) ->
+ setelement(Pos, Tpl, Val);
+update_tuple([{Pos,Val} | Tail], Tpl) ->
+ update_tuple(Tail,setelement(Pos, Tpl, Val));
+update_tuple([], Tpl) ->
+ Tpl.
+
+
+
update_element_neg(Opts) ->
- Set = ets:new(set,Opts),
- OrdSet = ets:new(ordered_set,[ordered_set | Opts]),
+ Set = ets_new(set,Opts),
+ OrdSet = ets_new(ordered_set,[ordered_set | Opts]),
update_element_neg_do(Set),
update_element_neg_do(OrdSet),
ets:delete(Set),
@@ -1365,8 +1480,8 @@ update_element_neg(Opts) ->
ets:delete(OrdSet),
?line {'EXIT',{badarg,_}} = (catch ets:update_element(OrdSet,key,{2,1})),
- ?line Bag = ets:new(bag,[bag | Opts]),
- ?line DBag = ets:new(duplicate_bag,[duplicate_bag | Opts]),
+ ?line Bag = ets_new(bag,[bag | Opts]),
+ ?line DBag = ets_new(duplicate_bag,[duplicate_bag | Opts]),
?line {'EXIT',{badarg,_}} = (catch ets:update_element(Bag,key,{2,1})),
?line {'EXIT',{badarg,_}} = (catch ets:update_element(DBag,key,{2,1})),
true = ets:delete(Bag),
@@ -1416,8 +1531,8 @@ update_counter(Config) when is_list(Config) ->
?line verify_etsmem(EtsMem).
update_counter_do(Opts) ->
- Set = ets:new(set,Opts),
- OrdSet = ets:new(ordered_set,[ordered_set | Opts]),
+ Set = ets_new(set,Opts),
+ OrdSet = ets_new(ordered_set,[ordered_set | Opts]),
update_counter_for(Set),
update_counter_for(OrdSet),
ets:delete(Set),
@@ -1563,8 +1678,8 @@ uc_adder(Init, {_Pos, Add, Thres, Warp}) ->
end.
update_counter_neg(Opts) ->
- Set = ets:new(set,Opts),
- OrdSet = ets:new(ordered_set,[ordered_set | Opts]),
+ Set = ets_new(set,Opts),
+ OrdSet = ets_new(ordered_set,[ordered_set | Opts]),
update_counter_neg_for(Set),
update_counter_neg_for(OrdSet),
ets:delete(Set),
@@ -1572,8 +1687,8 @@ update_counter_neg(Opts) ->
ets:delete(OrdSet),
?line {'EXIT',{badarg,_}} = (catch ets:update_counter(OrdSet,key,1)),
- ?line Bag = ets:new(bag,[bag | Opts]),
- ?line DBag = ets:new(duplicate_bag,[duplicate_bag | Opts]),
+ ?line Bag = ets_new(bag,[bag | Opts]),
+ ?line DBag = ets_new(duplicate_bag,[duplicate_bag | Opts]),
?line {'EXIT',{badarg,_}} = (catch ets:update_counter(Bag,key,1)),
?line {'EXIT',{badarg,_}} = (catch ets:update_counter(DBag,key,1)),
true = ets:delete(Bag),
@@ -1646,7 +1761,7 @@ wait_for_all(Pids0) ->
end.
evil_counter(I,Opts) ->
- T = ets:new(a, Opts),
+ T = ets_new(a, Opts),
Start0 = case I rem 3 of
0 -> 16#12345678;
1 -> 16#12345678FFFFFFFF;
@@ -1654,7 +1769,7 @@ evil_counter(I,Opts) ->
end,
Start = Start0 + random:uniform(100000),
ets:insert(T, {dracula,Start}),
- Iter = 90000,
+ Iter = 40000,
End = Start + Iter,
End = evil_counter_1(Iter, T),
ets:delete(T).
@@ -1675,7 +1790,7 @@ fixtable_next(Config) when is_list(Config) ->
fixtable_next_do(Opts) ->
?line EtsMem = etsmem(),
- ?line do_fixtable_next(ets:new(set,[public | Opts])),
+ ?line do_fixtable_next(ets_new(set,[public | Opts])),
?line verify_etsmem(EtsMem).
do_fixtable_next(Tab) ->
@@ -1756,24 +1871,24 @@ write_concurrency(doc) -> ["The 'write_concurrency' option"];
write_concurrency(suite) -> [];
write_concurrency(Config) when is_list(Config) ->
?line EtsMem = etsmem(),
- Yes1 = ets:new(foo,[public,{write_concurrency,true}]),
- Yes2 = ets:new(foo,[protected,{write_concurrency,true}]),
- No1 = ets:new(foo,[private,{write_concurrency,true}]),
+ Yes1 = ets_new(foo,[public,{write_concurrency,true}]),
+ Yes2 = ets_new(foo,[protected,{write_concurrency,true}]),
+ No1 = ets_new(foo,[private,{write_concurrency,true}]),
- Yes3 = ets:new(foo,[bag,public,{write_concurrency,true}]),
- Yes4 = ets:new(foo,[bag,protected,{write_concurrency,true}]),
- No2 = ets:new(foo,[bag,private,{write_concurrency,true}]),
+ Yes3 = ets_new(foo,[bag,public,{write_concurrency,true}]),
+ Yes4 = ets_new(foo,[bag,protected,{write_concurrency,true}]),
+ No2 = ets_new(foo,[bag,private,{write_concurrency,true}]),
- Yes5 = ets:new(foo,[duplicate_bag,public,{write_concurrency,true}]),
- Yes6 = ets:new(foo,[duplicate_bag,protected,{write_concurrency,true}]),
- No3 = ets:new(foo,[duplicate_bag,private,{write_concurrency,true}]),
+ Yes5 = ets_new(foo,[duplicate_bag,public,{write_concurrency,true}]),
+ Yes6 = ets_new(foo,[duplicate_bag,protected,{write_concurrency,true}]),
+ No3 = ets_new(foo,[duplicate_bag,private,{write_concurrency,true}]),
- No4 = ets:new(foo,[ordered_set,public,{write_concurrency,true}]),
- No5 = ets:new(foo,[ordered_set,protected,{write_concurrency,true}]),
- No6 = ets:new(foo,[ordered_set,private,{write_concurrency,true}]),
+ No4 = ets_new(foo,[ordered_set,public,{write_concurrency,true}]),
+ No5 = ets_new(foo,[ordered_set,protected,{write_concurrency,true}]),
+ No6 = ets_new(foo,[ordered_set,private,{write_concurrency,true}]),
- No7 = ets:new(foo,[public,{write_concurrency,false}]),
- No8 = ets:new(foo,[protected,{write_concurrency,false}]),
+ No7 = ets_new(foo,[public,{write_concurrency,false}]),
+ No8 = ets_new(foo,[protected,{write_concurrency,false}]),
?line YesMem = ets:info(Yes1,memory),
?line NoHashMem = ets:info(No1,memory),
@@ -1800,10 +1915,10 @@ write_concurrency(Config) when is_list(Config) ->
?line true = YesMem =:= NoHashMem
end,
- ?line {'EXIT',{badarg,_}} = (catch ets:new(foo,[public,{write_concurrency,foo}])),
- ?line {'EXIT',{badarg,_}} = (catch ets:new(foo,[public,{write_concurrency}])),
- ?line {'EXIT',{badarg,_}} = (catch ets:new(foo,[public,{write_concurrency,true,foo}])),
- ?line {'EXIT',{badarg,_}} = (catch ets:new(foo,[public,write_concurrency])),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new(foo,[public,{write_concurrency,foo}])),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new(foo,[public,{write_concurrency}])),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new(foo,[public,{write_concurrency,true,foo}])),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new(foo,[public,write_concurrency])),
lists:foreach(fun(T) -> ets:delete(T) end,
[Yes1,Yes2,Yes3,Yes4,Yes5,Yes6,
@@ -1880,7 +1995,7 @@ heir_founder(Master, HeirData, Opts) ->
none -> {heir,none};
_ -> {heir, Heir, HeirData}
end,
- ?line T = ets:new(foo,[named_table, private, HeirTpl | Opts]),
+ ?line T = ets_new(foo,[named_table, private, HeirTpl | Opts]),
?line true = ets:insert(T,{key,1}),
?line [{key,1}] = ets:lookup(T,key),
Self = self(),
@@ -1952,7 +2067,7 @@ give_away(Config) when is_list(Config) ->
repeat_for_opts(give_away_do).
give_away_do(Opts) ->
- ?line T = ets:new(foo,[named_table, private | Opts]),
+ ?line T = ets_new(foo,[named_table, private | Opts]),
?line true = ets:insert(T,{key,1}),
?line [{key,1}] = ets:lookup(T,key),
Parent = self(),
@@ -1978,7 +2093,7 @@ give_away_do(Opts) ->
?line undefined = ets:info(T),
%% Give and then kill receiver to get back
- ?line T2 = ets:new(foo,[private | Opts]),
+ ?line T2 = ets_new(foo,[private | Opts]),
?line true = ets:insert(T2,{key,1}),
?line ets:setopts(T2,{heir,self(),"Som en gummiboll..."}),
?line {Receiver2,Mref2} = spawn_monitor(fun()-> give_away_receiver(T2,Parent) end),
@@ -2000,7 +2115,7 @@ give_away_do(Opts) ->
?line give_me = receive_any(),
?line {'EXIT',{badarg,_}} = (catch ets:give_away(T2,ReceiverNeg,"A deleted table")),
- ?line T3 = ets:new(foo,[public | Opts]),
+ ?line T3 = ets_new(foo,[public | Opts]),
spawn_link(fun()-> {'EXIT',{badarg,_}} = (catch ets:give_away(T3,ReceiverNeg,"From non owner")),
Parent ! done
end),
@@ -2035,7 +2150,7 @@ setopts(Config) when is_list(Config) ->
setopts_do(Opts) ->
Self = self(),
- ?line T = ets:new(foo,[named_table, private | Opts]),
+ ?line T = ets_new(foo,[named_table, private | Opts]),
?line none = ets:info(T,heir),
Heir = spawn_link(fun()->heir_heir(Self) end),
?line ets:setopts(T,{heir,Heir,"Data"}),
@@ -2088,10 +2203,10 @@ bad_table(Config) when is_list(Config) ->
bad_table_do(Opts, DummyFile) ->
Parent = self(),
- {Pid,Mref} = spawn_opt(fun()-> ets:new(priv,[private,named_table | Opts]),
- Priv = ets:new(priv,[private | Opts]),
- ets:new(prot,[protected,named_table | Opts]),
- Prot = ets:new(prot,[protected | Opts]),
+ {Pid,Mref} = spawn_opt(fun()-> ets_new(priv,[private,named_table | Opts]),
+ Priv = ets_new(priv,[private | Opts]),
+ ets_new(prot,[protected,named_table | Opts]),
+ Prot = ets_new(prot,[protected | Opts]),
Parent ! {self(),Priv,Prot},
die_please = receive_any()
end,
@@ -2149,11 +2264,11 @@ bad_table_do(Opts, DummyFile) ->
bad_table_op({Opts,Priv,Prot}, Op) ->
%%io:format("Doing Op=~p on ~p's\n",[Op,Type]),
- T1 = ets:new(noname,Opts),
+ T1 = ets_new(noname,Opts),
bad_table_call(noname,Op),
ets:delete(T1),
bad_table_call(T1,Op),
- T2 = ets:new(named,[named_table | Opts]),
+ T2 = ets_new(named,[named_table | Opts]),
ets:delete(T2),
bad_table_call(named,Op),
bad_table_call(T2,Op),
@@ -2187,7 +2302,7 @@ rename(Config) when is_list(Config) ->
rename_do(Opts) ->
?line EtsMem = etsmem(),
- ets:new(foobazz,[named_table, public | Opts]),
+ ets_new(foobazz,[named_table, public | Opts]),
ets:insert(foobazz,{foo,bazz}),
ungermanbazz = ets:rename(foobazz,ungermanbazz),
{'EXIT',{badarg, _}} = (catch ets:lookup(foobazz,foo)),
@@ -2205,7 +2320,7 @@ rename_unnamed(Config) when is_list(Config) ->
rename_unnamed_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(bonkz,[public | Opts]),
+ ?line Tab = ets_new(bonkz,[public | Opts]),
?line {'EXIT',{badarg, _}} = (catch ets:insert(bonkz,{foo,bazz})),
?line bonkz = ets:info(Tab, name),
?line Tab = ets:rename(Tab, tjabonkz),
@@ -2224,7 +2339,7 @@ evil_rename(Config) when is_list(Config) ->
evil_rename_1(Old, New, Flags) ->
?line process_flag(trap_exit, true),
- ?line Old = ets:new(Old, Flags),
+ ?line Old = ets_new(Old, Flags),
?line Fixer = fun() -> ets:safe_fixtable(Old, true) end,
?line crazy_fixtable(15000, Fixer),
?line erlang:yield(),
@@ -2234,7 +2349,7 @@ evil_rename_1(Old, New, Flags) ->
ok.
crazy_fixtable(N, Fixer) ->
- Dracula = ets:new(count_dracula, [public]),
+ Dracula = ets_new(count_dracula, [public]),
ets:insert(Dracula, {count,0}),
SpawnFun = fun() ->
Fixer(),
@@ -2268,7 +2383,7 @@ evil_creater_destroyer() ->
ets:delete(T1).
evil_create_fixed_tab() ->
- T = ets:new(arne, [public]),
+ T = ets_new(arne, [public]),
ets:safe_fixtable(T, true),
T.
@@ -2282,8 +2397,8 @@ interface_equality(Config) when is_list(Config) ->
interface_equality_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Set = ets:new(set,[set | Opts]),
- ?line OrderedSet = ets:new(ordered_set,[ordered_set | Opts]),
+ ?line Set = ets_new(set,[set | Opts]),
+ ?line OrderedSet = ets_new(ordered_set,[ordered_set | Opts]),
?line F = fun(X,T,FF) -> case X of
0 -> true;
_ ->
@@ -2362,7 +2477,7 @@ ordered_match_do(Opts) ->
FF(X-1,T,FF)
end
end,
- ?line T1 = ets:new(xxx,[ordered_set| Opts]),
+ ?line T1 = ets_new(xxx,[ordered_set| Opts]),
?line F(3000,T1,F),
?line [[3,3],[3,3],[3,3]] = ets:match(T1, {'_','_','$1','$2',3}),
?line F2 = fun(X,Rem,Res,FF) -> case X of
@@ -2400,7 +2515,7 @@ ordered(Config) when is_list(Config) ->
ordered_do(Opts) ->
?line EtsMem = etsmem(),
- ?line T = ets:new(oset, [ordered_set | Opts]),
+ ?line T = ets_new(oset, [ordered_set | Opts]),
?line InsList = [
25,26,27,28,
5,6,7,8,
@@ -2461,8 +2576,8 @@ setbag(doc) -> ["Small test case for both set and bag type ets tables."];
setbag(suite) -> [];
setbag(Config) when is_list(Config) ->
?line EtsMem = etsmem(),
- ?line Set = ets:new(set,[set]),
- ?line Bag = ets:new(bag,[bag]),
+ ?line Set = ets_new(set,[set]),
+ ?line Bag = ets_new(bag,[bag]),
?line Key = {foo,bar},
%% insert some value
@@ -2482,15 +2597,15 @@ setbag(Config) when is_list(Config) ->
?line verify_etsmem(EtsMem).
badnew(doc) ->
- ["Test case to check proper return values for illegal ets:new() calls."];
+ ["Test case to check proper return values for illegal ets_new() calls."];
badnew(suite) -> [];
badnew(Config) when is_list(Config) ->
?line EtsMem = etsmem(),
- ?line {'EXIT',{badarg,_}} = (catch ets:new(12,[])),
- ?line {'EXIT',{badarg,_}} = (catch ets:new({a,b},[])),
- ?line {'EXIT',{badarg,_}} = (catch ets:new(name,[foo])),
- ?line {'EXIT',{badarg,_}} = (catch ets:new(name,{bag})),
- ?line {'EXIT',{badarg,_}} = (catch ets:new(name,bag)),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new(12,[])),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new({a,b},[])),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new(name,[foo])),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new(name,{bag})),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new(name,bag)),
?line verify_etsmem(EtsMem).
verybadnew(doc) ->
@@ -2499,7 +2614,7 @@ verybadnew(doc) ->
verybadnew(suite) -> [];
verybadnew(Config) when is_list(Config) ->
?line EtsMem = etsmem(),
- ?line {'EXIT',{badarg,_}} = (catch ets:new(verybad,[set|protected])),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new(verybad,[set|protected])),
?line verify_etsmem(EtsMem).
named(doc) -> ["Small check to see if named tables work."];
@@ -2576,9 +2691,9 @@ privacy_check(Pub,Prot,Priv) ->
?line [] = ets:lookup(Prot,foo).
privacy_owner(Boss, Opts) ->
- ets:new(pub, [public,named_table | Opts]),
- ets:new(prot,[protected,named_table | Opts]),
- ets:new(priv,[private,named_table | Opts]),
+ ets_new(pub, [public,named_table | Opts]),
+ ets_new(prot,[protected,named_table | Opts]),
+ ets_new(priv,[private,named_table | Opts]),
Boss ! ok,
privacy_owner_loop(Boss).
@@ -2616,7 +2731,7 @@ empty(Config) when is_list(Config) ->
empty_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foo,Opts),
+ ?line Tab = ets_new(foo,Opts),
?line [] = ets:lookup(Tab,key),
?line true = ets:insert(Tab,{key2,val}),
?line [] = ets:lookup(Tab,key),
@@ -2633,10 +2748,10 @@ badinsert_do(Opts) ->
?line EtsMem = etsmem(),
?line {'EXIT',{badarg,_}} = (catch ets:insert(foo,{key,val})),
- ?line Tab = ets:new(foo,Opts),
+ ?line Tab = ets_new(foo,Opts),
?line {'EXIT',{badarg,_}} = (catch ets:insert(Tab,{})),
- ?line Tab3 = ets:new(foo,[{keypos,3}| Opts]),
+ ?line Tab3 = ets_new(foo,[{keypos,3}| Opts]),
?line {'EXIT',{badarg,_}} = (catch ets:insert(Tab3,{a,b})),
?line {'EXIT',{badarg,_}} = (catch ets:insert(Tab,[key,val2])),
@@ -2660,7 +2775,7 @@ time_lookup(Config) when is_list(Config) ->
"~p ets lookups/s",[Values]))}.
time_lookup_do(Opts) ->
- ?line Tab = ets:new(foo,Opts),
+ ?line Tab = ets_new(foo,Opts),
?line fill_tab(Tab,foo),
?line ets:insert(Tab,{{a,key},foo}),
?line {Time,_} = ?t:timecall(test_server,do_times,
@@ -2675,7 +2790,7 @@ badlookup(suite) -> [];
badlookup(Config) when is_list(Config) ->
?line EtsMem = etsmem(),
?line {'EXIT',{badarg,_}} = (catch ets:lookup(foo,key)),
- ?line Tab = ets:new(foo,[]),
+ ?line Tab = ets_new(foo,[]),
?line ets:delete(Tab),
?line {'EXIT',{badarg,_}} = (catch ets:lookup(Tab,key)),
?line verify_etsmem(EtsMem).
@@ -2700,7 +2815,7 @@ lookup_order_2(Opts, Fixed) ->
Pair = [{A,B},{B,A},{A,C},{C,A},{B,C},{C,B}],
Combos = [{D1,D2,D3} || D1<-ABC, D2<-Pair, D3<-Pair],
lists:foreach(fun({D1,{D2a,D2b},{D3a,D3b}}) ->
- T = ets:new(foo,Opts),
+ T = ets_new(foo,Opts),
case Fixed of
true -> ets:safe_fixtable(T,true);
false -> ok
@@ -2784,10 +2899,12 @@ lookup_element_mult(Config) when is_list(Config) ->
lookup_element_mult_do(Opts) ->
?line EtsMem = etsmem(),
- ?line T = ets:new(service, [bag, {keypos, 2} | Opts]),
+ ?line T = ets_new(service, [bag, {keypos, 2} | Opts]),
?line D = lists:reverse(lem_data()),
?line lists:foreach(fun(X) -> ets:insert(T, X) end, D),
?line ok = lem_crash_3(T),
+ ?line ets:insert(T, {0, "heap_key"}),
+ ?line ets:lookup_element(T, "heap_key", 2),
?line true = ets:delete(T),
?line verify_etsmem(EtsMem).
@@ -2829,7 +2946,7 @@ delete_elem(Config) when is_list(Config) ->
delete_elem_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foo,Opts),
+ ?line Tab = ets_new(foo,Opts),
?line fill_tab(Tab,foo),
?line ets:insert(Tab,{{b,key},foo}),
?line ets:insert(Tab,{{c,key},foo}),
@@ -2849,17 +2966,17 @@ delete_tab(Config) when is_list(Config) ->
delete_tab_do(Opts) ->
Name = foo,
?line EtsMem = etsmem(),
- ?line Name = ets:new(Name, [named_table | Opts]),
+ ?line Name = ets_new(Name, [named_table | Opts]),
?line true = ets:delete(foo),
%% The name should be available again.
- ?line Name = ets:new(Name, [named_table | Opts]),
+ ?line Name = ets_new(Name, [named_table | Opts]),
?line true = ets:delete(Name),
?line verify_etsmem(EtsMem).
delete_large_tab(doc) ->
"Check that ets:delete/1 works and that other processes can run.";
delete_large_tab(Config) when is_list(Config) ->
- ?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 500000)],
+ ?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 200000)],
?line EtsMem = etsmem(),
repeat_for_opts(fun(Opts) -> delete_large_tab_do(Opts,Data) end),
?line verify_etsmem(EtsMem).
@@ -2871,7 +2988,7 @@ delete_large_tab_do(Opts,Data) ->
delete_large_tab_1(Name, Flags, Data, Fix) ->
- ?line Tab = ets:new(Name, Flags),
+ ?line Tab = ets_new(Name, Flags),
?line ets:insert(Tab, Data),
case Fix of
@@ -2938,7 +3055,7 @@ delete_large_named_table_do(Opts,Data) ->
?line delete_large_named_table_1(foo_hash, [named_table | Opts], Data, true).
delete_large_named_table_1(Name, Flags, Data, Fix) ->
- ?line Tab = ets:new(Name, Flags),
+ ?line Tab = ets_new(Name, Flags),
?line ets:insert(Tab, Data),
case Fix of
@@ -2951,7 +3068,7 @@ delete_large_named_table_1(Name, Flags, Data, Fix) ->
Pid = spawn_link(fun() ->
receive
{trace,Parent,call,_} ->
- ets:new(Name, [named_table])
+ ets_new(Name, [named_table])
end
end),
?line erlang:trace(self(), true, [call,{tracer,Pid}]),
@@ -2985,7 +3102,7 @@ evil_delete_do(Opts,Data) ->
evil_delete_not_owner(Name, Flags, Data, Fix) ->
io:format("Not owner: ~p, fix = ~p", [Name,Fix]),
- ?line Tab = ets:new(Name, [public|Flags]),
+ ?line Tab = ets_new(Name, [public|Flags]),
?line ets:insert(Tab, Data),
case Fix of
false -> ok;
@@ -3010,7 +3127,7 @@ evil_delete_not_owner(Name, Flags, Data, Fix) ->
evil_delete_owner(Name, Flags, Data, Fix) ->
?line Fun = fun() ->
- ?line Tab = ets:new(Name, [public|Flags]),
+ ?line Tab = ets_new(Name, [public|Flags]),
?line ets:insert(Tab, Data),
case Fix of
false -> ok;
@@ -3037,48 +3154,60 @@ exit_large_table_owner(doc) ->
exit_large_table_owner(suite) ->
[];
exit_large_table_owner(Config) when is_list(Config) ->
- ?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 500000)],
+ %%?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 500000)],
+ ?line FEData = fun(Do) -> repeat_while(fun(500000) -> {false,ok};
+ (I) -> Do({erlang:phash2(I, 16#ffffff),I}),
+ {true, I+1}
+ end, 1)
+ end,
?line EtsMem = etsmem(),
- repeat_for_opts(fun(Opts) -> exit_large_table_owner_do(Opts,Data,Config) end),
+ repeat_for_opts({exit_large_table_owner_do,{FEData,Config}}),
?line verify_etsmem(EtsMem).
-exit_large_table_owner_do(Opts,Data,Config) ->
- ?line verify_rescheduling_exit(Config, Data, [named_table | Opts], true, 1, 1),
- ?line verify_rescheduling_exit(Config, Data, Opts, false, 1, 1).
+exit_large_table_owner_do(Opts,{FEData,Config}) ->
+ ?line verify_rescheduling_exit(Config, FEData, [named_table | Opts], true, 1, 1),
+ ?line verify_rescheduling_exit(Config, FEData, Opts, false, 1, 1).
exit_many_large_table_owner(doc) -> [];
exit_many_large_table_owner(suite) -> [];
exit_many_large_table_owner(Config) when is_list(Config) ->
- ?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 500000)],
+ %%?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 500000)],
+ ?line FEData = fun(Do) -> repeat_while(fun(500000) -> {false,ok};
+ (I) -> Do({erlang:phash2(I, 16#ffffff),I}),
+ {true, I+1}
+ end, 1)
+ end,
?line EtsMem = etsmem(),
- repeat_for_opts(fun(Opts) -> exit_many_large_table_owner_do(Opts,Data,Config) end),
+ repeat_for_opts(fun(Opts) -> exit_many_large_table_owner_do(Opts,FEData,Config) end),
?line verify_etsmem(EtsMem).
-exit_many_large_table_owner_do(Opts,Data,Config) ->
- ?line verify_rescheduling_exit(Config, Data, Opts, true, 1, 4),
- ?line verify_rescheduling_exit(Config, Data, [named_table | Opts], false, 1, 4).
+exit_many_large_table_owner_do(Opts,FEData,Config) ->
+ ?line verify_rescheduling_exit(Config, FEData, Opts, true, 1, 4),
+ ?line verify_rescheduling_exit(Config, FEData, [named_table | Opts], false, 1, 4).
exit_many_tables_owner(doc) -> [];
exit_many_tables_owner(suite) -> [];
exit_many_tables_owner(Config) when is_list(Config) ->
+ NoData = fun(_Do) -> ok end,
?line EtsMem = etsmem(),
- ?line verify_rescheduling_exit(Config, [], [named_table], false, 1000, 1),
- ?line verify_rescheduling_exit(Config, [], [named_table,{write_concurrency,true}], false, 1000, 1),
+ ?line verify_rescheduling_exit(Config, NoData, [named_table], false, 1000, 1),
+ ?line verify_rescheduling_exit(Config, NoData, [named_table,{write_concurrency,true}], false, 1000, 1),
?line verify_etsmem(EtsMem).
exit_many_many_tables_owner(doc) -> [];
exit_many_many_tables_owner(suite) -> [];
exit_many_many_tables_owner(Config) when is_list(Config) ->
?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 50)],
- repeat_for_opts(fun(Opts) -> exit_many_many_tables_owner_do(Opts,Data,Config) end).
+ ?line FEData = fun(Do) -> lists:foreach(Do, Data) end,
+ repeat_for_opts(fun(Opts) -> exit_many_many_tables_owner_do(Opts,FEData,Config) end).
-exit_many_many_tables_owner_do(Opts,Data,Config) ->
- ?line verify_rescheduling_exit(Config, Data, [named_table | Opts], true, 200, 5),
- ?line verify_rescheduling_exit(Config, Data, Opts, false, 200, 5),
+exit_many_many_tables_owner_do(Opts,FEData,Config) ->
+ ?line verify_rescheduling_exit(Config, FEData, [named_table | Opts], true, 200, 5),
+ ?line verify_rescheduling_exit(Config, FEData, Opts, false, 200, 5),
?line wait_for_test_procs(),
?line EtsMem = etsmem(),
- ?line verify_rescheduling_exit(Config, Data, Opts, true, 200, 5),
- ?line verify_rescheduling_exit(Config, Data, [named_table | Opts], false, 200, 5),
+ ?line verify_rescheduling_exit(Config, FEData, Opts, true, 200, 5),
+ ?line verify_rescheduling_exit(Config, FEData, [named_table | Opts], false, 200, 5),
?line verify_etsmem(EtsMem).
@@ -3121,7 +3250,7 @@ vre_fix_tables(Tab) ->
receive Go -> ok end,
ok.
-verify_rescheduling_exit(Config, Data, Flags, Fix, NOTabs, NOProcs) ->
+verify_rescheduling_exit(Config, ForEachData, Flags, Fix, NOTabs, NOProcs) ->
?line NoFix = 5,
?line TestCase = atom_to_list(?config(test_case, Config)),
?line Parent = self(),
@@ -3136,8 +3265,8 @@ verify_rescheduling_exit(Config, Data, Flags, Fix, NOTabs, NOProcs) ->
++ "-" ++ integer_to_list(A)
++ "-" ++ integer_to_list(B)
++ "-" ++ integer_to_list(C)),
- Tab = ets:new(Name, Flags),
- ets:insert(Tab, Data),
+ Tab = ets_new(Name, Flags),
+ ForEachData(fun(Data) -> ets:insert(Tab, Data) end),
case Fix of
false -> ok;
true ->
@@ -3145,10 +3274,10 @@ verify_rescheduling_exit(Config, Data, Flags, Fix, NOTabs, NOProcs) ->
vre_fix_tables(Tab)
end,
lists:seq(1,NoFix)),
- lists:foreach(fun({K,_}) ->
- ets:delete(Tab, K)
- end,
- Data)
+ KeyPos = ets:info(Tab,keypos),
+ ForEachData(fun(Data) ->
+ ets:delete(Tab, element(KeyPos,Data))
+ end)
end
end,
NOTabs),
@@ -3195,7 +3324,7 @@ table_leak(Config) when is_list(Config) ->
table_leak_1(_,0) -> ok;
table_leak_1(Opts,N) ->
- ?line T = ets:new(fooflarf, Opts),
+ ?line T = ets_new(fooflarf, Opts),
?line true = ets:delete(T),
table_leak_1(Opts,N-1).
@@ -3205,7 +3334,7 @@ baddelete(suite) -> [];
baddelete(Config) when is_list(Config) ->
?line EtsMem = etsmem(),
?line {'EXIT',{badarg,_}} = (catch ets:delete(foo)),
- ?line Tab = ets:new(foo,[]),
+ ?line Tab = ets_new(foo,[]),
?line true = ets:delete(Tab),
?line {'EXIT',{badarg,_}} = (catch ets:delete(Tab)),
?line verify_etsmem(EtsMem).
@@ -3220,7 +3349,7 @@ match_delete(Config) when is_list(Config) ->
match_delete_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(kad,Opts),
+ ?line Tab = ets_new(kad,Opts),
?line fill_tab(Tab,foo),
?line ets:insert(Tab,{{c,key},bar}),
?line _ = ets:match_delete(Tab,{'_',foo}),
@@ -3264,7 +3393,7 @@ firstnext(Config) when is_list(Config) ->
firstnext_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foo,Opts),
+ ?line Tab = ets_new(foo,Opts),
?line [] = firstnext_collect(Tab,ets:first(Tab),[]),
?line fill_tab(Tab,foo),
?line Len = length(ets:tab2list(Tab)),
@@ -3290,7 +3419,7 @@ firstnext_concurrent(Config) when is_list(Config) ->
end.
ets_init(Tab, N) ->
- ets:new(Tab, [named_table,public,ordered_set]),
+ ets_new(Tab, [named_table,public,ordered_set]),
cycle(Tab, lists:seq(1,N+1)).
cycle(_Tab, [H|T]) when H > length(T)-> ok;
@@ -3323,7 +3452,7 @@ slot(Config) when is_list(Config) ->
slot_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foo,Opts),
+ ?line Tab = ets_new(foo,Opts),
?line fill_tab(Tab,foo),
?line Elts = ets:info(Tab,size),
?line Elts = slot_loop(Tab,0,0),
@@ -3350,7 +3479,7 @@ match1(Config) when is_list(Config) ->
match1_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foo,Opts),
+ ?line Tab = ets_new(foo,Opts),
?line fill_tab(Tab,foo),
?line [] = ets:match(Tab,{}),
?line ets:insert(Tab,{{one,4},4}),
@@ -3415,7 +3544,7 @@ match_object(Config) when is_list(Config) ->
match_object_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foobar, Opts),
+ ?line Tab = ets_new(foobar, Opts),
?line fill_tab(Tab, foo),
?line ets:insert(Tab, {{one, 4}, 4}),
?line ets:insert(Tab,{{one,5},5}),
@@ -3459,7 +3588,7 @@ match_object2(Config) when is_list(Config) ->
match_object2_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foo, [bag, {keypos, 2} | Opts]),
+ ?line Tab = ets_new(foo, [bag, {keypos, 2} | Opts]),
?line fill_tab2(Tab, 0, 13005), % match_db_object does 1000
% elements per pass, might
% change in the future.
@@ -3498,7 +3627,7 @@ misc1(Config) when is_list(Config) ->
misc1_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foo,Opts),
+ ?line Tab = ets_new(foo,Opts),
?line true = lists:member(Tab,ets:all()),
?line ets:delete(Tab),
?line false = lists:member(Tab,ets:all()),
@@ -3517,7 +3646,7 @@ safe_fixtable(Config) when is_list(Config) ->
safe_fixtable_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foo, Opts),
+ ?line Tab = ets_new(foo, Opts),
?line fill_tab(Tab, foobar),
?line true = ets:safe_fixtable(Tab, true),
?line receive after 1 -> ok end,
@@ -3556,7 +3685,7 @@ info_do(Opts) ->
?line EtsMem = etsmem(),
?line MeMyselfI=self(),
?line ThisNode=node(),
- ?line Tab = ets:new(foobar, [{keypos, 2} | Opts]),
+ ?line Tab = ets_new(foobar, [{keypos, 2} | Opts]),
%% Note: ets:info/1 used to return a tuple, but from R11B onwards it
%% returns a list.
@@ -3610,7 +3739,7 @@ dups_do(Opts) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-files(suite) -> [tab2file, tab2file2, tab2file3, tabfile_ext1, tabfile_ext2,
+files(suite) -> [tab2file, tab2file2, tabfile_ext1, tabfile_ext2,
tabfile_ext3, tabfile_ext4].
tab2file(doc) -> ["Check the ets:tab2file function on an empty "
@@ -3618,7 +3747,7 @@ tab2file(doc) -> ["Check the ets:tab2file function on an empty "
tab2file(suite) -> [];
tab2file(Config) when is_list(Config) ->
%% Write an empty ets table to a file, read back and check properties.
- ?line Tab = ets:new(ets_SUITE_foo_tab, [named_table, set, private,
+ ?line Tab = ets_new(ets_SUITE_foo_tab, [named_table, set, private,
{keypos, 2}]),
?line FName = filename:join([?config(priv_dir, Config),"tab2file_case"]),
?line ok = ets:tab2file(Tab, FName),
@@ -3634,51 +3763,36 @@ tab2file(Config) when is_list(Config) ->
?line verify_etsmem(EtsMem).
tab2file2(doc) -> ["Check the ets:tab2file function on a ",
- "filled set type ets table."];
+ "filled set/bag type ets table."];
tab2file2(suite) -> [];
-tab2file2(Config) when is_list(Config) ->
- %% Try the same on a filled set table.
- ?line EtsMem = etsmem(),
- ?line Tab = ets:new(ets_SUITE_foo_tab, [named_table, set, private,
- {keypos, 2}]),
- ?line FName = filename:join([?config(priv_dir, Config),"tab2file2_case"]),
- ?line ok = fill_tab2(Tab, 0, 10000), % Fill up the table (grucho mucho!)
- ?line Len = length(ets:tab2list(Tab)),
- ?line ok = ets:tab2file(Tab, FName),
- ?line true = ets:delete(Tab),
- %
- ?line {ok, Tab2} = ets:file2tab(FName),
- ?line private = ets:info(Tab2, protection),
- ?line true = ets:info(Tab2, named_table),
- ?line 2 = ets:info(Tab2, keypos),
- ?line set = ets:info(Tab2, type),
- ?line Len = length(ets:tab2list(Tab2)),
- ?line true = ets:delete(Tab2),
- ?line verify_etsmem(EtsMem).
+tab2file2(Config) when is_list(Config) ->
+ repeat_for_opts({tab2file2_do,Config}, [[set,bag],compressed]).
-tab2file3(doc) -> ["Check the ets:tab2file function on a ",
- "filled bag type ets table."];
-tab2file3(suite) -> [];
-tab2file3(Config) when is_list(Config) ->
- %% Try the same on a filled bag table.
+tab2file2_do(Opts, Config) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(ets_SUITE_foo_tab, [named_table, bag, private,
- {keypos, 2}]),
- ?line FName = filename:join([?config(priv_dir, Config),"tab2file3_case"]),
+ ?line Tab = ets_new(ets_SUITE_foo_tab, [named_table, private,
+ {keypos, 2} | Opts]),
+ ?line FName = filename:join([?config(priv_dir, Config),"tab2file2_case"]),
?line ok = fill_tab2(Tab, 0, 10000), % Fill up the table (grucho mucho!)
?line Len = length(ets:tab2list(Tab)),
?line Mem = ets:info(Tab, memory),
+ ?line Type = ets:info(Tab, type),
+ %%io:format("org tab: ~p\n",[ets:info(Tab)]),
?line ok = ets:tab2file(Tab, FName),
?line true = ets:delete(Tab),
+ ?line EtsMem4 = etsmem(),
+
?line {ok, Tab2} = ets:file2tab(FName),
+ %%io:format("loaded tab: ~p\n",[ets:info(Tab2)]),
?line private = ets:info(Tab2, protection),
?line true = ets:info(Tab2, named_table),
?line 2 = ets:info(Tab2, keypos),
- ?line bag = ets:info(Tab2, type),
+ ?line Type = ets:info(Tab2, type),
?line Len = length(ets:tab2list(Tab2)),
?line Mem = ets:info(Tab2, memory),
?line true = ets:delete(Tab2),
+ io:format("Between = ~p\n", [EtsMem4]),
?line verify_etsmem(EtsMem).
-define(test_list, [8,5,4,1,58,125,255, 250, 245, 240, 235,
@@ -3722,7 +3836,7 @@ tabfile_ext1_do(Opts,Config) ->
?line FName = filename:join([?config(priv_dir, Config),"nisse.dat"]),
?line FName2 = filename:join([?config(priv_dir, Config),"countflip.dat"]),
L = lists:seq(1,10),
- T = ets:new(x,Opts),
+ T = ets_new(x,Opts),
Name = make_ref(),
[ets:insert(T,{X,integer_to_list(X)}) || X <- L],
ok = ets:tab2file(T,FName,[{extended_info,[object_count]}]),
@@ -3762,7 +3876,7 @@ tabfile_ext2_do(Opts,Config) ->
?line FName = filename:join([?config(priv_dir, Config),"olle.dat"]),
?line FName2 = filename:join([?config(priv_dir, Config),"bitflip.dat"]),
L = lists:seq(1,10),
- T = ets:new(x,Opts),
+ T = ets_new(x,Opts),
Name = make_ref(),
[ets:insert(T,{X,integer_to_list(X)}) || X <- L],
ok = ets:tab2file(T,FName,[{extended_info,[md5sum]}]),
@@ -3800,7 +3914,7 @@ tabfile_ext3(Config) when is_list(Config) ->
?line FName2 = filename:join([?config(priv_dir, Config),"ncountflip.dat"]),
L = lists:seq(1,10),
Name = make_ref(),
- ?MODULE = ets:new(?MODULE,[named_table]),
+ ?MODULE = ets_new(?MODULE,[named_table]),
[ets:insert(?MODULE,{X,integer_to_list(X)}) || X <- L],
ets:tab2file(?MODULE,FName),
{error,cannot_create_table} = ets:file2tab(FName),
@@ -3832,7 +3946,7 @@ tabfile_ext4(doc) ->
tabfile_ext4(Config) when is_list(Config) ->
?line FName = filename:join([?config(priv_dir, Config),"bauta.dat"]),
LL = lists:seq(1,10000),
- TL = ets:new(x,[]),
+ TL = ets_new(x,[]),
Name2 = make_ref(),
[ets:insert(TL,{X,integer_to_list(X)}) || X <- LL],
ok = ets:tab2file(TL,FName,[{extended_info,[md5sum]}]),
@@ -3877,7 +3991,7 @@ make_sub_binary(List, Num) when is_list(List) ->
{_,B} = split_binary(Bin, N+1),
B.
-heavy(suite) -> [heavy_lookup, heavy_lookup_element].
+heavy(suite) -> [heavy_lookup, heavy_lookup_element, heavy_concurrent].
%% Lookup stuff like crazy...
heavy_lookup(doc) -> ["Performs multiple lookups for every key ",
@@ -3888,7 +4002,7 @@ heavy_lookup(Config) when is_list(Config) ->
heavy_lookup_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foobar_table, [set, protected, {keypos, 2} | Opts]),
+ ?line Tab = ets_new(foobar_table, [set, protected, {keypos, 2} | Opts]),
?line ok = fill_tab2(Tab, 0, 7000),
?line ?t:do_times(50, ?MODULE, do_lookup, [Tab, 6999]),
?line true = ets:delete(Tab),
@@ -3911,7 +4025,7 @@ heavy_lookup_element(Config) when is_list(Config) ->
heavy_lookup_element_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foobar_table, [set, protected, {keypos, 2} | Opts]),
+ ?line Tab = ets_new(foobar_table, [set, protected, {keypos, 2} | Opts]),
?line ok = fill_tab2(Tab, 0, 7000),
case os:type() of
vxworks ->
@@ -3940,6 +4054,44 @@ do_lookup_element(Tab, N, M) ->
end.
+heavy_concurrent(_Config) ->
+ repeat_for_opts(do_heavy_concurrent).
+
+do_heavy_concurrent(Opts) ->
+ ?line Size = 10000,
+ ?line EtsMem = etsmem(),
+ ?line Tab = ets_new(blupp, [set, public, {keypos, 2} | Opts]),
+ ?line ok = fill_tab2(Tab, 0, Size),
+ ?line Procs = lists:map(
+ fun (N) ->
+ spawn_link(
+ fun () ->
+ do_heavy_concurrent_proc(Tab, Size, N)
+ end)
+ end,
+ lists:seq(1, 500)),
+ ?line lists:foreach(fun (P) ->
+ M = erlang:monitor(process, P),
+ receive
+ {'DOWN', M, process, P, _} ->
+ ok
+ end
+ end,
+ Procs),
+ ?line true = ets:delete(Tab),
+ ?line verify_etsmem(EtsMem).
+
+do_heavy_concurrent_proc(_Tab, 0, _Offs) ->
+ done;
+do_heavy_concurrent_proc(Tab, N, Offs) when (N+Offs) rem 100 == 0 ->
+ Data = {"here", are, "S O M E ", data, "toooooooooooooooooo", insert,
+ make_ref(), make_ref(), make_ref()},
+ true=ets:insert(Tab, {{self(),Data}, N}),
+ do_heavy_concurrent_proc(Tab, N-1, Offs);
+do_heavy_concurrent_proc(Tab, N, Offs) ->
+ _ = ets:lookup(Tab, N),
+ do_heavy_concurrent_proc(Tab, N-1, Offs).
+
fold(suite) -> [foldl_ordered, foldr_ordered,
foldl, foldr,
fold_empty].
@@ -4012,7 +4164,7 @@ member(Config) when is_list(Config) ->
member_do(Opts) ->
?line EtsMem = etsmem(),
- ?line T = ets:new(xxx, Opts),
+ ?line T = ets_new(xxx, Opts),
?line false = ets:member(T,hej),
?line E = fun(0,_F)->ok;
(N,F) ->
@@ -4037,7 +4189,7 @@ member_do(Opts) ->
build_table(L1,L2,Num) ->
- T = ets:new(xxx, [ordered_set]
+ T = ets_new(xxx, [ordered_set]
),
lists:foreach(
fun(X1) ->
@@ -4059,7 +4211,7 @@ build_table(L1,L2,Num) ->
T.
build_table2(L1,L2,Num) ->
- T = ets:new(xxx, [ordered_set]
+ T = ets_new(xxx, [ordered_set]
),
lists:foreach(
fun(X1) ->
@@ -4190,7 +4342,7 @@ do_n_times(Fun,N) ->
do_n_times(Fun,N-1).
make_table(Name, Options, Elements) ->
- T = ets:new(Name, Options),
+ T = ets_new(Name, Options),
lists:foreach(fun(E) -> ets:insert(T, E) end, Elements),
T.
filltabint(Tab,0) ->
@@ -4254,13 +4406,13 @@ xfilltabstr(Tab,N) ->
fill_sets_int(N) ->
fill_sets_int(N,[]).
fill_sets_int(N,Opts) ->
- Tab1 = ets:new(xxx, [ordered_set|Opts]),
+ Tab1 = ets_new(xxx, [ordered_set|Opts]),
filltabint(Tab1,N),
- Tab2 = ets:new(xxx, [set|Opts]),
+ Tab2 = ets_new(xxx, [set|Opts]),
filltabint(Tab2,N),
- Tab3 = ets:new(xxx, [bag|Opts]),
+ Tab3 = ets_new(xxx, [bag|Opts]),
filltabint2(Tab3,N),
- Tab4 = ets:new(xxx, [duplicate_bag|Opts]),
+ Tab4 = ets_new(xxx, [duplicate_bag|Opts]),
filltabint3(Tab4,N),
[Tab1,Tab2,Tab3,Tab4].
@@ -4412,7 +4564,7 @@ gen_dets_filename(Config,N) ->
"testdets_" ++ integer_to_list(N) ++ ".dets").
otp_6842_select_1000(Config) when is_list(Config) ->
- ?line Tab = ets:new(xxx,[ordered_set]),
+ ?line Tab = ets_new(xxx,[ordered_set]),
?line [ets:insert(Tab,{X,X}) || X <- lists:seq(1,10000)],
?line AllTrue = lists:duplicate(10,true),
?line AllTrue =
@@ -4445,7 +4597,7 @@ check_seq(A,B,C) ->
otp_6338(Config) when is_list(Config) ->
L = binary_to_term(<<131,108,0,0,0,2,104,2,108,0,0,0,2,103,100,0,19,112,112,98,49,95,98,115,49,50,64,98,108,97,100,101,95,48,95,53,0,0,33,50,0,0,0,4,1,98,0,0,23,226,106,100,0,4,101,120,105,116,104,2,108,0,0,0,2,104,2,100,0,3,115,98,109,100,0,19,112,112,98,50,95,98,115,49,50,64,98,108,97,100,101,95,48,95,56,98,0,0,18,231,106,100,0,4,114,101,99,118,106>>),
- T = ets:new(xxx,[ordered_set]),
+ T = ets_new(xxx,[ordered_set]),
lists:foreach(fun(X) -> ets:insert(T,X) end,L),
[[4839,recv]] = ets:match(T,{[{sbm,ppb2_bs12@blade_0_8},'$1'],'$2'}),
ets:delete(T).
@@ -4456,7 +4608,7 @@ otp_5340(Config) when is_list(Config) ->
otp_5340_do(Opts) ->
N = 3000,
- T = ets:new(otp_5340, [bag,public | Opts]),
+ T = ets_new(otp_5340, [bag,public | Opts]),
Ids = [1,2,3,4,5],
[w(T, N, Id) || Id <- Ids],
verify(T, Ids),
@@ -4492,7 +4644,7 @@ otp_7665(Config) when is_list(Config) ->
repeat_for_opts(otp_7665_do).
otp_7665_do(Opts) ->
- Tab = ets:new(otp_7665,[bag | Opts]),
+ Tab = ets_new(otp_7665,[bag | Opts]),
Min = 0,
Max = 10,
lists:foreach(fun(N)-> otp_7665_act(Tab,Min,Max,N) end,
@@ -4555,7 +4707,7 @@ meta_wb_do(Opts) ->
Names).
meta_wb_new(Name, _, Tabs, Opts) ->
- case (catch ets:new(Name,[named_table|Opts])) of
+ case (catch ets_new(Name,[named_table|Opts])) of
Name ->
?line false = lists:member(Name, Tabs),
[Name | Tabs];
@@ -4603,7 +4755,7 @@ grow_shrink_0([N|Ns], EtsMem) ->
grow_shrink_0([], _) -> ok.
grow_shrink_1(N, Flags) ->
- ?line T = ets:new(a, Flags),
+ ?line T = ets_new(a, Flags),
?line grow_shrink_2(N, N, T),
?line ets:delete(T).
@@ -4633,7 +4785,7 @@ grow_pseudo_deleted_do() ->
grow_pseudo_deleted_do(Type) ->
process_flag(scheduler,1),
Self = self(),
- ?line T = ets:new(kalle,[Type,public,{write_concurrency,true}]),
+ ?line T = ets_new(kalle,[Type,public,{write_concurrency,true}]),
Mod = 7, Mult = 10000,
filltabint(T,Mod*Mult),
?line true = ets:safe_fixtable(T,true),
@@ -4675,7 +4827,7 @@ shrink_pseudo_deleted_do() ->
shrink_pseudo_deleted_do(Type) ->
process_flag(scheduler,1),
Self = self(),
- ?line T = ets:new(kalle,[Type,public,{write_concurrency,true}]),
+ ?line T = ets_new(kalle,[Type,public,{write_concurrency,true}]),
Half = 10000,
filltabint(T,Half*2),
?line true = ets:safe_fixtable(T,true),
@@ -4714,7 +4866,7 @@ meta_smp(suite) ->
meta_lookup_unnamed_read(suite) -> [];
meta_lookup_unnamed_read(Config) when is_list(Config) ->
- InitF = fun(_) -> Tab = ets:new(unnamed,[]),
+ InitF = fun(_) -> Tab = ets_new(unnamed,[]),
true = ets:insert(Tab,{key,data}),
Tab
end,
@@ -4727,7 +4879,7 @@ meta_lookup_unnamed_read(Config) when is_list(Config) ->
meta_lookup_unnamed_write(suite) -> [];
meta_lookup_unnamed_write(Config) when is_list(Config) ->
- InitF = fun(_) -> Tab = ets:new(unnamed,[]),
+ InitF = fun(_) -> Tab = ets_new(unnamed,[]),
{Tab,0}
end,
ExecF = fun({Tab,N}) -> true = ets:insert(Tab,{key,N}),
@@ -4740,7 +4892,7 @@ meta_lookup_unnamed_write(Config) when is_list(Config) ->
meta_lookup_named_read(suite) -> [];
meta_lookup_named_read(Config) when is_list(Config) ->
InitF = fun([ProcN|_]) -> Name = list_to_atom(integer_to_list(ProcN)),
- Tab = ets:new(Name,[named_table]),
+ Tab = ets_new(Name,[named_table]),
true = ets:insert(Tab,{key,data}),
Tab
end,
@@ -4754,7 +4906,7 @@ meta_lookup_named_read(Config) when is_list(Config) ->
meta_lookup_named_write(suite) -> [];
meta_lookup_named_write(Config) when is_list(Config) ->
InitF = fun([ProcN|_]) -> Name = list_to_atom(integer_to_list(ProcN)),
- Tab = ets:new(Name,[named_table]),
+ Tab = ets_new(Name,[named_table]),
{Tab,0}
end,
ExecF = fun({Tab,N}) -> true = ets:insert(Tab,{key,N}),
@@ -4767,7 +4919,7 @@ meta_lookup_named_write(Config) when is_list(Config) ->
meta_newdel_unnamed(suite) -> [];
meta_newdel_unnamed(Config) when is_list(Config) ->
InitF = fun(_) -> ok end,
- ExecF = fun(_) -> Tab = ets:new(unnamed,[]),
+ ExecF = fun(_) -> Tab = ets_new(unnamed,[]),
true = ets:delete(Tab)
end,
FiniF = fun(_) -> ok end,
@@ -4777,7 +4929,7 @@ meta_newdel_named(suite) -> [];
meta_newdel_named(Config) when is_list(Config) ->
InitF = fun([ProcN|_]) -> list_to_atom(integer_to_list(ProcN))
end,
- ExecF = fun(Name) -> Name = ets:new(Name,[named_table]),
+ ExecF = fun(Name) -> Name = ets_new(Name,[named_table]),
true = ets:delete(Name),
Name
end,
@@ -4787,7 +4939,7 @@ meta_newdel_named(Config) when is_list(Config) ->
smp_insert(doc) -> ["Concurrent insert's on same table"];
smp_insert(suite) -> [];
smp_insert(Config) when is_list(Config) ->
- ets:new(smp_insert,[named_table,public,{write_concurrency,true}]),
+ ets_new(smp_insert,[named_table,public,{write_concurrency,true}]),
InitF = fun(_) -> ok end,
ExecF = fun(_) -> true = ets:insert(smp_insert,{random:uniform(10000)})
end,
@@ -4802,7 +4954,7 @@ smp_fixed_delete(Config) when is_list(Config) ->
only_if_smp(fun()->smp_fixed_delete_do() end).
smp_fixed_delete_do() ->
- T = ets:new(foo,[public,{write_concurrency,true}]),
+ T = ets_new(foo,[public,{write_concurrency,true}]),
%%Mem = ets:info(T,memory),
NumOfObjs = 100000,
filltabint(T,NumOfObjs),
@@ -4838,7 +4990,7 @@ smp_unfix_fix(Config) when is_list(Config) ->
smp_unfix_fix_do() ->
process_flag(scheduler,1),
Parent = self(),
- T = ets:new(foo,[public,{write_concurrency,true}]),
+ T = ets_new(foo,[public,{write_concurrency,true}]),
%%Mem = ets:info(T,memory),
NumOfObjs = 100000,
Deleted = 50000,
@@ -4898,7 +5050,7 @@ otp_8166_do(WC) ->
%% Bug scenario: One process segv while reading the table because another
%% process is doing unfix without write-lock at the end of a trapping match_object.
process_flag(scheduler,1),
- T = ets:new(foo,[public, {write_concurrency,WC}]),
+ T = ets_new(foo,[public, {write_concurrency,WC}]),
NumOfObjs = 3000, %% Need more than 1000 live objects for match_object to trap one time
Deleted = NumOfObjs div 2,
filltabint(T,NumOfObjs),
@@ -5010,14 +5162,20 @@ verify_table_load(T) ->
end.
-
-
+otp_8732(doc) -> ["ets:select on a tree with NIL key object"];
+otp_8732(Config) when is_list(Config) ->
+ Tab = ets_new(noname,[ordered_set]),
+ filltabstr(Tab,999),
+ ets:insert(Tab,{[],"nasty NIL object"}),
+ ?line [] = ets:match(Tab,{'_',nomatch}), %% Will hang if bug not fixed
+ ok.
+
smp_select_delete(suite) -> [];
smp_select_delete(doc) ->
["Run concurrent select_delete (and inserts) on same table."];
smp_select_delete(Config) when is_list(Config) ->
- T = ets:new(smp_select_delete,[named_table,public,{write_concurrency,true}]),
+ T = ets_new(smp_select_delete,[named_table,public,{write_concurrency,true}]),
Mod = 17,
Zeros = erlang:make_tuple(Mod,0),
InitF = fun(_) -> Zeros end,
@@ -5070,6 +5228,39 @@ smp_select_delete(Config) when is_list(Config) ->
?line false = ets:info(T,fixed),
ets:delete(T).
+types(doc) -> ["Test different types"];
+types(Config) when is_list(Config) ->
+ init_externals(),
+ repeat_for_opts(types_do,[[set,ordered_set],compressed]).
+
+types_do(Opts) ->
+ EtsMem = etsmem(),
+ ?line T = ets_new(xxx,Opts),
+ Fun = fun(Term) ->
+ ets:insert(T,{Term}),
+ ?line [{Term}] = ets:lookup(T,Term),
+ ets:insert(T,{Term,xxx}),
+ ?line [{Term,xxx}] = ets:lookup(T,Term),
+ ets:insert(T,{Term,"xxx"}),
+ ?line [{Term,"xxx"}] = ets:lookup(T,Term),
+ ets:insert(T,{xxx,Term}),
+ ?line [{xxx,Term}] = ets:lookup(T,xxx),
+ ets:insert(T,{"xxx",Term}),
+ ?line [{"xxx",Term}] = ets:lookup(T,"xxx"),
+ ets:delete_all_objects(T),
+ ?line 0 = ets:info(T,size)
+ end,
+ test_terms(Fun),
+ ets:delete(T),
+ ?line verify_etsmem(EtsMem).
+
+
+
+
+%
+% Utility functions:
+%
+
add_lists(L1,L2) ->
add_lists(L1,L2,[]).
add_lists([],[],Acc) ->
@@ -5135,6 +5326,10 @@ my_tab_to_list(Ts,Key, Acc) ->
my_tab_to_list(Ts,ets:next(Ts,Key),[ets:lookup(Ts, Key)| Acc]).
etsmem() ->
+ 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()),
+ Mem =
{try erlang:memory(ets) catch error:notsup -> notsup end,
case erlang:system_info({allocator,ets_alloc}) of
false -> undefined;
@@ -5153,12 +5348,13 @@ etsmem() ->
{value,{_,BlSz,_,_}} = lists:keysearch(blocks_size, 1, L),
{Bl0+Bl,BlSz0+BlSz}
end, {0,0}, MSBCS)
- end}.
+ end},
+ {Mem,AllTabs}.
-verify_etsmem(MemInfo) ->
+verify_etsmem({MemInfo,AllTabs}) ->
wait_for_test_procs(),
case etsmem() of
- MemInfo ->
+ {MemInfo,_} ->
io:format("Ets mem info: ~p", [MemInfo]),
case MemInfo of
{ErlMem,EtsAlloc} when ErlMem == notsup; EtsAlloc == undefined ->
@@ -5167,12 +5363,15 @@ verify_etsmem(MemInfo) ->
_ ->
ok
end;
- Other ->
+ {MemInfo2, AllTabs2} ->
io:format("Expected: ~p", [MemInfo]),
- io:format("Actual: ~p", [Other]),
+ io:format("Actual: ~p", [MemInfo2]),
+ io:format("Changed tables before: ~p\n",[AllTabs -- AllTabs2]),
+ io:format("Changed tables after: ~p\n", [AllTabs2 -- AllTabs]),
?t:fail()
end.
+
start_loopers(N, Prio, Fun, State) ->
lists:map(fun (_) ->
my_spawn_opt(fun () -> looper(Fun, State) end,
@@ -5332,22 +5531,220 @@ only_if_smp(Schedulers, Func) ->
{true,_} -> Func()
end.
+%% Copy-paste from emulator/test/binary_SUITE.erl
+-define(heap_binary_size, 64).
+test_terms(Test_Func) ->
+ garbage_collect(),
+ ?line Pib0 = process_info(self(),binary),
+
+ ?line Test_Func(atom),
+ ?line Test_Func(''),
+ ?line Test_Func('a'),
+ ?line Test_Func('ab'),
+ ?line Test_Func('abc'),
+ ?line Test_Func('abcd'),
+ ?line Test_Func('abcde'),
+ ?line Test_Func('abcdef'),
+ ?line Test_Func('abcdefg'),
+ ?line Test_Func('abcdefgh'),
+
+ ?line Test_Func(fun() -> ok end),
+ X = id([a,{b,c},c]),
+ Y = id({x,y,z}),
+ Z = id(1 bsl 8*257),
+ ?line Test_Func(fun() -> X end),
+ ?line Test_Func(fun() -> {X,Y} end),
+ ?line Test_Func([fun() -> {X,Y,Z} end,
+ fun() -> {Z,X,Y} end,
+ fun() -> {Y,Z,X} end]),
+
+ ?line Test_Func({trace_ts,{even_bigger,{some_data,fun() -> ok end}},{1,2,3}}),
+ ?line Test_Func({trace_ts,{even_bigger,{some_data,<<1,2,3,4,5,6,7,8,9,10>>}},
+ {1,2,3}}),
+
+ ?line Test_Func(1),
+ ?line Test_Func(42),
+ ?line Test_Func(-23),
+ ?line Test_Func(256),
+ ?line Test_Func(25555),
+ ?line Test_Func(-3333),
+
+ ?line Test_Func(1.0),
+
+ ?line Test_Func(183749783987483978498378478393874),
+ ?line Test_Func(-37894183749783987483978498378478393874),
+ Very_Big = very_big_num(),
+ ?line Test_Func(Very_Big),
+ ?line Test_Func(-Very_Big+1),
+
+ ?line Test_Func([]),
+ ?line Test_Func("abcdef"),
+ ?line Test_Func([a, b, 1, 2]),
+ ?line Test_Func([a|b]),
+
+ ?line Test_Func({}),
+ ?line Test_Func({1}),
+ ?line Test_Func({a, b}),
+ ?line Test_Func({a, b, c}),
+ ?line Test_Func(list_to_tuple(lists:seq(0, 255))),
+ ?line Test_Func(list_to_tuple(lists:seq(0, 256))),
+
+ ?line Test_Func(make_ref()),
+ ?line Test_Func([make_ref(), make_ref()]),
+
+ ?line Test_Func(make_port()),
+
+ ?line Test_Func(make_pid()),
+ ?line Test_Func(make_ext_pid()),
+ ?line Test_Func(make_ext_port()),
+ ?line Test_Func(make_ext_ref()),
+
+ Bin0 = list_to_binary(lists:seq(0, 14)),
+ ?line Test_Func(Bin0),
+ Bin1 = list_to_binary(lists:seq(0, ?heap_binary_size)),
+ ?line Test_Func(Bin1),
+ Bin2 = list_to_binary(lists:seq(0, ?heap_binary_size+1)),
+ ?line Test_Func(Bin2),
+ Bin3 = list_to_binary(lists:seq(0, 255)),
+ garbage_collect(),
+ Pib = process_info(self(),binary),
+ ?line Test_Func(Bin3),
+ garbage_collect(),
+ ?line Pib = process_info(self(),binary),
+
+ ?line Test_Func(make_unaligned_sub_binary(Bin0)),
+ ?line Test_Func(make_unaligned_sub_binary(Bin1)),
+ ?line Test_Func(make_unaligned_sub_binary(Bin2)),
+ ?line Test_Func(make_unaligned_sub_binary(Bin3)),
+
+ ?line Test_Func(make_sub_binary(lists:seq(42, 43))),
+ ?line Test_Func(make_sub_binary([42,43,44])),
+ ?line Test_Func(make_sub_binary([42,43,44,45])),
+ ?line Test_Func(make_sub_binary([42,43,44,45,46])),
+ ?line Test_Func(make_sub_binary([42,43,44,45,46,47])),
+ ?line Test_Func(make_sub_binary([42,43,44,45,46,47,48])),
+ ?line Test_Func(make_sub_binary(lists:seq(42, 49))),
+ ?line Test_Func(make_sub_binary(lists:seq(0, 14))),
+ ?line Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size))),
+ ?line Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size+1))),
+ ?line Test_Func(make_sub_binary(lists:seq(0, 255))),
+
+ ?line Test_Func(make_unaligned_sub_binary(lists:seq(42, 43))),
+ ?line Test_Func(make_unaligned_sub_binary([42,43,44])),
+ ?line Test_Func(make_unaligned_sub_binary([42,43,44,45])),
+ ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46])),
+ ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47])),
+ ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47,48])),
+ ?line Test_Func(make_unaligned_sub_binary(lists:seq(42, 49))),
+ ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, 14))),
+ ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size))),
+ ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size+1))),
+ ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, 255))),
+
+ %% Bit level binaries.
+ ?line Test_Func(<<1:1>>),
+ ?line Test_Func(<<2:2>>),
+ ?line Test_Func(<<42:10>>),
+ ?line Test_Func(list_to_bitstring([<<5:6>>|lists:seq(0, 255)])),
+
+ ?line Test_Func(F = fun(A) -> 42*A end),
+ ?line Test_Func(lists:duplicate(32, F)),
+
+ ?line Test_Func(FF = fun binary_SUITE:all/1),
+ ?line Test_Func(lists:duplicate(32, FF)),
+
+ garbage_collect(),
+ ?line Pib0 = process_info(self(),binary),
+ ok.
+
+id(I) -> I.
+
+very_big_num() ->
+ very_big_num(33, 1).
+
+very_big_num(Left, Result) when Left > 0 ->
+ ?line very_big_num(Left-1, Result*256);
+very_big_num(0, Result) ->
+ ?line Result.
+
+make_port() ->
+ ?line open_port({spawn, efile}, [eof]).
+
+make_pid() ->
+ ?line spawn_link(?MODULE, sleeper, []).
+
+sleeper() ->
+ ?line receive after infinity -> ok end.
+
+make_ext_pid() ->
+ {Pid, _, _} = get(externals),
+ Pid.
+
+make_ext_port() ->
+ {_, Port, _} = get(externals),
+ Port.
+make_ext_ref() ->
+ {_, _, Ref} = get(externals),
+ Ref.
+
+init_externals() ->
+ SysDistSz = ets:info(sys_dist,size),
+ ?line Pa = filename:dirname(code:which(?MODULE)),
+ ?line {ok, Node} = test_server:start_node(plopp, slave, [{args, " -pa " ++ Pa}]),
+ ?line Res = case rpc:call(Node, ?MODULE, rpc_externals, []) of
+ {badrpc, {'EXIT', E}} ->
+ test_server:fail({rpcresult, E});
+ R -> R
+ end,
+ ?line test_server:stop_node(Node),
+
+ %% Wait for table 'sys_dist' to stabilize
+ repeat_while(fun() ->
+ case ets:info(sys_dist,size) of
+ SysDistSz -> false;
+ Sz ->
+ io:format("Waiting for sys_dist to revert size from ~p to size ~p\n",
+ [Sz, SysDistSz]),
+ receive after 1000 -> true end
+ end
+ end),
+ put(externals, Res).
+
+rpc_externals() ->
+ {self(), make_port(), make_ref()}.
+
+make_sub_binary(Bin) when is_binary(Bin) ->
+ {_,B} = split_binary(list_to_binary([0,1,3,Bin]), 3),
+ B;
+make_sub_binary(List) ->
+ make_sub_binary(list_to_binary(List)).
+
+make_unaligned_sub_binary(Bin0) when is_binary(Bin0) ->
+ Bin1 = <<0:3,Bin0/binary,31:5>>,
+ Sz = size(Bin0),
+ <<0:3,Bin:Sz/binary,31:5>> = id(Bin1),
+ Bin;
+make_unaligned_sub_binary(List) ->
+ make_unaligned_sub_binary(list_to_binary(List)).
%% Repeat test function with different combination of table options
%%
repeat_for_opts(F) ->
- repeat_for_opts(F, [write_concurrency]).
+ repeat_for_opts(F, [write_concurrency, read_concurrency, compressed]).
repeat_for_opts(F, OptGenList) when is_atom(F) ->
repeat_for_opts(fun(Opts) -> ?MODULE:F(Opts) end, OptGenList);
+repeat_for_opts({F,Args}, OptGenList) when is_atom(F) ->
+ repeat_for_opts(fun(Opts) -> ?MODULE:F(Opts,Args) end, OptGenList);
repeat_for_opts(F, OptGenList) ->
repeat_for_opts(F, OptGenList, []).
repeat_for_opts(F, [], Acc) ->
- lists:map(fun(Opts) ->
- io:format("Calling with options ~p\n",[Opts]),
- F(Opts)
- end, Acc);
+ lists:map(fun(Opts) ->
+ OptList = lists:filter(fun(E) -> E =/= void end, Opts),
+ io:format("Calling with options ~p\n",[OptList]),
+ F(OptList)
+ end, Acc);
repeat_for_opts(F, [OptList | Tail], []) when is_list(OptList) ->
repeat_for_opts(F, Tail, [[Opt] || Opt <- OptList]);
repeat_for_opts(F, [OptList | Tail], AccList) when is_list(OptList) ->
@@ -5356,6 +5753,10 @@ repeat_for_opts(F, [Atom | Tail], AccList) when is_atom(Atom) ->
repeat_for_opts(F, [repeat_for_opts_atom2list(Atom) | Tail ], AccList).
repeat_for_opts_atom2list(all_types) -> [set,ordered_set,bag,duplicate_bag];
-repeat_for_opts_atom2list(write_concurrency) -> [{write_concurrency,false},{write_concurrency,true}].
-
+repeat_for_opts_atom2list(write_concurrency) -> [{write_concurrency,false},{write_concurrency,true}];
+repeat_for_opts_atom2list(read_concurrency) -> [{read_concurrency,false},{read_concurrency,true}];
+repeat_for_opts_atom2list(compressed) -> [compressed,void].
+ets_new(Name, Opts) ->
+ %%ets:new(Name, [compressed | Opts]).
+ ets:new(Name, Opts).
diff --git a/lib/stdlib/test/filelib_SUITE.erl b/lib/stdlib/test/filelib_SUITE.erl
index d54741051f..5a279609c6 100644
--- a/lib/stdlib/test/filelib_SUITE.erl
+++ b/lib/stdlib/test/filelib_SUITE.erl
@@ -53,8 +53,11 @@ wildcard_one(Config) when is_list(Config) ->
wildcard_two(Config) when is_list(Config) ->
?line Dir = filename:join(?config(priv_dir, Config), "wildcard_two"),
+ ?line DirB = unicode:characters_to_binary(Dir, file:native_name_encoding()),
?line ok = file:make_dir(Dir),
- ?line do_wildcard_1(Dir, fun(Wc) -> filelib:wildcard(Wc, Dir) end),
+ ?line do_wildcard_1(Dir, fun(Wc) -> io:format("~p~n",[{Wc,Dir, X = filelib:wildcard(Wc, Dir)}]),X end),
+ ?line do_wildcard_1(Dir, fun(Wc) -> io:format("~p~n",[{Wc,DirB, X = filelib:wildcard(Wc, DirB)}]),
+ [unicode:characters_to_list(Y,file:native_name_encoding()) || Y <- X] end),
?line do_wildcard_1(Dir, fun(Wc) -> filelib:wildcard(Wc, Dir++"/") end),
case os:type() of
{win32,_} ->
@@ -253,5 +256,7 @@ ensure_dir_eexist(Config) when is_list(Config) ->
%% There already is a file with the name of the directory
%% we want to create.
?line NeedFile = filename:join(Name, "file"),
+ ?line NeedFileB = filename:join(Name, <<"file">>),
?line {error, eexist} = filelib:ensure_dir(NeedFile),
+ ?line {error, eexist} = filelib:ensure_dir(NeedFileB),
ok.
diff --git a/lib/stdlib/test/filename_SUITE.erl b/lib/stdlib/test/filename_SUITE.erl
index ab6521f37b..dbce93600b 100644
--- a/lib/stdlib/test/filename_SUITE.erl
+++ b/lib/stdlib/test/filename_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,12 +22,19 @@
basename_1/1, basename_2/1,
dirname/1, extension/1, join/1, t_nativename/1]).
-export([pathtype/1,rootname/1,split/1,find_src/1]).
+-export([absname_bin/1, absname_bin_2/1,
+ basename_bin_1/1, basename_bin_2/1,
+ dirname_bin/1, extension_bin/1, join_bin/1]).
+-export([pathtype_bin/1,rootname_bin/1,split_bin/1]).
-include("test_server.hrl").
all(suite) ->
[absname, absname_2, basename_1, basename_2, dirname,
extension,
- join, pathtype, rootname, split, t_nativename, find_src].
+ join, pathtype, rootname, split, t_nativename, find_src,
+ absname_bin, absname_bin_2, basename_bin_1, basename_bin_2, dirname_bin,
+ extension_bin,
+ join_bin, pathtype_bin, rootname_bin, split_bin].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -457,3 +464,307 @@ find_src(Config) when is_list(Config) ->
%% Try to find the source for a preloaded module.
?line {error,{preloaded,init}} = filename:find_src(init),
ok.
+
+%%
+%%
+%% With binaries
+%%
+%%
+
+absname_bin(Config) when is_list(Config) ->
+ case os:type() of
+ {win32, _} ->
+ ?line [Drive|_] = ?config(priv_dir, Config),
+ ?line Temp = filename:join([Drive|":/"], "temp"),
+ ?line case file:make_dir(Temp) of
+ ok -> ok;
+ {error,eexist} -> ok
+ end,
+ ?line {ok,Cwd} = file:get_cwd(),
+ ?line ok = file:set_cwd(Temp),
+ ?line <<Drive:8,":/temp/foo">> = filename:absname(<<"foo">>),
+ ?line <<Drive:8,":/temp/../ebin">> = filename:absname(<<"../ebin">>),
+ ?line <<Drive:8,":/erlang">> = filename:absname(<<"/erlang">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"/erlang/src">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"\\erlang\\src">>),
+ ?line <<Drive:8,":/temp/erlang">> = filename:absname(<<Drive:8,":erlang">>),
+ ?line <<Drive:8,":/temp/erlang/src">> =
+ filename:absname(<<Drive:8,":erlang/src">>),
+ ?line <<Drive:8,":/temp/erlang/src">> =
+ filename:absname(<<Drive:8,":erlang\\src\\">>),
+ ?line <<"a:/erlang">> = filename:absname(<<"a:erlang">>),
+
+ ?line file:set_cwd(<<Drive:8,":/">>),
+ ?line <<Drive:8,":/foo">> = filename:absname(<<"foo">>),
+ ?line <<Drive:8,":/../ebin">> = filename:absname(<<"../ebin">>),
+ ?line <<Drive:8,":/erlang">> = filename:absname(<<"/erlang">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"/erlang/src">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"\\erlang\\\\src">>),
+ ?line <<Drive:8,":/erlang">> = filename:absname(<<Drive:8,":erlang">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<Drive:8,":erlang/src">>),
+ ?line <<"a:/erlang">> = filename:absname(<<"a:erlang">>),
+
+ ?line file:set_cwd(Cwd),
+ ok;
+ {unix, _} ->
+ ?line ok = file:set_cwd(<<"/usr">>),
+ ?line <<"/usr/foo">> = filename:absname(<<"foo">>),
+ ?line <<"/usr/../ebin">> = filename:absname(<<"../ebin">>),
+
+ ?line file:set_cwd(<<"/">>),
+ ?line <<"/foo">> = filename:absname(<<"foo">>),
+ ?line <<"/../ebin">> = filename:absname(<<"../ebin">>),
+ ?line <<"/erlang">> = filename:absname(<<"/erlang">>),
+ ?line <<"/erlang/src">> = filename:absname(<<"/erlang/src">>),
+ ?line <<"/erlang/src">> = filename:absname(<<"/erlang///src">>),
+ ok
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+absname_bin_2(Config) when is_list(Config) ->
+ case os:type() of
+ {win32, _} ->
+ ?line [Drive|_] = ?config(priv_dir, Config),
+ ?line <<Drive:8,":/temp/foo">> = filename:absname(<<"foo">>, <<Drive:8,":/temp">>),
+ ?line <<Drive:8,":/temp/../ebin">> = filename:absname(<<"../ebin">>,
+ <<Drive:8,":/temp">>),
+ ?line <<Drive:8,":/erlang">> = filename:absname(<<"/erlang">>, <<Drive:8,":/temp">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"/erlang/src">>,
+ <<Drive:8,":/temp">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"\\erlang\\src">>,
+ <<Drive:8,":/temp">>),
+ ?line <<Drive:8,":/temp/erlang">> = filename:absname(<<Drive:8,":erlang">>,
+ <<Drive:8,":/temp">>),
+ ?line <<Drive:8,":/temp/erlang/src">> = filename:absname(<<Drive:8,":erlang/src">>,
+ <<Drive:8,":/temp">>),
+ ?line <<Drive:8,":/temp/erlang/src">> =
+ filename:absname(<<Drive:8,":erlang\\src\\">>, <<Drive:8,":/temp">>),
+ ?line <<"a:/erlang">> = filename:absname(<<"a:erlang">>, <<Drive:8,":/temp">>),
+
+ ?line file:set_cwd(<<Drive:8,":/">>),
+ ?line <<Drive:8,":/foo">> = filename:absname(foo, <<Drive:8,":/">>),
+ ?line <<Drive:8,":/foo">> = filename:absname(<<"foo">>, <<Drive:8,":/">>),
+ ?line <<Drive:8,":/../ebin">> = filename:absname(<<"../ebin">>, <<Drive:8,":/">>),
+ ?line <<Drive:8,":/erlang">> = filename:absname(<<"/erlang">>, <<Drive:8,":/">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"/erlang/src">>,
+ <<Drive:8,":/">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"\\erlang\\\\src">>,
+ <<Drive:8,":/">>),
+ ?line <<Drive:8,":/erlang">> = filename:absname(<<Drive:8,":erlang">>,
+ <<Drive:8,":/">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<Drive:8,":erlang/src">>,
+ <<Drive:8,":/">>),
+ ?line <<"a:/erlang">> = filename:absname(<<"a:erlang">>, <<Drive:8,":/">>),
+
+ ok;
+ {unix, _} ->
+ ?line <<"/usr/foo">> = filename:absname(<<"foo">>, <<"/usr">>),
+ ?line <<"/usr/../ebin">> = filename:absname(<<"../ebin">>, <<"/usr">>),
+
+ ?line <<"/foo">> = filename:absname(<<"foo">>, <<"/">>),
+ ?line <<"/../ebin">> = filename:absname(<<"../ebin">>, <<"/">>),
+ ?line <<"/erlang">> = filename:absname(<<"/erlang">>, <<"/">>),
+ ?line <<"/erlang/src">> = filename:absname(<<"/erlang/src">>, <<"/">>),
+ ?line <<"/erlang/src">> = filename:absname(<<"/erlang///src">>, <<"/">>),
+ ok
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+basename_bin_1(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line <<".">> = filename:basename(<<".">>),
+ ?line <<"foo">> = filename:basename(<<"foo">>),
+ ?line <<"foo">> = filename:basename(<<"/usr/foo">>),
+ ?line <<"foo.erl">> = filename:basename(<<"A:usr/foo.erl">>),
+ ?line case os:type() of
+ {win32, _} ->
+ ?line <<"foo">> = filename:basename(<<"A:\\usr\\foo">>),
+ ?line <<"foo">> = filename:basename(<<"A:foo">>);
+ {unix, _} ->
+ ?line <<"strange\\but\\true">> =
+ filename:basename(<<"strange\\but\\true">>)
+ end,
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+basename_bin_2(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line <<".">> = filename:basename(<<".">>, <<".erl">>),
+ ?line <<"foo">> = filename:basename(<<"foo.erl">>, <<".erl">>),
+ ?line <<"foo.erl">> = filename:basename(<<"/usr/foo.erl">>, <<".hrl">>),
+ ?line <<"foo.erl">> = filename:basename(<<"/usr.hrl/foo.erl">>, <<".hrl">>),
+ ?line <<"foo">> = filename:basename(<<"/usr.hrl/foo">>, <<".hrl">>),
+ ?line <<"foo">> = filename:basename(<<"usr/foo/">>, <<".erl">>),
+ ?line <<"foo.erl">> = filename:basename(<<"usr/foo.erl/">>, <<".erl">>),
+ ?line case os:type() of
+ {win32, _} ->
+ ?line <<"foo">> = filename:basename(<<"A:foo">>, <<".erl">>),
+ ?line <<"foo.erl">> = filename:basename(<<"a:\\usr\\foo.erl">>,
+ <<".hrl">>),
+ ?line <<"foo.erl">> = filename:basename(<<"c:\\usr.hrl\\foo.erl">>,
+ <<".hrl">>),
+ ?line <<"foo">> = filename:basename(<<"A:\\usr\\foo">>, <<".hrl">>);
+ {unix, _} ->
+ ?line <<"strange\\but\\true">> =
+ filename:basename(<<"strange\\but\\true.erl">>, <<".erl">>),
+ ?line <<"strange\\but\\true">> =
+ filename:basename(<<"strange\\but\\true">>, <<".erl">>)
+ end,
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+dirname_bin(Config) when is_list(Config) ->
+ case os:type() of
+ {win32,_} ->
+ ?line <<"A:/usr">> = filename:dirname(<<"A:/usr/foo.erl">>),
+ ?line <<"A:usr">> = filename:dirname(<<"A:usr/foo.erl">>),
+ ?line <<"/usr">> = filename:dirname(<<"\\usr\\foo.erl">>),
+ ?line <<"/">> = filename:dirname(<<"\\usr">>),
+ ?line <<"A:">> = filename:dirname(<<"A:">>);
+ vxworks ->
+ ?line <<"net:/usr">> = filename:dirname(<<"net:/usr/foo.erl">>),
+ ?line <<"/disk0:/usr">> = filename:dirname(<<"/disk0:/usr/foo.erl">>),
+ ?line <<"/usr">> = filename:dirname(<<"\\usr\\foo.erl">>),
+ ?line <<"/usr">> = filename:dirname(<<"\\usr">>),
+ ?line <<"net:">> = filename:dirname(<<"net:">>);
+ _ -> true
+ end,
+ ?line <<"usr">> = filename:dirname(<<"usr///foo.erl">>),
+ ?line <<".">> = filename:dirname(<<"foo.erl">>),
+ ?line <<".">> = filename:dirname(<<".">>),
+ case os:type() of
+ vxworks ->
+ ?line <<"/">> = filename:dirname(<<"/">>),
+ ?line <<"/usr">> = filename:dirname(<<"/usr">>);
+ _ ->
+ ?line <<"/">> = filename:dirname(<<"/">>),
+ ?line <<"/">> = filename:dirname(<<"/usr">>)
+ end,
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+extension_bin(Config) when is_list(Config) ->
+ ?line <<".erl">> = filename:extension(<<"A:/usr/foo.erl">>),
+ ?line <<".erl">> = filename:extension(<<"A:/usr/foo.nisse.erl">>),
+ ?line <<".erl">> = filename:extension(<<"A:/usr.bar/foo.nisse.erl">>),
+ ?line <<"">> = filename:extension(<<"A:/usr.bar/foo">>),
+ ?line <<"">> = filename:extension(<<"A:/usr/foo">>),
+ ?line case os:type() of
+ {win32, _} ->
+ ?line <<"">> = filename:extension(<<"A:\\usr\\foo">>),
+ ?line <<".erl">> =
+ filename:extension(<<"A:/usr.bar/foo.nisse.erl">>),
+ ?line <<"">> = filename:extension(<<"A:/usr.bar/foo">>),
+ ok;
+ vxworks ->
+ ?line <<"">> = filename:extension(<<"/disk0:\\usr\\foo">>),
+ ?line <<".erl">> =
+ filename:extension(<<"net:/usr.bar/foo.nisse.erl">>),
+ ?line <<"">> = filename:extension(<<"net:/usr.bar/foo">>),
+ ok;
+ _ -> ok
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+join_bin(Config) when is_list(Config) ->
+ ?line <<"/">> = filename:join([<<"/">>]),
+ ?line <<"/">> = filename:join([<<"//">>]),
+ ?line <<"usr/foo.erl">> = filename:join(<<"usr">>,<<"foo.erl">>),
+ ?line <<"/src/foo.erl">> = filename:join(usr, <<"/src/foo.erl">>),
+ ?line <<"/src/foo.erl">> = filename:join([<<"/src/">>,'foo.erl']),
+ ?line <<"/src/foo.erl">> = filename:join(<<"usr">>, ["/sr", 'c/foo.erl']),
+ ?line <<"/src/foo.erl">> = filename:join(<<"usr">>, <<"/src/foo.erl">>),
+
+ %% Make sure that redundant slashes work too.
+ ?line <<"a/b/c/d/e/f/g">> = filename:join([<<"a//b/c/////d//e/f/g">>]),
+ ?line <<"a/b/c/d/e/f/g">> = filename:join([<<"a//b/c/">>, <<"d//e/f/g">>]),
+ ?line <<"a/b/c/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"d//e/f/g">>]),
+ ?line <<"/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"/d//e/f/g">>]),
+ ?line <<"/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"//d//e/f/g">>]),
+
+ ?line <<"foo/bar">> = filename:join([$f,$o,$o,$/,[]], <<"bar">>),
+
+ ?line case os:type() of
+ {win32, _} ->
+ ?line <<"d:/">> = filename:join([<<"D:/">>]),
+ ?line <<"d:/">> = filename:join([<<"D:\\">>]),
+ ?line <<"d:/abc">> = filename:join([<<"D:/">>, <<"abc">>]),
+ ?line <<"d:abc">> = filename:join([<<"D:">>, <<"abc">>]),
+ ?line <<"a/b/c/d/e/f/g">> =
+ filename:join([<<"a//b\\c//\\/\\d/\\e/f\\g">>]),
+ ?line <<"a:usr/foo.erl">> =
+ filename:join([<<"A:">>,<<"usr">>,<<"foo.erl">>]),
+ ?line <<"/usr/foo.erl">> =
+ filename:join([<<"A:">>,<<"/usr">>,<<"foo.erl">>]),
+ ?line <<"c:usr">> = filename:join(<<"A:">>,<<"C:usr">>),
+ ?line <<"a:usr">> = filename:join(<<"A:">>,<<"usr">>),
+ ?line <<"c:/usr">> = filename:join(<<"A:">>, <<"C:/usr">>),
+ ?line <<"c:/usr/foo.erl">> =
+ filename:join([<<"A:">>,<<"C:/usr">>,<<"foo.erl">>]),
+ ?line <<"c:usr/foo.erl">> =
+ filename:join([<<"A:">>,<<"C:usr">>,<<"foo.erl">>]),
+ ?line <<"d:/foo">> = filename:join([$D, $:, $/, []], <<"foo">>),
+ ok;
+ {unix, _} ->
+ ok
+ end.
+
+pathtype_bin(Config) when is_list(Config) ->
+ ?line relative = filename:pathtype(<<"..">>),
+ ?line relative = filename:pathtype(<<"foo">>),
+ ?line relative = filename:pathtype(<<"foo/bar">>),
+ ?line relative = filename:pathtype('foo/bar'),
+ case os:type() of
+ {win32, _} ->
+ ?line volumerelative = filename:pathtype(<<"/usr/local/bin">>),
+ ?line volumerelative = filename:pathtype(<<"A:usr/local/bin">>),
+ ok;
+ {unix, _} ->
+ ?line absolute = filename:pathtype(<<"/">>),
+ ?line absolute = filename:pathtype(<<"/usr/local/bin">>),
+ ok
+ end.
+
+rootname_bin(Config) when is_list(Config) ->
+ ?line <<"/jam.src/kalle">> = filename:rootname(<<"/jam.src/kalle">>),
+ ?line <<"/jam.src/foo">> = filename:rootname(<<"/jam.src/foo.erl">>),
+ ?line <<"/jam.src/foo">> = filename:rootname(<<"/jam.src/foo.erl">>, <<".erl">>),
+ ?line <<"/jam.src/foo.jam">> = filename:rootname(<<"/jam.src/foo.jam">>, <<".erl">>),
+ ?line <<"/jam.src/foo.jam">> = filename:rootname(["/jam.sr",'c/foo.j',"am"],<<".erl">>),
+ ?line <<"/jam.src/foo.jam">> = filename:rootname(["/jam.sr",'c/foo.j'|am],<<".erl">>),
+ ok.
+
+split_bin(Config) when is_list(Config) ->
+ case os:type() of
+ vxworks ->
+ ?line [<<"/usr">>,<<"local">>,<<"bin">>] = filename:split(<<"/usr/local/bin">>);
+ _ ->
+ ?line [<<"/">>,<<"usr">>,<<"local">>,<<"bin">>] = filename:split(<<"/usr/local/bin">>)
+ end,
+ ?line [<<"foo">>,<<"bar">>]= filename:split(<<"foo/bar">>),
+ ?line [<<"foo">>, <<"bar">>, <<"hello">>]= filename:split(<<"foo////bar//hello">>),
+ case os:type() of
+ {win32,_} ->
+ ?line [<<"a:/">>,<<"msdev">>,<<"include">>] =
+ filename:split(<<"a:/msdev/include">>),
+ ?line [<<"a:/">>,<<"msdev">>,<<"include">>] =
+ filename:split(<<"A:/msdev/include">>),
+ ?line [<<"msdev">>,<<"include">>] =
+ filename:split(<<"msdev\\include">>),
+ ?line [<<"a:/">>,<<"msdev">>,<<"include">>] =
+ filename:split(<<"a:\\msdev\\include">>),
+ ?line [<<"a:">>,<<"msdev">>,<<"include">>] =
+ filename:split(<<"a:msdev\\include">>),
+ ok;
+ _ ->
+ ok
+ end.
+
diff --git a/lib/stdlib/test/gen_event_SUITE.erl b/lib/stdlib/test/gen_event_SUITE.erl
index 8cbffaca56..4f7de451e3 100644
--- a/lib/stdlib/test/gen_event_SUITE.erl
+++ b/lib/stdlib/test/gen_event_SUITE.erl
@@ -23,9 +23,11 @@
-export([all/1]).
-export([start/1, test_all/1, add_handler/1, add_sup_handler/1,
delete_handler/1, swap_handler/1, swap_sup_handler/1,
- notify/1, sync_notify/1, call/1, info/1, hibernate/1]).
+ notify/1, sync_notify/1, call/1, info/1, hibernate/1,
+ call_format_status/1, error_format_status/1]).
-all(suite) -> {req, [stdlib], [start, test_all, hibernate]}.
+all(suite) -> {req, [stdlib], [start, test_all, hibernate,
+ call_format_status, error_format_status]}.
%% --------------------------------------
%% Start an event manager.
@@ -844,3 +846,56 @@ info(Config) when is_list(Config) ->
?line ok = gen_event:stop(my_dummy_handler),
ok.
+
+call_format_status(suite) ->
+ [];
+call_format_status(doc) ->
+ ["Test that sys:get_status/1,2 calls format_status/2"];
+call_format_status(Config) when is_list(Config) ->
+ ?line {ok, Pid} = gen_event:start({local, my_dummy_handler}),
+ %% State here intentionally differs from what we expect from format_status
+ State = self(),
+ FmtState = "dummy1_h handler state",
+ ?line ok = gen_event:add_handler(my_dummy_handler, dummy1_h, [State]),
+ ?line Status1 = sys:get_status(Pid),
+ ?line Status2 = sys:get_status(Pid, 5000),
+ ?line ok = gen_event:stop(Pid),
+ ?line {status, Pid, _, [_, _, Pid, [], Data1]} = Status1,
+ ?line HandlerInfo1 = proplists:get_value(items, Data1),
+ ?line {"Installed handlers", [{_,dummy1_h,_,FmtState,_}]} = HandlerInfo1,
+ ?line {status, Pid, _, [_, _, Pid, [], Data2]} = Status2,
+ ?line HandlerInfo2 = proplists:get_value(items, Data2),
+ ?line {"Installed handlers", [{_,dummy1_h,_,FmtState,_}]} = HandlerInfo2,
+ ok.
+
+error_format_status(suite) ->
+ [];
+error_format_status(doc) ->
+ ["Test that a handler error calls format_status/2"];
+error_format_status(Config) when is_list(Config) ->
+ ?line error_logger_forwarder:register(),
+ OldFl = process_flag(trap_exit, true),
+ State = self(),
+ ?line {ok, Pid} = gen_event:start({local, my_dummy_handler}),
+ ?line ok = gen_event:add_sup_handler(my_dummy_handler, dummy1_h, [State]),
+ ?line ok = gen_event:notify(my_dummy_handler, do_crash),
+ ?line receive
+ {gen_event_EXIT,dummy1_h,{'EXIT',_}} -> ok
+ after 5000 ->
+ ?t:fail(exit_gen_event)
+ end,
+ FmtState = "dummy1_h handler state",
+ receive
+ {error,_GroupLeader, {Pid,
+ "** gen_event handler"++_,
+ [dummy1_h,my_dummy_handler,do_crash,
+ FmtState, _]}} ->
+ ok;
+ Other ->
+ ?line io:format("Unexpected: ~p", [Other]),
+ ?line ?t:fail()
+ end,
+ ?t:messages_get(),
+ ?line ok = gen_event:stop(Pid),
+ process_flag(trap_exit, OldFl),
+ ok.
diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl
index 23c1d9a193..dd120f8c05 100644
--- a/lib/stdlib/test/gen_fsm_SUITE.erl
+++ b/lib/stdlib/test/gen_fsm_SUITE.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(gen_fsm_SUITE).
@@ -30,7 +30,7 @@
-export([shutdown/1]).
--export([sys/1, sys1/1, call_format_status/1]).
+-export([sys/1, sys1/1, call_format_status/1, error_format_status/1]).
-export([hibernate/1,hiber_idle/3,hiber_wakeup/3,hiber_idle/2,hiber_wakeup/2]).
@@ -305,7 +305,7 @@ shutdown(Config) when is_list(Config) ->
ok.
-sys(suite) -> [sys1, call_format_status].
+sys(suite) -> [sys1, call_format_status, error_format_status].
sys1(Config) when is_list(Config) ->
?line {ok, Pid} =
@@ -320,10 +320,53 @@ sys1(Config) when is_list(Config) ->
call_format_status(Config) when is_list(Config) ->
?line {ok, Pid} = gen_fsm:start(gen_fsm_SUITE, [], []),
?line Status = sys:get_status(Pid),
- ?line {status, Pid, _Mod, [_PDict, running, _Parent, _, Data]} = Status,
+ ?line {status, Pid, _Mod, [_PDict, running, _, _, Data]} = Status,
?line [format_status_called | _] = lists:reverse(Data),
- ?line stop_it(Pid).
+ ?line stop_it(Pid),
+
+ %% check that format_status can handle a name being an atom (pid is
+ %% already checked by the previous test)
+ ?line {ok, Pid2} = gen_fsm:start({local, gfsm}, gen_fsm_SUITE, [], []),
+ ?line Status2 = sys:get_status(gfsm),
+ ?line {status, Pid2, _Mod, [_PDict2, running, _, _, Data2]} = Status2,
+ ?line [format_status_called | _] = lists:reverse(Data2),
+ ?line stop_it(Pid2),
+ %% check that format_status can handle a name being a term other than a
+ %% pid or atom
+ GlobalName1 = {global, "CallFormatStatus"},
+ ?line {ok, Pid3} = gen_fsm:start(GlobalName1, gen_fsm_SUITE, [], []),
+ ?line Status3 = sys:get_status(GlobalName1),
+ ?line {status, Pid3, _Mod, [_PDict3, running, _, _, Data3]} = Status3,
+ ?line [format_status_called | _] = lists:reverse(Data3),
+ ?line stop_it(Pid3),
+ GlobalName2 = {global, {name, "term"}},
+ ?line {ok, Pid4} = gen_fsm:start(GlobalName2, gen_fsm_SUITE, [], []),
+ ?line Status4 = sys:get_status(GlobalName2),
+ ?line {status, Pid4, _Mod, [_PDict4, running, _, _, Data4]} = Status4,
+ ?line [format_status_called | _] = lists:reverse(Data4),
+ ?line stop_it(Pid4).
+
+error_format_status(Config) when is_list(Config) ->
+ ?line error_logger_forwarder:register(),
+ OldFl = process_flag(trap_exit, true),
+ StateData = "called format_status",
+ ?line {ok, Pid} = gen_fsm:start(gen_fsm_SUITE, {state_data, StateData}, []),
+ %% bad return value in the gen_fsm loop
+ ?line {'EXIT',{{bad_return_value, badreturn},_}} =
+ (catch gen_fsm:sync_send_event(Pid, badreturn)),
+ receive
+ {error,_GroupLeader,{Pid,
+ "** State machine"++_,
+ [Pid,{_,_,badreturn},idle,StateData,_]}} ->
+ ok;
+ Other ->
+ ?line io:format("Unexpected: ~p", [Other]),
+ ?line ?t:fail()
+ end,
+ ?t:messages_get(),
+ process_flag(trap_exit, OldFl),
+ ok.
%% Hibernation
hibernate(suite) -> [];
@@ -704,6 +747,8 @@ init(hiber) ->
{ok, hiber_idle, []};
init(hiber_now) ->
{ok, hiber_idle, [], hibernate};
+init({state_data, StateData}) ->
+ {ok, idle, StateData};
init(_) ->
{ok, idle, state_data}.
@@ -844,5 +889,7 @@ handle_sync_event(stop_shutdown_reason, _From, _State, Data) ->
handle_sync_event({get, _Pid}, _From, State, Data) ->
{reply, {state, State, Data}, State, Data}.
-format_status(_Opt, [_Pdict, _StateData]) ->
+format_status(terminate, [_Pdict, StateData]) ->
+ StateData;
+format_status(normal, [_Pdict, _StateData]) ->
[format_status_called].
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index 6efdce78a1..99388ba2e3 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(gen_server_SUITE).
@@ -30,7 +30,8 @@
call_remote_n1/1, call_remote_n2/1, call_remote_n3/1, spec_init/1,
spec_init_local_registered_parent/1,
spec_init_global_registered_parent/1,
- otp_5854/1, hibernate/1, otp_7669/1, call_format_status/1
+ otp_5854/1, hibernate/1, otp_7669/1, call_format_status/1,
+ error_format_status/1, call_with_huge_message_queue/1
]).
% spawn export
@@ -51,7 +52,9 @@ all(suite) ->
call_remote_n2, call_remote_n3, spec_init,
spec_init_local_registered_parent,
spec_init_global_registered_parent,
- otp_5854, hibernate, otp_7669, call_format_status].
+ otp_5854, hibernate, otp_7669,
+ call_format_status, error_format_status,
+ call_with_huge_message_queue].
-define(default_timeout, ?t:minutes(1)).
@@ -895,15 +898,105 @@ call_format_status(doc) ->
["Test that sys:get_status/1,2 calls format_status/2"];
call_format_status(Config) when is_list(Config) ->
?line {ok, Pid} = gen_server:start_link({local, call_format_status},
- gen_server_SUITE, [], []),
+ ?MODULE, [], []),
?line Status1 = sys:get_status(call_format_status),
?line {status, Pid, _Mod, [_PDict, running, _Parent, _, Data1]} = Status1,
?line [format_status_called | _] = lists:reverse(Data1),
?line Status2 = sys:get_status(call_format_status, 5000),
?line {status, Pid, _Mod, [_PDict, running, _Parent, _, Data2]} = Status2,
?line [format_status_called | _] = lists:reverse(Data2),
+
+ %% check that format_status can handle a name being a pid (atom is
+ %% already checked by the previous test)
+ ?line {ok, Pid3} = gen_server:start_link(gen_server_SUITE, [], []),
+ ?line Status3 = sys:get_status(Pid3),
+ ?line {status, Pid3, _Mod, [_PDict3, running, _Parent, _, Data3]} = Status3,
+ ?line [format_status_called | _] = lists:reverse(Data3),
+
+ %% check that format_status can handle a name being a term other than a
+ %% pid or atom
+ GlobalName1 = {global, "CallFormatStatus"},
+ ?line {ok, Pid4} = gen_server:start_link(GlobalName1,
+ gen_server_SUITE, [], []),
+ ?line Status4 = sys:get_status(Pid4),
+ ?line {status, Pid4, _Mod, [_PDict4, running, _Parent, _, Data4]} = Status4,
+ ?line [format_status_called | _] = lists:reverse(Data4),
+ GlobalName2 = {global, {name, "term"}},
+ ?line {ok, Pid5} = gen_server:start_link(GlobalName2,
+ gen_server_SUITE, [], []),
+ ?line Status5 = sys:get_status(GlobalName2),
+ ?line {status, Pid5, _Mod, [_PDict5, running, _Parent, _, Data5]} = Status5,
+ ?line [format_status_called | _] = lists:reverse(Data5),
+ ok.
+
+%% Verify that error termination correctly calls our format_status/2 fun
+%%
+error_format_status(suite) ->
+ [];
+error_format_status(doc) ->
+ ["Test that an error termination calls format_status/2"];
+error_format_status(Config) when is_list(Config) ->
+ ?line error_logger_forwarder:register(),
+ OldFl = process_flag(trap_exit, true),
+ State = "called format_status",
+ ?line {ok, Pid} = gen_server:start_link(?MODULE, {state, State}, []),
+ ?line {'EXIT',{crashed,_}} = (catch gen_server:call(Pid, crash)),
+ receive
+ {'EXIT', Pid, crashed} ->
+ ok
+ end,
+ receive
+ {error,_GroupLeader,{Pid,
+ "** Generic server"++_,
+ [Pid,crash,State,crashed]}} ->
+ ok;
+ Other ->
+ ?line io:format("Unexpected: ~p", [Other]),
+ ?line ?t:fail()
+ end,
+ ?t:messages_get(),
+ process_flag(trap_exit, OldFl),
+ ok.
+
+%% Test that the time for a huge message queue is not
+%% significantly slower than with an empty message queue.
+call_with_huge_message_queue(Config) when is_list(Config) ->
+ ?line Pid = spawn_link(fun echo_loop/0),
+
+ ?line {Time,ok} = tc(fun() -> calls(10, Pid) end),
+
+ ?line [self() ! {msg,N} || N <- lists:seq(1, 500000)],
+ erlang:garbage_collect(),
+ ?line {NewTime,ok} = tc(fun() -> calls(10, Pid) end),
+ io:format("Time for empty message queue: ~p", [Time]),
+ io:format("Time for huge message queue: ~p", [NewTime]),
+
+ case (NewTime+1) / (Time+1) of
+ Q when Q < 10 ->
+ ok;
+ Q ->
+ io:format("Q = ~p", [Q]),
+ ?line ?t:fail()
+ end,
ok.
+calls(0, _) -> ok;
+calls(N, Pid) ->
+ {ultimate_answer,42} = call(Pid, {ultimate_answer,42}),
+ calls(N-1, Pid).
+
+call(Pid, Msg) ->
+ gen_server:call(Pid, Msg, infinity).
+
+tc(Fun) ->
+ timer:tc(erlang, apply, [Fun,[]]).
+
+echo_loop() ->
+ receive
+ {'$gen_call',{Pid,Ref},Msg} ->
+ Pid ! {Ref,Msg},
+ echo_loop()
+ end.
%%--------------------------------------------------------------
%% Help functions to spec_init_*
@@ -1064,5 +1157,7 @@ terminate({From, stopped_info}, _State) ->
terminate(_Reason, _State) ->
ok.
-format_status(_Opt, [_PDict, _State]) ->
- [format_status_called].
+format_status(terminate, [_PDict, State]) ->
+ State;
+format_status(normal, [_PDict, _State]) ->
+ format_status_called.
diff --git a/lib/stdlib/test/io_proto_SUITE.erl b/lib/stdlib/test/io_proto_SUITE.erl
index 93159fbd5b..d9672a8c7b 100644
--- a/lib/stdlib/test/io_proto_SUITE.erl
+++ b/lib/stdlib/test/io_proto_SUITE.erl
@@ -17,6 +17,7 @@
%% %CopyrightEnd%
%%
-module(io_proto_SUITE).
+-compile(r12).
-export([all/1]).
diff --git a/lib/stdlib/test/ms_transform_SUITE.erl b/lib/stdlib/test/ms_transform_SUITE.erl
index 79a0a9af89..2d90d5b823 100644
--- a/lib/stdlib/test/ms_transform_SUITE.erl
+++ b/lib/stdlib/test/ms_transform_SUITE.erl
@@ -37,6 +37,7 @@
-export([andalso_orelse/1]).
-export([float_1_function/1]).
-export([action_function/1]).
+-export([warnings/1]).
-export([init_per_testcase/2, fin_per_testcase/2]).
init_per_testcase(_Func, Config) ->
@@ -50,8 +51,90 @@ fin_per_testcase(_Func, Config) ->
all(suite) -> [from_shell,basic_ets,basic_dbg,records,record_index,multipass,
bitsyntax, record_defaults, andalso_orelse,
float_1_function, action_function,
+ warnings,
top_match, old_guards, autoimported, semicolon].
+%% This may be subject to change
+-define(WARN_NUMBER_SHADOW,50).
+warnings(suite) ->
+ [];
+warnings(doc) ->
+ ["Check that shadowed variables in fun head generate warning"];
+warnings(Config) when is_list(Config) ->
+ ?line setup(Config),
+ Prog = <<"A=5, "
+ "ets:fun2ms(fun({A,B}) "
+ " when is_integer(A) and (A+5 > B) -> "
+ " A andalso B "
+ " end)">>,
+ ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}}]}] =
+ compile_ww(Prog),
+ Prog2 = <<"C=5, "
+ "ets:fun2ms(fun({A,B} = C) "
+ " when is_integer(A) and (A+5 > B) -> "
+ " {A andalso B,C} "
+ " end)">>,
+ ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] =
+ compile_ww(Prog2),
+ Rec3 = <<"-record(a,{a,b,c,d=foppa}).">>,
+ Prog3 = <<"A=3,C=5, "
+ "ets:fun2ms(fun(#a{a = A, b = B} = C) "
+ " when is_integer(A) and (A+5 > B) -> "
+ " {A andalso B,C} "
+ " end)">>,
+ ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}},
+ {_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] =
+ compile_ww(Rec3,Prog3),
+ Rec4 = <<"-record(a,{a,b,c,d=foppa}).">>,
+ Prog4 = <<"A=3,C=5, "
+ "F = fun(B) -> B*3 end,"
+ "erlang:display(F(A)),"
+ "ets:fun2ms(fun(#a{a = A, b = B} = C) "
+ " when is_integer(A) and (A+5 > B) -> "
+ " {A andalso B,C} "
+ " end)">>,
+ ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}},
+ {_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] =
+ compile_ww(Rec4,Prog4),
+ Rec5 = <<"-record(a,{a,b,c,d=foppa}).">>,
+ Prog5 = <<"A=3,C=5, "
+ "F = fun(B) -> B*3 end,"
+ "erlang:display(F(A)),"
+ "B = ets:fun2ms(fun(#a{a = A, b = B} = C) "
+ " when is_integer(A) and (A+5 > B) -> "
+ " {A andalso B,C} "
+ " end)">>,
+ ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}},
+ {_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] =
+ compile_ww(Rec5,Prog5),
+ Prog6 = <<" X=bar, "
+ " A = case X of"
+ " foo ->"
+ " foo;"
+ " Y ->"
+ " ets:fun2ms(fun(Y) ->" % This is a warning
+ " 3*Y"
+ " end)"
+ " end,"
+ " ets:fun2ms(fun(Y) ->" % Y out of "scope" here, so no warning
+ " {3*Y,A}"
+ " end)">>,
+ ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'Y'}}]}] =
+ compile_ww(Prog6),
+ Prog7 = <<" X=bar, "
+ " A = case X of"
+ " foo ->"
+ " Y = foo;"
+ " Y ->"
+ " bar"
+ " end,"
+ " ets:fun2ms(fun(Y) ->" % Y exported from case and safe, so warn
+ " {3*Y,A}"
+ " end)">>,
+ ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'Y'}}]}] =
+ compile_ww(Prog7),
+ ok.
+
andalso_orelse(suite) ->
[];
andalso_orelse(doc) ->
@@ -721,6 +804,24 @@ compile_and_run(Records,Expr) ->
code:load_binary(tmp,FN,Bin),
tmp:tmp().
+compile_ww(Expr) ->
+ compile_ww(<<>>,Expr).
+compile_ww(Records,Expr) ->
+ Prog = <<
+ "-module(tmp).\n",
+ "-include_lib(\"stdlib/include/ms_transform.hrl\").\n",
+ "-export([tmp/0]).\n",
+ Records/binary,"\n",
+ "tmp() ->\n",
+ Expr/binary,".\n">>,
+ FN=temp_name(),
+ file:write_file(FN,Prog),
+ {ok,Forms} = epp:parse_file(FN,"",""),
+ {ok,tmp,_Bin,Wlist} = compile:forms(Forms,[return_warnings,
+ nowarn_unused_vars,
+ nowarn_unused_record]),
+ Wlist.
+
do_eval(String) ->
{done,{ok,T,_},[]} = erl_scan:tokens(
[],
diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl
index ff11ebc6bf..e21de8770a 100644
--- a/lib/stdlib/test/qlc_SUITE.erl
+++ b/lib/stdlib/test/qlc_SUITE.erl
@@ -1,25 +1,26 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%%----------------------------------------------------------------
%%% Purpose:Test Suite for the 'qlc' module.
%%%-----------------------------------------------------------------
-module(qlc_SUITE).
+-compile(r12).
-define(QLC, qlc).
-define(QLCs, "qlc").
@@ -3183,7 +3184,9 @@ lookup2(Config) when is_list(Config) ->
[] = qlc:e(Q),
false = lookup_keys(Q)
end, [{1,b},{2,3}])">>,
- {warnings,[{{3,48},qlc,nomatch_filter}]}},
+ {warnings,[{2,sys_core_fold,nomatch_guard},
+ {3,qlc,nomatch_filter},
+ {3,sys_core_fold,{eval_failure,badarg}}]}},
<<"etsc(fun(E) ->
Q = qlc:q([X || {X} <- ets:table(E), element(1,{X}) =:= 1]),
diff --git a/lib/stdlib/test/re_SUITE.erl b/lib/stdlib/test/re_SUITE.erl
index 02683f9f1a..46a84d4e24 100644
--- a/lib/stdlib/test/re_SUITE.erl
+++ b/lib/stdlib/test/re_SUITE.erl
@@ -18,12 +18,12 @@
%%
-module(re_SUITE).
--export([all/1, pcre/1,compile_options/1,run_options/1,combined_options/1,replace_autogen/1,global_capture/1,replace_input_types/1,replace_return/1,split_autogen/1,split_options/1,split_specials/1,error_handling/1,pcre_cve_2008_2371/1]).
+-export([all/1, pcre/1,compile_options/1,run_options/1,combined_options/1,replace_autogen/1,global_capture/1,replace_input_types/1,replace_return/1,split_autogen/1,split_options/1,split_specials/1,error_handling/1,pcre_cve_2008_2371/1,pcre_compile_workspace_overflow/1,re_infinite_loop/1]).
-include("test_server.hrl").
-include_lib("kernel/include/file.hrl").
-all(suite) -> [pcre,compile_options,run_options,combined_options,replace_autogen,global_capture,replace_input_types,replace_return,split_autogen,split_options,split_specials,error_handling,pcre_cve_2008_2371].
+all(suite) -> [pcre,compile_options,run_options,combined_options,replace_autogen,global_capture,replace_input_types,replace_return,split_autogen,split_options,split_specials,error_handling,pcre_cve_2008_2371,pcre_compile_workspace_overflow,re_infinite_loop].
pcre(doc) ->
["Run all applicable tests from the PCRE testsuites."];
@@ -544,3 +544,25 @@ pcre_cve_2008_2371(Config) when is_list(Config) ->
%% Make sure it doesn't crash the emulator.
re:compile(<<"(?i)[\xc3\xa9\xc3\xbd]|[\xc3\xa9\xc3\xbdA]">>, [unicode]),
ok.
+
+pcre_compile_workspace_overflow(doc) ->
+ "Patch from http://vcs.pcre.org/viewvc/code/trunk/pcre_compile.c?r1=504&r2=505&view=patch";
+pcre_compile_workspace_overflow(Config) when is_list(Config) ->
+ N = 819,
+ ?line {error,{"internal error: overran compiling workspace",799}} =
+ re:compile([lists:duplicate(N, $(), lists:duplicate(N, $))]),
+ ok.
+re_infinite_loop(doc) ->
+ "Make sure matches that really loop infinitely actually fail";
+re_infinite_loop(Config) when is_list(Config) ->
+ Dog = ?t:timetrap(?t:minutes(1)),
+ ?line Str =
+ "http:/www.flickr.com/slideShow/index.gne?group_id=&user_id=69845378@N0",
+ ?line EMail_regex = "[a-z0-9!#$%&'*+/=?^_`{|}~-]+"
+ ++ "(\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*"
+ ++ "@.*([a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+"
+ ++ "([a-zA-Z]{2}|com|org|net|gov|mil"
+ ++ "|biz|info|mobi|name|aero|jobs|museum)",
+ ?line nomatch = re:run(Str, EMail_regex),
+ ?t:timetrap_cancel(Dog),
+ ok.
diff --git a/lib/stdlib/test/string_SUITE.erl b/lib/stdlib/test/string_SUITE.erl
index 3171b87c44..452e048dd7 100644
--- a/lib/stdlib/test/string_SUITE.erl
+++ b/lib/stdlib/test/string_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -240,7 +240,8 @@ copies(Config) when is_list(Config) ->
?line "." = string:copies(".", 1),
?line 30 = length(string:copies("123", 10)),
%% invalid arg type
- ?line {'EXIT',_} = (catch string:chars("hej", -1)),
+ ?line {'EXIT',_} = (catch string:copies("hej", -1)),
+ ?line {'EXIT',_} = (catch string:copies("hej", 2.0)),
ok.
words(suite) ->
diff --git a/lib/stdlib/test/timer_simple_SUITE.erl b/lib/stdlib/test/timer_simple_SUITE.erl
index 021a22c61b..6aa2b7b945 100644
--- a/lib/stdlib/test/timer_simple_SUITE.erl
+++ b/lib/stdlib/test/timer_simple_SUITE.erl
@@ -224,11 +224,19 @@ cancel2(Config) when is_list(Config) ->
tc(doc) -> "Test sleep/1 and tc/3.";
tc(suite) -> [];
tc(Config) when is_list(Config) ->
- % This should both sleep and tc
- ?line {Res, ok} = timer:tc(timer, sleep, [500]),
- ?line ok = if
- Res < 500*1000 -> {too_early, Res}; % Too early
- Res > 800*1000 -> {too_late, Res}; % Too much time
+ % This should both sleep and tc/3
+ ?line {Res1, ok} = timer:tc(timer, sleep, [500]),
+ ?line ok = if
+ Res1 < 500*1000 -> {too_early, Res1}; % Too early
+ Res1 > 800*1000 -> {too_late, Res1}; % Too much time
+ true -> ok
+ end,
+
+ % This should both sleep and tc/2
+ ?line {Res2, ok} = timer:tc(fun(T) -> timer:sleep(T) end, [500]),
+ ?line ok = if
+ Res2 < 500*1000 -> {too_early, Res2}; % Too early
+ Res2 > 800*1000 -> {too_late, Res2}; % Too much time
true -> ok
end,
diff --git a/lib/stdlib/test/zip_SUITE.erl b/lib/stdlib/test/zip_SUITE.erl
index 12ca655000..48b14396c1 100644
--- a/lib/stdlib/test/zip_SUITE.erl
+++ b/lib/stdlib/test/zip_SUITE.erl
@@ -18,12 +18,13 @@
%%
-module(zip_SUITE).
--export([all/1, borderline/1, atomic/1,
+-export([all/1, borderline/1, atomic/1,
bad_zip/1, unzip_from_binary/1, unzip_to_binary/1,
zip_to_binary/1,
unzip_options/1, zip_options/1, list_dir_options/1, aliases/1,
openzip_api/1, zip_api/1, unzip_jar/1,
- compress_control/1]).
+ compress_control/1,
+ foldl/1]).
-include("test_server.hrl").
-include("test_server_line.hrl").
@@ -35,7 +36,8 @@ all(suite) -> [borderline, atomic, bad_zip,
zip_to_binary,
unzip_options, zip_options, list_dir_options, aliases,
openzip_api, zip_api, unzip_jar,
- compress_control].
+ compress_control,
+ foldl].
borderline(doc) ->
["Test creating, listing and extracting one file from an archive "
@@ -110,17 +112,17 @@ get_data(Port, Expect) ->
{Port, {data, Bytes}} ->
get_data(Port, match_output(Bytes, Expect, Port));
{Port, eof} ->
- Port ! {self(), close},
+ Port ! {self(), close},
receive
{Port, closed} ->
true
- end,
+ end,
receive
- {'EXIT', Port, _} ->
+ {'EXIT', Port, _} ->
ok
after 1 -> % force context switch
ok
- end,
+ end,
match_output(eof, Expect, Port)
end.
@@ -290,7 +292,7 @@ unzip_options(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
Long = filename:join(DataDir, "abc.zip"),
-
+
%% create a temp directory
Subdir = filename:join(PrivDir, "t"),
ok = file:make_dir(Subdir),
@@ -303,7 +305,7 @@ unzip_options(Config) when is_list(Config) ->
%% Verify.
?line true = (length(FList) =:= length(RetList)),
- ?line lists:foreach(fun(F)-> {ok,B} = file:read_file(filename:join(DataDir, F)),
+ ?line lists:foreach(fun(F)-> {ok,B} = file:read_file(filename:join(DataDir, F)),
{ok,B} = file:read_file(filename:join(Subdir, F)) end,
FList),
?line lists:foreach(fun(F)-> ok = file:delete(F) end,
@@ -321,7 +323,7 @@ unzip_jar(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
JarFile = filename:join(DataDir, "test.jar"),
-
+
%% create a temp directory
Subdir = filename:join(PrivDir, "jartest"),
ok = file:make_dir(Subdir),
@@ -479,7 +481,7 @@ unzip_to_binary(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
- delete_all_in(PrivDir),
+ delete_all_in(PrivDir),
file:set_cwd(PrivDir),
Long = filename:join(DataDir, "abc.zip"),
@@ -595,17 +597,17 @@ do_delete_files([],Cnt) ->
Cnt;
do_delete_files([Item|Rest], Cnt) ->
case file:delete(Item) of
- ok ->
+ ok ->
DelCnt = 1;
{error,eperm} ->
file:change_mode(Item, 8#777),
DelCnt = delete_files(filelib:wildcard(filename:join(Item, "*"))),
- file:del_dir(Item);
+ file:del_dir(Item);
{error,eacces} ->
%% We'll see about that!
file:change_mode(Item, 8#777),
case file:delete(Item) of
- ok ->
+ ok ->
DelCnt = 1;
{error,_} ->
erlang:yield(),
@@ -643,22 +645,22 @@ compress_control(Config) when is_list(Config) ->
],
test_compress_control(Dir,
- Files,
+ Files,
[{compress, []}],
[]),
test_compress_control(Dir,
- Files,
+ Files,
[{uncompress, all}],
[]),
test_compress_control(Dir,
- Files,
+ Files,
[{uncompress, []}],
[".txt", ".exe", ".zip", ".lzh", ".arj"]),
test_compress_control(Dir,
- Files,
+ Files,
[],
[".txt", ".exe"]),
@@ -686,7 +688,7 @@ test_compress_control(Dir, Files, ZipOptions, Expected) ->
create_files(Files),
{ok, Zip} = zip:create(Zip, [Dir], ZipOptions),
-
+
{ok, OpenZip} = zip:openzip_open(Zip, [memory]),
{ok,[#zip_comment{comment = ""} | ZipList]} = zip:openzip_list_dir(OpenZip),
io:format("compress_control: -> ~p -> ~p\n -> ~pn", [Expected, ZipOptions, ZipList]),
@@ -698,19 +700,19 @@ test_compress_control(Dir, Files, ZipOptions, Expected) ->
delete_files(lists:reverse(Names)), % Remove plain files before directories
ok.
-
+
verify_compression([{Name, Kind, _Filler} | Files], ZipList, OpenZip, ZipOptions, Expected) ->
{Name2, BinSz} =
case Kind of
- dir ->
+ dir ->
{Name ++ "/", 0};
- _ ->
+ _ ->
{ok, {Name, Bin}} = zip:openzip_get(Name, OpenZip),
{Name, size(Bin)}
end,
{Name2, {value, ZipFile}} = {Name2, lists:keysearch(Name2, #zip_file.name, ZipList)},
#zip_file{info = #file_info{size = InfoSz, type = InfoType}, comp_size = InfoCompSz} = ZipFile,
-
+
Ext = filename:extension(Name),
IsComp = is_compressed(Ext, Kind, ZipOptions),
ExpComp = lists:member(Ext, Expected),
@@ -757,3 +759,33 @@ extensions([H | T], Old) ->
extensions([], Old) ->
Old.
+foldl(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ File = filename:join([PrivDir, "foldl.zip"]),
+
+ FooBin = <<"FOO">>,
+ BarBin = <<"BAR">>,
+ Files = [{"foo", FooBin}, {"bar", BarBin}],
+ ?line {ok, {File, Bin}} = zip:create(File, Files, [memory]),
+ ZipFun = fun(N, I, B, Acc) -> [{N, B(), I()} | Acc] end,
+ ?line {ok, FileSpec} = zip:foldl(ZipFun, [], {File, Bin}),
+ ?line [{"bar", BarBin, #file_info{}}, {"foo", FooBin, #file_info{}}] = FileSpec,
+ ?line {ok, {File, Bin}} = zip:create(File, lists:reverse(FileSpec), [memory]),
+ ?line {foo_bin, FooBin} =
+ try
+ zip:foldl(fun("foo", _, B, _) -> throw(B()); (_, _, _, Acc) -> Acc end, [], {File, Bin})
+ catch
+ throw:FooBin ->
+ {foo_bin, FooBin}
+ end,
+ ?line ok = file:write_file(File, Bin),
+ ?line {ok, FileSpec} = zip:foldl(ZipFun, [], File),
+
+ ?line {error, einval} = zip:foldl(fun() -> ok end, [], File),
+ ?line {error, einval} = zip:foldl(ZipFun, [], 42),
+ ?line {error, einval} = zip:foldl(ZipFun, [], {File, 42}),
+
+ ?line ok = file:delete(File),
+ ?line {error, enoent} = zip:foldl(ZipFun, [], File),
+
+ ok.
diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk
index 75dc7dc62f..db7954af04 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1,2 +1 @@
-STDLIB_VSN = 1.16.5
-
+STDLIB_VSN = 1.17.2