diff options
Diffstat (limited to 'lib/stdlib')
97 files changed, 7999 insertions, 1693 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> | {chunks_different, ChunkId}</v> + <v> | different_chunks</v> <v> | Reason1 -- see info/1</v> <v> Module1 = Module2 = atom()</v> <v> 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> >= <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(<<"erlang">>,{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>[<<"functional">>,<<"programming">>]</c> + is given as <c>Pattern</c>, this + means "either <c><<"functional">></c> or + <c><<"programming">></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 > 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> < <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(<<169,138,199>>,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). +<<169,138,199>> + </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([<<"erlang">>,<<"ergonomy">>]). +2 +2> binary:longest_common_prefix([<<"erlang">>,<<"perl">>]). +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([<<"erlang">>,<<"fang">>]). +3 +2> binary:longest_common_suffix([<<"erlang">>,<<"perl">>]). +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(<<"abcde">>, [<<"bcde">>,<<"cd">>],[]). +{1,4} +</code> + + <p>Even though <c><<"cd">></c> ends before + <c><<"bcde">></c>, <c><<"bcde">></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(<<"abcde">>, + [<<"bcde">>,<<"bc">>>,<<"de">>],[]). +[{1,4}] +</code> + + <p>The result shows that <<bcde">> is selected instead of the + shorter match <<"bc">> (which would have given raise to one + more match,<<"de">>). 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 = <<1,2,3,4,5,6,7,8,9,10>>. +2> binary:part(Bin,{byte_size(Bin), -5)). +<<6,7,8,9,10>> +</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(<<1>>,100). +<<1,1,1,1,1 ... +2> byte_size(A). +100 +3> binary:referenced_byte_size(A) +100 +4> <<_:10/binary,B:10/binary,_/binary>> = A. +<<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() =< 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(<<"abcde">>,<<"b">>,<<"[]">>,[{insert_replaced,1}]). +<<"a[b]cde">> +2> binary:replace(<<"abcde">>,[<<"b">>,<<"d">>],<<"[]">>, + [global,{insert_replaced,1}]). +<<"a[b]c[d]e">> +3> binary:replace(<<"abcde">>,[<<"b">>,<<"d">>],<<"[]">>, + [global,{insert_replaced,[1,1]}]). +<<"a[bb]c[dd]e">> +4> binary:replace(<<"abcde">>,[<<"b">>,<<"d">>],<<"[-]">>, + [global,{insert_replaced,[1,2]}]). +<<"a[b-b]c[d-d]e">> +</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(<<1,255,4,0,0,0,2,3>>, [<<0,0,0>>,<<2>>],[]). +[<<1,255,4>>, <<2,3>>] +2> binary:split(<<0,1,0,0,4,255,255,9>>, [<<0,0>>, <<255,255>>],[global]). +[<<0,1>>,<<4>>,<<9>>] +</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(<<"banana">>,[<<"a">>],[{scope,{2,3}}]). +[<<"ban">>,<<"na">>] +2> binary:split(binary:part(<<"banana">>,{2,3}),[<<"a">>],[]). +[<<"n">>,<<"n">>] +</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/ets.xml b/lib/stdlib/doc/src/ets.xml index ee1befc882..746f94d3f4 100644 --- a/lib/stdlib/doc/src/ets.xml +++ b/lib/stdlib/doc/src/ets.xml @@ -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> Option = Type | Access | named_table | {keypos,Pos} | {heir,pid(),HeirData} | {heir,none} | {write_concurrency,bool()}</v> + <v> Option = Type | Access | named_table | {keypos,Pos} | {heir,pid(),HeirData} | {heir,none} | Tweaks</v> <v> Type = set | ordered_set | bag | duplicate_bag</v> <v> Access = public | protected | private</v> + <v> Tweaks = {write_concurrency,bool()} | {read_concurrency,bool()} | compressed</v> <v> Pos = int()</v> <v> 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 <- 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> diff --git a/lib/stdlib/doc/src/filelib.xml b/lib/stdlib/doc/src/filelib.xml index c1c4ca9350..47d64f245c 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 do + 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 b52e862a5c..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 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> 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> 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/notes.xml b/lib/stdlib/doc/src/notes.xml index 28a3719d38..a8fe41f000 100644 --- a/lib/stdlib/doc/src/notes.xml +++ b/lib/stdlib/doc/src/notes.xml @@ -30,6 +30,557 @@ </header> <p>This document describes the changes made to the STDLIB application.</p> +<section><title>STDLIB 1.17.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Several type specifications for standard libraries were + wrong in the R14B01 release. This is now corrected. The + corrections concern types in re,io,filename and the + module erlang itself.</p> + <p> + Own Id: OTP-9008</p> + </item> + </list> + </section> + +</section> + +<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> 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 "\" (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: - "\\".</p> + <p>The Erlang literal syntax for strings uses the "\" + (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.: "\\".</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 > 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 + > 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> ".*(?<FOO>abcd).*"</code> <p>With this expression, we could still give the index of the subpattern with the following call:</p> <code> re:run("ABCabcdABC",".*(?<FOO>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",".*(?<FOO>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> ".*((?<FOO>abdd)|a(..d)).*"</code> <p>There are three explicitly capturing subpatterns, where the opening parenthesis position determines the order in the result, hence <c>((?<FOO>abdd)|a(..d))</c> is subpattern index 1, <c>(?<FOO>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>&</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>&</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","[&]",[{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 "strings", 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 "l", causing "Er" to be the first part in the result. When the regular expression matched, the (only) subexpression was - bound to the "l", why the "l" is inserted + bound to the "l", so the "l" is inserted in the group together with "Er". The next match is of the "n", making "a" the next part to be - returned. As the subexpression is bound to the substring + returned. Since the subexpression is bound to the substring "n" in this case, the "n" 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 "g" in the end of the string + <p>since the matching of the "g" 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 "ang", not "an", 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] × ... × 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 : z = ((x[1], ..., x[n]), (y[1],...,y[n])) for some (x[1], ..., x[n]) in S and for some (x[i], 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 f1b0659ea2..416df1f02c 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> @@ -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 have names that can consistently be interpreted. In MacOSX, all file names are retrieved in UTF-8 encoding, while Windows has 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 "Unicode file name translation mode", 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 "raw file names" 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 do 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, do 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, were 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 "raw file names" 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 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 "bj�rn" 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 have 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 <<"bj�rn"/utf8>>. 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 "bj�rn", 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 "raw" file names, i.e. as binaries.</p> +<p>The core system of Erlang (kernel and stdlib) accepts 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, a 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 "UTF-8 list" 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 "%HH", 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 "bj�rn" 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> 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/base64.erl b/lib/stdlib/src/base64.erl index ebef998ee1..a14a72ac6d 100644 --- a/lib/stdlib/src/base64.erl +++ b/lib/stdlib/src/base64.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. 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 @@ -114,7 +114,7 @@ decode(List) when is_list(List) -> mime_decode(Bin) when is_binary(Bin) -> mime_decode_binary(<<>>, Bin); mime_decode(List) when is_list(List) -> - list_to_binary(mime_decode_l(List)). + mime_decode(list_to_binary(List)). -spec decode_l(string()) -> string(). @@ -125,7 +125,7 @@ decode_l(List) -> -spec mime_decode_l(string()) -> string(). mime_decode_l(List) -> - L = strip_illegal(List, []), + L = strip_illegal(List, [], 0), decode(L, []). %%------------------------------------------------------------------------- @@ -198,6 +198,9 @@ decode_binary(Result, <<>>) -> true = is_binary(Result), Result. +%% Skipping pad character if not at end of string. Also liberal about +%% excess padding and skipping of other illegal (non-base64 alphabet) +%% characters. See section 3.3 of RFC4648 mime_decode_binary(Result, <<0:8,T/bits>>) -> mime_decode_binary(Result, T); mime_decode_binary(Result0, <<C:8,T/bits>>) -> @@ -205,15 +208,27 @@ mime_decode_binary(Result0, <<C:8,T/bits>>) -> Bits when is_integer(Bits) -> mime_decode_binary(<<Result0/bits,Bits:6>>, T); eq -> - case tail_contains_equal(T) of - true -> - Split = byte_size(Result0) - 1, - <<Result:Split/bytes,_:4>> = Result0, - Result; - false -> - Split = byte_size(Result0) - 1, - <<Result:Split/bytes,_:2>> = Result0, - Result + case tail_contains_more(T, false) of + {<<>>, Eq} -> + %% No more valid data. + case bit_size(Result0) rem 8 of + 0 -> + %% '====' is not uncommon. + Result0; + 4 when Eq -> + %% enforce at least one more '=' only ignoring illegals and spacing + Split = byte_size(Result0) - 1, + <<Result:Split/bytes,_:4>> = Result0, + Result; + 2 -> + %% remove 2 bits + Split = byte_size(Result0) - 1, + <<Result:Split/bytes,_:2>> = Result0, + Result + end; + {More, _} -> + %% More valid data, skip the eq as invalid + mime_decode_binary(Result0, More) end; _ -> mime_decode_binary(Result0, T) @@ -262,31 +277,63 @@ strip_ws(<<$\s,T/binary>>) -> strip_ws(T); strip_ws(T) -> T. -strip_illegal([0|Cs], A) -> - strip_illegal(Cs, A); -strip_illegal([C|Cs], A) -> +%% Skipping pad character if not at end of string. Also liberal about +%% excess padding and skipping of other illegal (non-base64 alphabet) +%% characters. See section 3.3 of RFC4648 +strip_illegal([], A, _Cnt) -> + A; +strip_illegal([0|Cs], A, Cnt) -> + strip_illegal(Cs, A, Cnt); +strip_illegal([C|Cs], A, Cnt) -> case element(C, ?DECODE_MAP) of - bad -> strip_illegal(Cs, A); - ws -> strip_illegal(Cs, A); - eq -> strip_illegal_end(Cs, [$=|A]); - _ -> strip_illegal(Cs, [C|A]) - end; -strip_illegal([], A) -> A. + bad -> + strip_illegal(Cs, A, Cnt); + ws -> + strip_illegal(Cs, A, Cnt); + eq -> + case {tail_contains_more(Cs, false), Cnt rem 4} of + {{[], _}, 0} -> + A; %% Ignore extra = + {{[], true}, 2} -> + [$=|[$=|A]]; %% 'XX==' + {{[], _}, 3} -> + [$=|A]; %% 'XXX=' + {{[H|T], _}, _} -> + %% more data, skip equals + strip_illegal(T, [H|A], Cnt+1) + end; + _ -> + strip_illegal(Cs, [C|A], Cnt+1) + end. -strip_illegal_end([0|Cs], A) -> - strip_illegal_end(Cs, A); -strip_illegal_end([C|Cs], A) -> +%% Search the tail for more valid data and remember if we saw +%% another equals along the way. +tail_contains_more([], Eq) -> + {[], Eq}; +tail_contains_more(<<>>, Eq) -> + {<<>>, Eq}; +tail_contains_more([C|T]=More, Eq) -> case element(C, ?DECODE_MAP) of - bad -> strip_illegal(Cs, A); - ws -> strip_illegal(Cs, A); - eq -> [C|A]; - _ -> strip_illegal(Cs, [C|A]) + bad -> + tail_contains_more(T, Eq); + ws -> + tail_contains_more(T, Eq); + eq -> + tail_contains_more(T, true); + _ -> + {More, Eq} end; -strip_illegal_end([], A) -> A. - -tail_contains_equal(<<$=,_/binary>>) -> true; -tail_contains_equal(<<_,T/binary>>) -> tail_contains_equal(T); -tail_contains_equal(<<>>) -> false. +tail_contains_more(<<C:8,T/bits>> =More, Eq) -> + case element(C, ?DECODE_MAP) of + bad -> + tail_contains_more(T, Eq); + ws -> + tail_contains_more(T, Eq); + eq -> + tail_contains_more(T, true); + _ -> + {More, Eq} + end. %% accessors b64e(X) -> 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 e05a1c787f..235ea939a8 100644 --- a/lib/stdlib/src/c.erl +++ b/lib/stdlib/src/c.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -20,6 +20,8 @@ %% 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, @@ -40,31 +42,31 @@ -spec help() -> 'ok'. help() -> - format("bt(Pid) -- stack backtrace for a process\n" - "c(File) -- compile and load code in <File>\n" - "cd(Dir) -- change working directory\n" - "flush() -- flush any messages sent to the shell\n" - "help() -- help info\n" - "i() -- information about the system\n" - "ni() -- information about the networked system\n" - "i(X,Y,Z) -- information about pid <X,Y,Z>\n" - "l(Module) -- load or reload module\n" - "lc([File]) -- compile a list of Erlang modules\n" - "ls() -- list files in the current directory\n" - "ls(Dir) -- list files in directory <Dir>\n" - "m() -- which modules are loaded\n" - "m(Mod) -- information about module <Mod>\n" - "memory() -- memory allocation information\n" - "memory(T) -- memory allocation information of type <T>\n" - "nc(File) -- compile and load code in <File> on all nodes\n" - "nl(Module) -- load module on all nodes\n" - "pid(X,Y,Z) -- convert X,Y,Z to a Pid\n" - "pwd() -- print working directory\n" - "q() -- quit - shorthand for init:stop()\n" - "regs() -- information about registered processes\n" - "nregs() -- information about all registered processes\n" - "xm(M) -- cross reference check a module\n" - "y(File) -- generate a Yecc parser\n"). + io:put_chars(<<"bt(Pid) -- stack backtrace for a process\n" + "c(File) -- compile and load code in <File>\n" + "cd(Dir) -- change working directory\n" + "flush() -- flush any messages sent to the shell\n" + "help() -- help info\n" + "i() -- information about the system\n" + "ni() -- information about the networked system\n" + "i(X,Y,Z) -- information about pid <X,Y,Z>\n" + "l(Module) -- load or reload module\n" + "lc([File]) -- compile a list of Erlang modules\n" + "ls() -- list files in the current directory\n" + "ls(Dir) -- list files in directory <Dir>\n" + "m() -- which modules are loaded\n" + "m(Mod) -- information about module <Mod>\n" + "memory() -- memory allocation information\n" + "memory(T) -- memory allocation information of type <T>\n" + "nc(File) -- compile and load code in <File> on all nodes\n" + "nl(Module) -- load module on all nodes\n" + "pid(X,Y,Z) -- convert X,Y,Z to a Pid\n" + "pwd() -- print working directory\n" + "q() -- quit - shorthand for init:stop()\n" + "regs() -- information about registered processes\n" + "nregs() -- information about all registered processes\n" + "xm(M) -- cross reference check a module\n" + "y(File) -- generate a Yecc parser\n">>). %% c(FileName) %% Compile a file/module. @@ -657,7 +659,7 @@ portformat(Name, Id, Cmd) -> 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. @@ -682,11 +684,27 @@ ls() -> 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, @@ -696,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(). 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_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 f144cbb938..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 @@ -111,6 +113,8 @@ 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}) -> @@ -163,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 -> @@ -188,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) -> @@ -232,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); @@ -324,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 @@ -356,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), @@ -380,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) @@ -491,9 +504,9 @@ 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 catch macro_expansion(Toks) of + 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) -> @@ -608,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); @@ -644,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, @@ -766,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), - wait_req_scan(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) @@ -814,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}]) -> throw({error,Ld,missing_parenthesis}); -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) @@ -1084,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 @@ -1129,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}) -> @@ -1145,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 @@ -1165,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 @@ -1177,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 @@ -1188,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 a38b7639d8..61ce41f714 100644 --- a/lib/stdlib/src/erl_expand_records.erl +++ b/lib/stdlib/src/erl_expand_records.erl @@ -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}; @@ -800,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 94ad560549..cfb9f0ca98 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -40,7 +40,7 @@ %% 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; @@ -60,6 +60,10 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> (_Opt, Def) -> Def end, Default, Opts). +%% The maximum number of arguments allowed for a function. + +-define(MAX_ARGUMENTS, 255). + %% The error and warning info structures, {Line,Module,Descriptor}, %% are kept in their seperate fields in the lint state record together %% with the name of the file (when a new file is entered, marked by @@ -72,6 +76,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 +102,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 +120,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 +172,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 +200,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", @@ -208,11 +230,17 @@ format_error({obsolete_guard, {F, A}}) -> io_lib:format("~p/~p obsolete", [F, A]); format_error({reserved_for_future,K}) -> io_lib:format("atom ~w: future reserved keyword - rename or quote", [K]); +format_error({too_many_arguments,Arity}) -> + io_lib:format("too many arguments (~w) - " + "maximum allowed is ~w", [Arity,?MAX_ARGUMENTS]); %% --- patterns and guards --- 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 +270,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 +318,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 +386,7 @@ pseudolocals() -> %% %% Used by erl_eval.erl to check commands. -%% +%% exprs(Exprs, BindingsList) -> exprs_opt(Exprs, BindingsList, []). @@ -362,7 +394,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 +423,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 +538,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 +548,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 +570,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 +597,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 +703,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 +762,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 +911,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 +961,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 +981,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 +1052,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])]). @@ -1048,10 +1098,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) -> @@ -1061,18 +1111,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, @@ -1081,8 +1132,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), @@ -1094,11 +1168,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) @@ -1141,13 +1245,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) @@ -1179,7 +1285,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) -> @@ -1191,12 +1297,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 =/= [] -> @@ -1205,7 +1305,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), @@ -1214,18 +1314,18 @@ define_function(Line, Name, Arity, St0) -> true -> 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 + St2 = function_check_max_args(Line, Arity, St1), + St3 = St2#lint{defined=gb_sets:add_element(NA, St2#lint.defined)}, + case imported(Name, Arity, St3) of + {yes,_M} -> add_error(Line, {define_import,NA}, St3); + no -> St3 end end. +function_check_max_args(Line, Arity, St) when Arity > ?MAX_ARGUMENTS -> + add_error(Line, {too_many_arguments,Arity}, St); +function_check_max_args(_, _, St) -> St. + %% clauses([Clause], VarTable, State) -> {VarTable, State}. clauses(Cs, Vt, St) -> @@ -1258,7 +1358,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 @@ -1276,7 +1376,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}; @@ -1294,7 +1394,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) @@ -1309,7 +1409,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)} @@ -1369,7 +1469,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}}} -> @@ -1451,7 +1551,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. @@ -1498,7 +1598,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! @@ -1513,7 +1613,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! @@ -1596,7 +1696,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) -> @@ -1678,8 +1778,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 ); @@ -1710,7 +1808,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,_,_}]}, @@ -1725,14 +1823,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), @@ -1777,7 +1883,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), @@ -1798,7 +1904,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; @@ -1820,7 +1926,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) -> @@ -1895,15 +2001,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 @@ -1958,8 +2062,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; @@ -1969,7 +2076,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) -> @@ -1992,29 +2099,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; @@ -2155,7 +2287,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 @@ -2306,7 +2438,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), @@ -2316,7 +2448,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)} || @@ -2394,7 +2526,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()) @@ -2432,7 +2564,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, @@ -2441,8 +2573,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, @@ -2467,7 +2599,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 @@ -2475,12 +2613,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. @@ -2563,7 +2701,6 @@ default_types() -> {set, 0}, {string, 0}, {term, 0}, - {tid, 0}, {timeout, 0}, {var, 1}], dict:from_list([{T, -1} || T <- DefTypes]). @@ -2585,7 +2722,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. @@ -2606,7 +2742,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, []} @@ -2666,10 +2802,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 @@ -2677,19 +2815,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. @@ -2834,7 +2971,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 @@ -2864,9 +3001,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}}],[], @@ -2919,7 +3056,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}}], @@ -2957,7 +3094,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 @@ -2973,7 +3110,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) @@ -3073,7 +3210,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). @@ -3103,7 +3240,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)}}; @@ -3118,7 +3255,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) -> @@ -3154,7 +3291,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. @@ -3170,7 +3307,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); _ -> @@ -3355,11 +3492,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); @@ -3437,3 +3574,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 69807aad83..bb4b18cf9b 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -47,7 +47,7 @@ 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 @@ -61,7 +61,7 @@ char integer float atom string var '++' '--' '==' '/=' '=<' '<' '>=' '>' '=:=' '=/=' '<=' '<<' '>>' -'!' '=' '::' +'!' '=' '::' '..' '...' 'spec' % helper dot. @@ -112,6 +112,7 @@ type_guards -> type_guard ',' type_guards : ['$1'|'$3']. 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']. @@ -119,8 +120,24 @@ 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'. @@ -134,7 +151,7 @@ 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'}. @@ -142,19 +159,13 @@ 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', @@ -179,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']. @@ -597,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}) -> @@ -610,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"). diff --git a/lib/stdlib/src/erl_posix_msg.erl b/lib/stdlib/src/erl_posix_msg.erl index fe981b23a7..909cc1d102 100644 --- a/lib/stdlib/src/erl_posix_msg.erl +++ b/lib/stdlib/src/erl_posix_msg.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 @@ -24,143 +24,146 @@ -spec message(atom()) -> string(). -message(e2big) -> "argument list too long"; -message(eacces) -> "permission denied"; -message(eaddrinuse) -> "address already in use"; -message(eaddrnotavail) -> "can't assign requested address"; -message(eadv) -> "advertise error"; -message(eafnosupport) -> "address family not supported by protocol family"; -message(eagain) -> "resource temporarily unavailable"; -message(ealign) -> "EALIGN"; -message(ealready) -> "operation already in progress"; -message(ebade) -> "bad exchange descriptor"; -message(ebadf) -> "bad file number"; -message(ebadfd) -> "file descriptor in bad state"; -message(ebadmsg) -> "not a data message"; -message(ebadr) -> "bad request descriptor"; -message(ebadrpc) -> "RPC structure is bad"; -message(ebadrqc) -> "bad request code"; -message(ebadslt) -> "invalid slot"; -message(ebfont) -> "bad font file format"; -message(ebusy) -> "file busy"; -message(echild) -> "no children"; -message(echrng) -> "channel number out of range"; -message(ecomm) -> "communication error on send"; -message(econnaborted) -> "software caused connection abort"; -message(econnrefused) -> "connection refused"; -message(econnreset) -> "connection reset by peer"; -message(edeadlk) -> "resource deadlock avoided"; -message(edeadlock) -> "resource deadlock avoided"; -message(edestaddrreq) -> "destination address required"; -message(edirty) -> "mounting a dirty fs w/o force"; -message(edom) -> "math argument out of range"; -message(edotdot) -> "cross mount point"; -message(edquot) -> "disk quota exceeded"; -message(eduppkg) -> "duplicate package name"; -message(eexist) -> "file already exists"; -message(efault) -> "bad address in system call argument"; -message(efbig) -> "file too large"; -message(ehostdown) -> "host is down"; -message(ehostunreach) -> "host is unreachable"; -message(eidrm) -> "identifier removed"; -message(einit) -> "initialization error"; -message(einprogress) -> "operation now in progress"; -message(eintr) -> "interrupted system call"; -message(einval) -> "invalid argument"; -message(eio) -> "I/O error"; -message(eisconn) -> "socket is already connected"; -message(eisdir) -> "illegal operation on a directory"; -message(eisnam) -> "is a name file"; -message(elbin) -> "ELBIN"; -message(el2hlt) -> "level 2 halted"; -message(el2nsync) -> "level 2 not synchronized"; -message(el3hlt) -> "level 3 halted"; -message(el3rst) -> "level 3 reset"; -message(elibacc) -> "can not access a needed shared library"; -message(elibbad) -> "accessing a corrupted shared library"; -message(elibexec) -> "can not exec a shared library directly"; -message(elibmax) -> - "attempting to link in more shared libraries than system limit"; -message(elibscn) -> ".lib section in a.out corrupted"; -message(elnrng) -> "link number out of range"; -message(eloop) -> "too many levels of symbolic links"; -message(emfile) -> "too many open files"; -message(emlink) -> "too many links"; -message(emsgsize) -> "message too long"; -message(emultihop) -> "multihop attempted"; -message(enametoolong) -> "file name too long"; -message(enavail) -> "not available"; -message(enet) -> "ENET"; -message(enetdown) -> "network is down"; -message(enetreset) -> "network dropped connection on reset"; -message(enetunreach) -> "network is unreachable"; -message(enfile) -> "file table overflow"; -message(enoano) -> "anode table overflow"; -message(enobufs) -> "no buffer space available"; -message(enocsi) -> "no CSI structure available"; -message(enodata) -> "no data available"; -message(enodev) -> "no such device"; -message(enoent) -> "no such file or directory"; -message(enoexec) -> "exec format error"; -message(enolck) -> "no locks available"; -message(enolink) -> "link has be severed"; -message(enomem) -> "not enough memory"; -message(enomsg) -> "no message of desired type"; -message(enonet) -> "machine is not on the network"; -message(enopkg) -> "package not installed"; -message(enoprotoopt) -> "bad proocol option"; -message(enospc) -> "no space left on device"; -message(enosr) -> "out of stream resources or not a stream device"; -message(enosym) -> "unresolved symbol name"; -message(enosys) -> "function not implemented"; -message(enotblk) -> "block device required"; -message(enotconn) -> "socket is not connected"; -message(enotdir) -> "not a directory"; -message(enotempty) -> "directory not empty"; -message(enotnam) -> "not a name file"; -message(enotsock) -> "socket operation on non-socket"; -message(enotsup) -> "operation not supported"; -message(enotty) -> "inappropriate device for ioctl"; -message(enotuniq) -> "name not unique on network"; -message(enxio) -> "no such device or address"; -message(eopnotsupp) -> "operation not supported on socket"; -message(eperm) -> "not owner"; -message(epfnosupport) -> "protocol family not supported"; -message(epipe) -> "broken pipe"; -message(eproclim) -> "too many processes"; -message(eprocunavail) -> "bad procedure for program"; -message(eprogmismatch) -> "program version wrong"; -message(eprogunavail) -> "RPC program not available"; -message(eproto) -> "protocol error"; -message(eprotonosupport) -> "protocol not suppored"; -message(eprototype) -> "protocol wrong type for socket"; -message(erange) -> "math result unrepresentable"; -message(erefused) -> "EREFUSED"; -message(eremchg) -> "remote address changed"; -message(eremdev) -> "remote device"; -message(eremote) -> "pathname hit remote file system"; -message(eremoteio) -> "remote i/o error"; -message(eremoterelease) -> "EREMOTERELEASE"; -message(erofs) -> "read-only file system"; -message(erpcmismatch) -> "RPC version is wrong"; -message(erremote) -> "object is remote"; -message(eshutdown) -> "can't send after socket shutdown"; -message(esocktnosupport) -> "socket type not supported"; -message(espipe) -> "invalid seek"; -message(esrch) -> "no such process"; -message(esrmnt) -> "srmount error"; -message(estale) -> "stale remote file handle"; -message(esuccess) -> "Error 0"; -message(etime) -> "timer expired"; -message(etimedout) -> "connection timed out"; -message(etoomanyrefs) -> "too many references: can't splice"; -message(etxtbsy) -> "text file or pseudo-device busy"; -message(euclean) -> "structure needs cleaning"; -message(eunatch) -> "protocol driver not attached"; -message(eusers) -> "too many users"; -message(eversion) -> "version mismatch"; -message(ewouldblock) -> "operation would block"; -message(exdev) -> "cross-domain link"; -message(exfull) -> "message tables full"; -message(nxdomain) -> "non-existing domain"; -message(_) -> "unknown POSIX error". +message(T) -> + binary_to_list(message_1(T)). + +message_1(e2big) -> <<"argument list too long">>; +message_1(eacces) -> <<"permission denied">>; +message_1(eaddrinuse) -> <<"address already in use">>; +message_1(eaddrnotavail) -> <<"can't assign requested address">>; +message_1(eadv) -> <<"advertise error">>; +message_1(eafnosupport) -> <<"address family not supported by protocol family">>; +message_1(eagain) -> <<"resource temporarily unavailable">>; +message_1(ealign) -> <<"EALIGN">>; +message_1(ealready) -> <<"operation already in progress">>; +message_1(ebade) -> <<"bad exchange descriptor">>; +message_1(ebadf) -> <<"bad file number">>; +message_1(ebadfd) -> <<"file descriptor in bad state">>; +message_1(ebadmsg) -> <<"not a data message">>; +message_1(ebadr) -> <<"bad request descriptor">>; +message_1(ebadrpc) -> <<"RPC structure is bad">>; +message_1(ebadrqc) -> <<"bad request code">>; +message_1(ebadslt) -> <<"invalid slot">>; +message_1(ebfont) -> <<"bad font file format">>; +message_1(ebusy) -> <<"file busy">>; +message_1(echild) -> <<"no children">>; +message_1(echrng) -> <<"channel number out of range">>; +message_1(ecomm) -> <<"communication error on send">>; +message_1(econnaborted) -> <<"software caused connection abort">>; +message_1(econnrefused) -> <<"connection refused">>; +message_1(econnreset) -> <<"connection reset by peer">>; +message_1(edeadlk) -> <<"resource deadlock avoided">>; +message_1(edeadlock) -> <<"resource deadlock avoided">>; +message_1(edestaddrreq) -> <<"destination address required">>; +message_1(edirty) -> <<"mounting a dirty fs w/o force">>; +message_1(edom) -> <<"math argument out of range">>; +message_1(edotdot) -> <<"cross mount point">>; +message_1(edquot) -> <<"disk quota exceeded">>; +message_1(eduppkg) -> <<"duplicate package name">>; +message_1(eexist) -> <<"file already exists">>; +message_1(efault) -> <<"bad address in system call argument">>; +message_1(efbig) -> <<"file too large">>; +message_1(ehostdown) -> <<"host is down">>; +message_1(ehostunreach) -> <<"host is unreachable">>; +message_1(eidrm) -> <<"identifier removed">>; +message_1(einit) -> <<"initialization error">>; +message_1(einprogress) -> <<"operation now in progress">>; +message_1(eintr) -> <<"interrupted system call">>; +message_1(einval) -> <<"invalid argument">>; +message_1(eio) -> <<"I/O error">>; +message_1(eisconn) -> <<"socket is already connected">>; +message_1(eisdir) -> <<"illegal operation on a directory">>; +message_1(eisnam) -> <<"is a name file">>; +message_1(elbin) -> <<"ELBIN">>; +message_1(el2hlt) -> <<"level 2 halted">>; +message_1(el2nsync) -> <<"level 2 not synchronized">>; +message_1(el3hlt) -> <<"level 3 halted">>; +message_1(el3rst) -> <<"level 3 reset">>; +message_1(elibacc) -> <<"can not access a needed shared library">>; +message_1(elibbad) -> <<"accessing a corrupted shared library">>; +message_1(elibexec) -> <<"can not exec a shared library directly">>; +message_1(elibmax) -> + <<"attempting to link in more shared libraries than system limit">>; +message_1(elibscn) -> <<".lib section in a.out corrupted">>; +message_1(elnrng) -> <<"link number out of range">>; +message_1(eloop) -> <<"too many levels of symbolic links">>; +message_1(emfile) -> <<"too many open files">>; +message_1(emlink) -> <<"too many links">>; +message_1(emsgsize) -> <<"message too long">>; +message_1(emultihop) -> <<"multihop attempted">>; +message_1(enametoolong) -> <<"file name too long">>; +message_1(enavail) -> <<"not available">>; +message_1(enet) -> <<"ENET">>; +message_1(enetdown) -> <<"network is down">>; +message_1(enetreset) -> <<"network dropped connection on reset">>; +message_1(enetunreach) -> <<"network is unreachable">>; +message_1(enfile) -> <<"file table overflow">>; +message_1(enoano) -> <<"anode table overflow">>; +message_1(enobufs) -> <<"no buffer space available">>; +message_1(enocsi) -> <<"no CSI structure available">>; +message_1(enodata) -> <<"no data available">>; +message_1(enodev) -> <<"no such device">>; +message_1(enoent) -> <<"no such file or directory">>; +message_1(enoexec) -> <<"exec format error">>; +message_1(enolck) -> <<"no locks available">>; +message_1(enolink) -> <<"link has be severed">>; +message_1(enomem) -> <<"not enough memory">>; +message_1(enomsg) -> <<"no message of desired type">>; +message_1(enonet) -> <<"machine is not on the network">>; +message_1(enopkg) -> <<"package not installed">>; +message_1(enoprotoopt) -> <<"bad proocol option">>; +message_1(enospc) -> <<"no space left on device">>; +message_1(enosr) -> <<"out of stream resources or not a stream device">>; +message_1(enosym) -> <<"unresolved symbol name">>; +message_1(enosys) -> <<"function not implemented">>; +message_1(enotblk) -> <<"block device required">>; +message_1(enotconn) -> <<"socket is not connected">>; +message_1(enotdir) -> <<"not a directory">>; +message_1(enotempty) -> <<"directory not empty">>; +message_1(enotnam) -> <<"not a name file">>; +message_1(enotsock) -> <<"socket operation on non-socket">>; +message_1(enotsup) -> <<"operation not supported">>; +message_1(enotty) -> <<"inappropriate device for ioctl">>; +message_1(enotuniq) -> <<"name not unique on network">>; +message_1(enxio) -> <<"no such device or address">>; +message_1(eopnotsupp) -> <<"operation not supported on socket">>; +message_1(eperm) -> <<"not owner">>; +message_1(epfnosupport) -> <<"protocol family not supported">>; +message_1(epipe) -> <<"broken pipe">>; +message_1(eproclim) -> <<"too many processes">>; +message_1(eprocunavail) -> <<"bad procedure for program">>; +message_1(eprogmismatch) -> <<"program version wrong">>; +message_1(eprogunavail) -> <<"RPC program not available">>; +message_1(eproto) -> <<"protocol error">>; +message_1(eprotonosupport) -> <<"protocol not suppored">>; +message_1(eprototype) -> <<"protocol wrong type for socket">>; +message_1(erange) -> <<"math result unrepresentable">>; +message_1(erefused) -> <<"EREFUSED">>; +message_1(eremchg) -> <<"remote address changed">>; +message_1(eremdev) -> <<"remote device">>; +message_1(eremote) -> <<"pathname hit remote file system">>; +message_1(eremoteio) -> <<"remote i/o error">>; +message_1(eremoterelease) -> <<"EREMOTERELEASE">>; +message_1(erofs) -> <<"read-only file system">>; +message_1(erpcmismatch) -> <<"RPC version is wrong">>; +message_1(erremote) -> <<"object is remote">>; +message_1(eshutdown) -> <<"can't send after socket shutdown">>; +message_1(esocktnosupport) -> <<"socket type not supported">>; +message_1(espipe) -> <<"invalid seek">>; +message_1(esrch) -> <<"no such process">>; +message_1(esrmnt) -> <<"srmount error">>; +message_1(estale) -> <<"stale remote file handle">>; +message_1(esuccess) -> <<"Error 0">>; +message_1(etime) -> <<"timer expired">>; +message_1(etimedout) -> <<"connection timed out">>; +message_1(etoomanyrefs) -> <<"too many references: can't splice">>; +message_1(etxtbsy) -> <<"text file or pseudo-device busy">>; +message_1(euclean) -> <<"structure needs cleaning">>; +message_1(eunatch) -> <<"protocol driver not attached">>; +message_1(eusers) -> <<"too many users">>; +message_1(eversion) -> <<"version mismatch">>; +message_1(ewouldblock) -> <<"operation would block">>; +message_1(exdev) -> <<"cross-domain link">>; +message_1(exfull) -> <<"message tables full">>; +message_1(nxdomain) -> <<"non-existing domain">>; +message_1(_) -> <<"unknown POSIX error">>. diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index 0859bf0466..df4a20b833 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -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); @@ -174,12 +174,15 @@ ltype({atom,_,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) -> @@ -443,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), @@ -727,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. @@ -761,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 -> @@ -876,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} @@ -956,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); @@ -966,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 1013d54bdc..18f64c46d0 100644 --- a/lib/stdlib/src/erl_scan.erl +++ b/lib/stdlib/src/erl_scan.erl @@ -55,18 +55,13 @@ 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,6 +90,15 @@ -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) ++ @@ -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}; + try lists:keyfind(Item, 1, Attrs) of + {_Item, _Value} = T -> + T; false -> - undefined; - _ -> + 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 @@ -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)}; diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl index d26443f277..7cb02afb11 100644 --- a/lib/stdlib/src/escript.erl +++ b/lib/stdlib/src/escript.erl @@ -24,35 +24,41 @@ %% Internal API. -export([start/0, start/1]). --include_lib("kernel/include/file.hrl"). +%%----------------------------------------------------------------------- -define(SHEBANG, "/usr/bin/env escript"). -define(COMMENT, "This is an -*- erlang -*- file"). --record(state, {file, - module, +%%----------------------------------------------------------------------- + +-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}). --record(sections, {type, - shebang, - comment, - emu_args, - body}). --record(extract_options, {compile_source}). + 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(). --type escript_filename() :: string(). --type filename() :: string(). + +-record(sections, {type, + shebang :: shebang(), + comment :: comment(), + emu_args :: emu_args(), + body}). + +-record(extract_options, {compile_source}). + -type zip_file() :: - filename() - | {filename(), binary()} - | {filename(), binary(), #file_info{}}. + file:filename() + | {file:filename(), binary()} + | {file:filename(), binary(), file:file_info()}. -type zip_create_option() :: term(). -type section() :: shebang @@ -60,13 +66,15 @@ | comment | {comment, comment()} | {emu_args, emu_args()} - | {source, filename() | binary()} - | {beam, filename() | binary()} - | {archive, filename() | binary()} + | {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(escript_filename() | binary, [section()]) -> +-spec create(file:filename() | binary, [section()]) -> ok | {ok, binary()} | {error, term()}. create(File, Options) when is_list(Options) -> @@ -149,7 +157,9 @@ prepare(BadOptions, _) -> -type section_name() :: shebang | comment | emu_args | body . -type extract_option() :: compile_source | {section, [section_name()]}. --spec extract(filename(), [extract_option()]) -> {ok, [section()]} | {error, term()}. +-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, @@ -239,6 +249,7 @@ normalize_section(Name, Chars) -> {Name, Chars}. -spec script_name() -> string(). + script_name() -> [ScriptName|_] = init:get_plain_arguments(), ScriptName. @@ -248,10 +259,12 @@ script_name() -> %% -spec start() -> no_return(). + start() -> start([]). -spec start([string()]) -> no_return(). + start(EscriptOptions) -> try %% Commands run using -run or -s are run in a process @@ -484,18 +497,12 @@ find_first_body_line(Fd, HeaderSz0, LineNo, KeepFirst, Sections) -> classify_line(Line) -> case Line of - [$\#, $\! | _] -> - shebang; - [$P, $K | _] -> - archive; - [$F, $O, $R, $1 | _] -> - beam; - [$%, $%, $\! | _] -> - emu_args; - [$% | _] -> - comment; - _ -> - undefined + "#!" ++ _ -> shebang; + "PK" ++ _ -> archive; + "FOR1" ++ _ -> beam; + "%%!" ++ _ -> emu_args; + "%" ++ _ -> comment; + _ -> undefined end. guess_type(Line) -> @@ -531,7 +538,6 @@ parse_archive(S, File, HeaderSz) -> end, list_to_atom(lists:reverse(RevBase2)) end, - S#state{source = archive, mode = run, module = Mod, @@ -564,9 +570,7 @@ parse_beam(S, File, HeaderSz, CheckOnly) -> forms_or_bin = Bin} end; {error, beam_lib, Reason} when is_tuple(Reason) -> - fatal(element(1, Reason)); - {error, beam_lib, Reason} -> - fatal(Reason) + fatal(element(1, Reason)) end. parse_source(S, File, Fd, StartLine, HeaderSz, CheckOnly) -> @@ -587,8 +591,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), @@ -683,8 +687,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. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl index d7b5dbc636..6e6e949e2c 100644 --- a/lib/stdlib/src/ets.erl +++ b/lib/stdlib/src/ets.erl @@ -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). @@ -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 -> @@ -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([]) -> @@ -855,19 +862,28 @@ load_table(ReadFun, State, Tab) -> load_table(ReadFun, NewState, Tab) end. -create_tab(I) -> +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), - try - Tab = ets:new(Name, [Type, P, Keypos | named_table(Val)]), - {ok, Tab, Sz} - catch - _:_ -> - throw(cannot_create_table) + 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]; 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 5c5e084e17..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]). @@ -45,14 +47,14 @@ wildcard(Pattern) when is_list(Pattern) -> ?HANDLE_ERROR(do_wildcard(Pattern, file)). -spec wildcard(file:name(), file:name() | atom()) -> [file:filename()]. -wildcard(Pattern, Cwd) when is_list(Pattern), is_list(Cwd) -> +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(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(file:name()) -> boolean(). @@ -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. @@ -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 01c06e4596..24abf1e977 100644 --- a/lib/stdlib/src/filename.erl +++ b/lib/stdlib/src/filename.erl @@ -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(file:name()) -> string(). + +-spec absname(file:name()) -> file:filename(). absname(Name) -> {ok, Cwd} = file:get_cwd(), absname(Name, Cwd). --spec absname(file: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(), file: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,18 +131,40 @@ absname_pretty(Abspath, [First|Rest], AbsBase) -> %% basename("/usr/foo/") -> "foo" (trailing slashes ignored) %% basename("/") -> [] --spec basename(file: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) -> basename1(Rest, [], DirSep2); -basename1([[_|_]=List|Rest], Tail, DirSep2) -> - basename1(List++Rest, Tail, DirSep2); basename1([DirSep2|Rest], Tail, DirSep2) when is_integer(DirSep2) -> basename1([$/|Rest], Tail, DirSep2); basename1([Char|Rest], Tail, DirSep2) when is_integer(Char) -> @@ -155,26 +172,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_prefix1([L], _) when is_integer(L) -> - [L]; -skip_prefix1(Name, _) -> +skip_prefix([L, DrvSep|Name], DrvSep) when ?IS_DRIVELETTER(L) -> + Name; +skip_prefix(Name, _) -> Name. %% Returns the last component of the filename, with the given @@ -190,7 +192,29 @@ skip_prefix1(Name, _) -> %% rootname(basename("xxx.jam")) -> "xxx" %% rootname(basename("xxx.erl")) -> "xxx" --spec basename(file:name(), file: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 +228,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,24 +240,44 @@ basename([], _Ext, Tail, _DrvSep2) -> %% Example: dirname("/usr/src/kalle.erl") -> "/usr/src", %% dirname("kalle.erl") -> "." --spec dirname(file: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); dirname([$/|Rest], Dir, File, Seps) -> dirname(Rest, File++Dir, [$/], Seps); dirname([DirSep|Rest], Dir, File, {DirSep,_}=Seps) when is_integer(DirSep) -> @@ -258,6 +302,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 +332,29 @@ dirname([], Dir, _, _) -> %% %% On Windows: fn:dirname("\\usr\\src/kalle.erl") -> "/usr/src" --spec extension(file: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 + [] -> + <<>>; + 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 +367,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 +374,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 +418,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 +439,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 +472,13 @@ 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(), file:name()) -> string(). +-spec append(file:filename(), file:name()) -> file:filename(). +append(Dir, Name) when is_binary(Dir), is_binary(Name) -> + <<Dir/binary,$/:8,Name/binary>>; +append(Dir, Name) when is_binary(Dir) -> + append(Dir,filename_string_to_binary(Name)); +append(Dir, Name) when is_binary(Name) -> + append(filename_string_to_binary(Dir),Name); append(Dir, Name) -> Dir ++ [$/|Name]. @@ -376,19 +497,14 @@ append(Dir, Name) -> -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(file: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(file:name(), file: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(file: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]) -> @@ -528,8 +685,6 @@ split([$/|Rest], Comp, Components, OsType) -> split(Rest, [], [lists:reverse(Comp)|Components], OsType); split([Char|Rest], Comp, Components, OsType) when is_integer(Char) -> split(Rest, [Char|Comp], Components, OsType); -split([List|Rest], Comp, Components, OsType) when is_list(List) -> - split(List++Rest, Comp, Components, OsType); split([], [], Components, _OsType) -> lists:reverse(Components); split([], Comp, Components, OsType) -> @@ -540,7 +695,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 +712,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 +887,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(file:name()) -> string(). +-spec flatten(file:name()) -> file:filename(). +flatten(Bin) when is_binary(Bin) -> + Bin; flatten(List) -> do_flatten(List, []). @@ -785,3 +906,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 27ff9441e6..b1e9e3a02f 100644 --- a/lib/stdlib/src/gen_event.erl +++ b/lib/stdlib/src/gen_event.erl @@ -677,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; @@ -711,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 9961646418..7d9960b912 100644 --- a/lib/stdlib/src/gen_fsm.erl +++ b/lib/stdlib/src/gen_fsm.erl @@ -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 1c9e5270b6..ac81df9cab 100644 --- a/lib/stdlib/src/gen_server.erl +++ b/lib/stdlib/src/gen_server.erl @@ -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..3efa68ca09 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,12 +32,15 @@ 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]). %%------------------------------------------------------------------------- -type device() :: atom() | pid(). -type prompt() :: atom() | string(). +-type error_description() :: term(). % Whatever the io-server sends. +-type request_error() :: {'error',error_description()}. %% XXX: Some uses of line() in this file may need to read erl_scan:location() -type line() :: pos_integer(). @@ -52,26 +55,12 @@ to_tuple(T) when is_tuple(T) -> T; to_tuple(T) -> {T}. -%% Problem: the variables Other, Name and Args may collide with surrounding -%% ones. -%% Give extra args to macro, being the variables to use. --define(O_REQUEST(Io, Request), - case request(Io, Request) of - {error, Reason} -> - [Name | Args] = tuple_to_list(to_tuple(Request)), - erlang:error(conv_reason(Name, Reason), [Name, Io | Args]); - Other -> - Other - end). - o_request(Io, Request, Func) -> case request(Io, Request) of {error, Reason} -> [_Name | Args] = tuple_to_list(to_tuple(Request)), - {'EXIT',{undef,[_Current|Mfas]}} = (catch erlang:error(undef)), - MFA = {io, Func, [Io | Args]}, - exit({conv_reason(Func, Reason),[MFA|Mfas]}); -% erlang:error(conv_reason(Name, Reason), [Name, Io | Args]); + {'EXIT',{get_stacktrace,[_Current|Mfas]}} = (catch erlang:error(get_stacktrace)), + erlang:raise(error, conv_reason(Func, Reason), [{io, Func, [Io | Args]}|Mfas]); Other -> Other end. @@ -298,32 +287,32 @@ format(Io, Format, Args) -> %% Scanning Erlang code. --spec scan_erl_exprs(prompt()) -> erl_scan:tokens_result(). +-spec scan_erl_exprs(prompt()) -> erl_scan:tokens_result() | request_error(). scan_erl_exprs(Prompt) -> scan_erl_exprs(default_input(), Prompt, 1). --spec scan_erl_exprs(device(), prompt()) -> erl_scan:tokens_result(). +-spec scan_erl_exprs(device(), prompt()) -> erl_scan:tokens_result() | request_error(). scan_erl_exprs(Io, Prompt) -> scan_erl_exprs(Io, Prompt, 1). --spec scan_erl_exprs(device(), prompt(), line()) -> erl_scan:tokens_result(). +-spec scan_erl_exprs(device(), prompt(), line()) -> erl_scan:tokens_result() | request_error(). scan_erl_exprs(Io, Prompt, Pos0) -> request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}). --spec scan_erl_form(prompt()) -> erl_scan:tokens_result(). +-spec scan_erl_form(prompt()) -> erl_scan:tokens_result() | request_error(). scan_erl_form(Prompt) -> scan_erl_form(default_input(), Prompt, 1). --spec scan_erl_form(device(), prompt()) -> erl_scan:tokens_result(). +-spec scan_erl_form(device(), prompt()) -> erl_scan:tokens_result() | request_error(). scan_erl_form(Io, Prompt) -> scan_erl_form(Io, Prompt, 1). --spec scan_erl_form(device(), prompt(), line()) -> erl_scan:tokens_result(). +-spec scan_erl_form(device(), prompt(), line()) -> erl_scan:tokens_result() | request_error(). scan_erl_form(Io, Prompt, Pos0) -> request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}). @@ -334,7 +323,8 @@ scan_erl_form(Io, Prompt, Pos0) -> -type parse_ret() :: {'ok', erl_parse_expr_list(), line()} | {'eof', line()} - | {'error', erl_scan:error_info(), line()}. + | {'error', erl_scan:error_info(), line()} + | request_error(). -spec parse_erl_exprs(prompt()) -> parse_ret(). @@ -363,7 +353,8 @@ parse_erl_exprs(Io, Prompt, Pos0) -> -type parse_form_ret() :: {'ok', erl_parse_absform(), line()} | {'eof', line()} - | {'error', erl_scan:error_info(), line()}. + | {'error', erl_scan:error_info(), line()} + | request_error(). -spec parse_erl_form(prompt()) -> parse_form_ret(). 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 857eda8161..c669c1f7c1 100644 --- a/lib/stdlib/src/lists.erl +++ b/lib/stdlib/src/lists.erl @@ -18,6 +18,9 @@ %% -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, @@ -347,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, []). @@ -355,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); @@ -367,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); @@ -379,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; @@ -391,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; @@ -417,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). @@ -437,7 +440,7 @@ do_flatten([], Tail) -> %% 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). @@ -478,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). @@ -488,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). @@ -499,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, []). @@ -510,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). @@ -521,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 @@ -579,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 @@ -594,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 @@ -606,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 @@ -672,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 @@ -687,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 @@ -699,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)]; @@ -722,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, []), []); @@ -731,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, []), []); @@ -765,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; @@ -774,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; @@ -839,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). @@ -848,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); @@ -861,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); @@ -873,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; @@ -886,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; @@ -949,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)); @@ -995,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), @@ -1010,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 f3cfd78d54..5c52dfcbf0 100644 --- a/lib/stdlib/src/otp_internal.erl +++ b/lib/stdlib/src/otp_internal.erl @@ -349,14 +349,24 @@ obsolete_1(ssh_sftp, stop, 1) -> %% 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},"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/re.erl b/lib/stdlib/src/re.erl index 296a6b3d23..9642de17b4 100644 --- a/lib/stdlib/src/re.erl +++ b/lib/stdlib/src/re.erl @@ -208,29 +208,25 @@ replace(Subject,RE,Replacement,Options) -> process_repl_params(Options,iodata,false), FlatSubject = to_binary(Subject, Unicode), FlatReplacement = to_binary(Replacement, Unicode), - case do_replace(FlatSubject,Subject,RE,FlatReplacement,NewOpt) of - {error,_Err} -> - throw(badre); - IoList -> - case Convert of - iodata -> - IoList; - binary -> - case Unicode of - false -> - iolist_to_binary(IoList); - true -> - unicode:characters_to_binary(IoList,unicode) - end; - list -> - case Unicode of - false -> - binary_to_list(iolist_to_binary(IoList)); - true -> - unicode:characters_to_list(IoList,unicode) - end - end - end + IoList = do_replace(FlatSubject,Subject,RE,FlatReplacement,NewOpt), + case Convert of + iodata -> + IoList; + binary -> + case Unicode of + false -> + iolist_to_binary(IoList); + true -> + unicode:characters_to_binary(IoList,unicode) + end; + list -> + case Unicode of + false -> + binary_to_list(iolist_to_binary(IoList)); + true -> + unicode:characters_to_list(IoList,unicode) + end + end catch throw:badopt -> erlang:error(badarg,[Subject,RE,Replacement,Options]); 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/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/base64_SUITE.erl b/lib/stdlib/test/base64_SUITE.erl index 44742063b3..d8bb2dfb60 100644 --- a/lib/stdlib/test/base64_SUITE.erl +++ b/lib/stdlib/test/base64_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. 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,6 @@ %% -module(base64_SUITE). --author('[email protected]'). -include("test_server.hrl"). -include("test_server_line.hrl"). @@ -29,7 +28,7 @@ %% Test cases must be exported. -export([base64_encode/1, base64_decode/1, base64_otp_5635/1, base64_otp_6279/1, big/1, illegal/1, mime_decode/1, - roundtrip/1]). + mime_decode_to_string/1, roundtrip/1]). init_per_testcase(_, Config) -> Dog = test_server:timetrap(?t:minutes(2)), @@ -50,7 +49,7 @@ all(doc) -> all(suite) -> [base64_encode, base64_decode, base64_otp_5635, base64_otp_6279, big, illegal, mime_decode, - roundtrip]. + mime_decode_to_string, roundtrip]. %%------------------------------------------------------------------------- @@ -59,7 +58,7 @@ base64_encode(doc) -> base64_encode(suite) -> []; base64_encode(Config) when is_list(Config) -> - %% Two pads + %% Two pads <<"QWxhZGRpbjpvcGVuIHNlc2FtZQ==">> = base64:encode("Aladdin:open sesame"), %% One pad @@ -77,8 +76,8 @@ base64_decode(doc) -> base64_decode(suite) -> []; base64_decode(Config) when is_list(Config) -> - %% Two pads - <<"Aladdin:open sesame">> = + %% Two pads + <<"Aladdin:open sesame">> = base64:decode("QWxhZGRpbjpvcGVuIHNlc2FtZQ=="), %% One pad <<"Hello World">> = base64:decode(<<"SGVsbG8gV29ybGQ=">>), @@ -138,20 +137,85 @@ illegal(Config) when is_list(Config) -> {'EXIT',{function_clause, _}} = (catch base64:decode("()")), ok. %%------------------------------------------------------------------------- +%% mime_decode and mime_decode_to_string have different implementations +%% so test both with the same input separately. Both functions have +%% the same implementation for binary/string arguments. mime_decode(doc) -> ["Test base64:mime_decode/1."]; mime_decode(suite) -> []; mime_decode(Config) when is_list(Config) -> - %% Two pads - <<"Aladdin:open sesame">> = + %% Test correct padding + <<"one">> = base64:mime_decode(<<"b25l">>), + <<"on">> = base64:mime_decode(<<"b24=">>), + <<"o">> = base64:mime_decode(<<"bw==">>), + %% Test 1 extra padding + <<"one">> = base64:mime_decode(<<"b25l= =">>), + <<"on">> = base64:mime_decode(<<"b24== =">>), + <<"o">> = base64:mime_decode(<<"bw=== =">>), + %% Test 2 extra padding + <<"one">> = base64:mime_decode(<<"b25l===">>), + <<"on">> = base64:mime_decode(<<"b24====">>), + <<"o">> = base64:mime_decode(<<"bw=====">>), + %% Test misc embedded padding + <<"one">> = base64:mime_decode(<<"b2=5l===">>), + <<"on">> = base64:mime_decode(<<"b=24====">>), + <<"o">> = base64:mime_decode(<<"b=w=====">>), + %% Test misc white space and illegals with embedded padding + <<"one">> = base64:mime_decode(<<" b~2=\r\n5()l===">>), + <<"on">> = base64:mime_decode(<<"\tb =2\"�4=�= ==">>), + <<"o">> = base64:mime_decode(<<"\nb=w=====">>), + %% Two pads + <<"Aladdin:open sesame">> = base64:mime_decode("QWxhZGRpbjpvc()GVuIHNlc2FtZQ=="), - %% One pad, followed by ignored text - <<"Hello World">> = base64:mime_decode(<<"SGVsb)(G8gV29ybGQ=apa">>), + %% One pad to ignore, followed by more text + <<"Hello World!!">> = base64:mime_decode(<<"SGVsb)(G8gV29ybGQ=h IQ= =">>), + %% No pad + <<"Aladdin:open sesam">> = + base64:mime_decode("QWxhZGRpbjpvcG�\")(VuIHNlc2Ft"), + %% Encoded base 64 strings may be divided by non base 64 chars. + %% In this cases whitespaces. + <<"0123456789!@#0^&*();:<>,. []{}">> = + base64:mime_decode( + <<"MDEy MzQ1Njc4 \tOSFAIzBeJ \nio)(oKTs6 PD4sLi \r\nBbXXt9">>), + ok. + +%%------------------------------------------------------------------------- + +%% Repeat of mime_decode() tests +mime_decode_to_string(doc) -> + ["Test base64:mime_decode_to_string/1."]; +mime_decode_to_string(suite) -> + []; +mime_decode_to_string(Config) when is_list(Config) -> + %% Test correct padding + "one" = base64:mime_decode_to_string(<<"b25l">>), + "on" = base64:mime_decode_to_string(<<"b24=">>), + "o" = base64:mime_decode_to_string(<<"bw==">>), + %% Test 1 extra padding + "one" = base64:mime_decode_to_string(<<"b25l= =">>), + "on" = base64:mime_decode_to_string(<<"b24== =">>), + "o" = base64:mime_decode_to_string(<<"bw=== =">>), + %% Test 2 extra padding + "one" = base64:mime_decode_to_string(<<"b25l===">>), + "on" = base64:mime_decode_to_string(<<"b24====">>), + "o" = base64:mime_decode_to_string(<<"bw=====">>), + %% Test misc embedded padding + "one" = base64:mime_decode_to_string(<<"b2=5l===">>), + "on" = base64:mime_decode_to_string(<<"b=24====">>), + "o" = base64:mime_decode_to_string(<<"b=w=====">>), + %% Test misc white space and illegals with embedded padding + "one" = base64:mime_decode_to_string(<<" b~2=\r\n5()l===">>), + "on" = base64:mime_decode_to_string(<<"\tb =2\"�4=�= ==">>), + "o" = base64:mime_decode_to_string(<<"\nb=w=====">>), + %% Two pads + "Aladdin:open sesame" = + base64:mime_decode_to_string("QWxhZGRpbjpvc()GVuIHNlc2FtZQ=="), + %% One pad to ignore, followed by more text + "Hello World!!" = base64:mime_decode_to_string(<<"SGVsb)(G8gV29ybGQ=h IQ= =">>), %% No pad "Aladdin:open sesam" = base64:mime_decode_to_string("QWxhZGRpbjpvcG�\")(VuIHNlc2Ft"), - %% Encoded base 64 strings may be divided by non base 64 chars. %% In this cases whitespaces. "0123456789!@#0^&*();:<>,. []{}" = @@ -159,6 +223,7 @@ mime_decode(Config) when is_list(Config) -> <<"MDEy MzQ1Njc4 \tOSFAIzBeJ \nio)(oKTs6 PD4sLi \r\nBbXXt9">>), ok. +%%------------------------------------------------------------------------- roundtrip(Config) when is_list(Config) -> Sizes = lists:seq(1, 255) ++ lists:seq(2400-5, 2440), 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 4806b5d361..e9fb932632 100644 --- a/lib/stdlib/test/epp_SUITE.erl +++ b/lib/stdlib/test/epp_SUITE.erl @@ -19,12 +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_8470/1, otp_8503/1, - otp_8562/1]). + otp_8562/1, otp_8665/1, otp_8911/1]). -export([epp_parse_erl_form/2]). @@ -39,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"). @@ -64,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, otp_8470, otp_8503, otp_8562]. + 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)."]; @@ -192,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. @@ -219,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), @@ -302,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)), @@ -333,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'}}]}, @@ -441,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, ''), @@ -494,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]), @@ -506,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,_}, _, _, @@ -637,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" @@ -681,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">>, @@ -775,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"), @@ -788,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", @@ -815,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", [], []), @@ -941,7 +941,7 @@ ifdef(Config) -> <<"\n-if.\n" "-endif.\n">>, {errors,[{{2,2},epp,{'NYI','if'}}],[]}}, - + {define_c7, <<"-ifndef(a).\n" "-elif.\n" @@ -1197,6 +1197,52 @@ otp_8562(Config) when is_list(Config) -> ?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). @@ -1213,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() @@ -1228,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. @@ -1245,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 [] -> []; @@ -1289,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_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl index c60a558fa1..254ce0095d 100644 --- a/lib/stdlib/test/erl_eval_SUITE.erl +++ b/lib/stdlib/test/erl_eval_SUITE.erl @@ -1,7 +1,7 @@ %% %% %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 @@ -38,7 +38,8 @@ otp_8133/1, funs/1, try_catch/1, - eval_expr_5/1]). + eval_expr_5/1, + zero_width/1]). %% %% Define to run outside of test server @@ -76,7 +77,8 @@ all(suite) -> [guard_1, guard_2, match_pattern, string_plusplus, pattern_expr, match_bin, guard_3, guard_4, lc, simple_cases, unary_plus, apply_atom, otp_5269, otp_6539, otp_6543, - otp_6787, otp_6977, otp_7550, otp_8133, funs, try_catch, eval_expr_5]. + otp_6787, otp_6977, otp_7550, otp_8133, funs, try_catch, eval_expr_5, + zero_width]. guard_1(doc) -> ["(OTP-2405)"]; @@ -1326,6 +1328,14 @@ eval_expr_5(Config) when is_list(Config) -> ok end. +zero_width(Config) when is_list(Config) -> + ?line check(fun() -> + {'EXIT',{badarg,_}} = (catch <<not_a_number:0>>), + ok + end, "begin {'EXIT',{badarg,_}} = (catch <<not_a_number:0>>), " + "ok end.", ok), + ok. + %% Check the string in different contexts: as is; in fun; from compiled code. check(F, String, Result) -> check1(F, String, Result), diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 8581b496aa..c4158abf32 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2010. All Rights Reserved. +%% Copyright Ericsson AB 1999-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -54,7 +54,8 @@ otp_7550/1, otp_8051/1, format_warn/1, - on_load/1, on_load_successful/1, on_load_failing/1 + on_load/1, on_load_successful/1, on_load_failing/1, + too_many_arguments/1 ]). % Default timetrap timeout (set in init_per_testcase). @@ -77,7 +78,7 @@ all(suite) -> otp_5362, otp_5371, otp_7227, otp_5494, otp_5644, otp_5878, otp_5917, otp_6585, otp_6885, export_all, bif_clash, behaviour_basic, behaviour_multiple, otp_7550, otp_8051, format_warn, - on_load]. + on_load,too_many_arguments]. unused_vars_warn(suite) -> [unused_vars_warn_basic, unused_vars_warn_lc, unused_vars_warn_rec, @@ -1784,6 +1785,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 +1799,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 +1811,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 +1825,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 +1841,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 +1870,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 +2253,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 +2408,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 +2419,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. @@ -2714,6 +2914,21 @@ on_load_failing(Config) when is_list(Config) -> ?line [] = run(Config, Ts), ok. +too_many_arguments(doc) -> + "Test that too many arguments is not accepted."; +too_many_arguments(suite) -> []; +too_many_arguments(Config) when is_list(Config) -> + Ts = [{too_many_1, + <<"f(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_) -> ok.">>, + [], + {errors, + [{1,erl_lint,{too_many_arguments,256}}],[]}} + ], + + ?line [] = run(Config, Ts), + ok. + + run(Config, Tests) -> F = fun({N,P,Ws,E}, BadL) -> case catch run_test(Config, P, Ws) of diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index 66730b7b94..c57541fba9 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -46,7 +46,7 @@ neg_indent/1, tickets/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_8473/1, otp_8522/1, otp_8567/1, otp_8664/1]). %% Internal export. -export([ehook/6]). @@ -765,7 +765,7 @@ neg_indent(Config) when is_list(Config) -> tickets(suite) -> [otp_6321, otp_6911, otp_6914, otp_8150, otp_8238, otp_8473, otp_8522, - otp_8567]. + otp_8567, otp_8664]. otp_6321(doc) -> "OTP_6321. Bug fix of exprs()."; @@ -995,6 +995,38 @@ otp_8567(Config) when is_list(Config) -> 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) -> diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl index afeb67eeb1..32eb97bc92 100644 --- a/lib/stdlib/test/erl_scan_SUITE.erl +++ b/lib/stdlib/test/erl_scan_SUITE.erl @@ -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'] , + 'or', 'xor'], [begin ?line {RW, true} = {RW, erl_scan:reserved_word(RW)}, S = atom_to_list(RW), @@ -244,6 +244,9 @@ punctuations() -> {'\\',1},{'^',1},{'`',1},{'~',1}], ?line test_string("#&*+/:<>?@\\^`~", PTs2), + ?line test_string(".. ", [{'..',1}]), + ?line test("1 .. 2"), + ?line test_string("...", [{'...',1}]), ok. comments() -> diff --git a/lib/stdlib/test/escript_SUITE.erl b/lib/stdlib/test/escript_SUITE.erl index 77fd190e45..162ca6006f 100644 --- a/lib/stdlib/test/escript_SUITE.erl +++ b/lib/stdlib/test/escript_SUITE.erl @@ -31,6 +31,7 @@ epp/1, create_and_extract/1, foldl/1, + overflow/1, verify_sections/3 ]). @@ -48,7 +49,8 @@ all(suite) -> archive_script, epp, create_and_extract, - foldl + foldl, + overflow ]. init_per_testcase(_Case, Config) -> @@ -736,6 +738,17 @@ emulate_escript_foldl(Fun, Acc, File) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +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 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..4e789790f6 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) -> @@ -5134,7 +5325,29 @@ my_tab_to_list(_Ts,'$end_of_table', Acc) -> lists:reverse(Acc); my_tab_to_list(Ts,Key, Acc) -> my_tab_to_list(Ts,ets:next(Ts,Key),[ets:lookup(Ts, Key)| Acc]). +wait_for_all_schedulers_online_to_execute() -> + PMs = lists:map(fun (Sched) -> + spawn_opt(fun () -> ok end, + [monitor, {scheduler, Sched}]) + end, + lists:seq(1,erlang:system_info(schedulers_online))), + lists:foreach(fun ({P, M}) -> + receive + {'DOWN', M, process, P, _} -> ok + end + end, + PMs), + ok. + etsmem() -> + %% Wait until it is guaranteed that all already scheduled + %% deallocations of DbTable structures have completed. + wait_for_all_schedulers_online_to_execute(), + + 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 +5366,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 +5381,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 +5549,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 +5771,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/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/vsn.mk b/lib/stdlib/vsn.mk index f081bd60e2..ac02e1f359 100644 --- a/lib/stdlib/vsn.mk +++ b/lib/stdlib/vsn.mk @@ -1,21 +1 @@ -# -# %CopyrightBegin% -# -# 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% -# - -STDLIB_VSN = 1.17 - +STDLIB_VSN = 1.17.3 |