diff options
161 files changed, 2934 insertions, 1494 deletions
diff --git a/bootstrap/lib/compiler/ebin/beam_bsm.beam b/bootstrap/lib/compiler/ebin/beam_bsm.beam Binary files differindex 6fc683bf76..a6757dfcfb 100644 --- a/bootstrap/lib/compiler/ebin/beam_bsm.beam +++ b/bootstrap/lib/compiler/ebin/beam_bsm.beam diff --git a/bootstrap/lib/compiler/ebin/beam_dead.beam b/bootstrap/lib/compiler/ebin/beam_dead.beam Binary files differindex 0bd9767208..6ae6de1d8b 100644 --- a/bootstrap/lib/compiler/ebin/beam_dead.beam +++ b/bootstrap/lib/compiler/ebin/beam_dead.beam diff --git a/bootstrap/lib/compiler/ebin/beam_dict.beam b/bootstrap/lib/compiler/ebin/beam_dict.beam Binary files differindex ccab57cc81..17b3c40eda 100644 --- a/bootstrap/lib/compiler/ebin/beam_dict.beam +++ b/bootstrap/lib/compiler/ebin/beam_dict.beam diff --git a/bootstrap/lib/compiler/ebin/beam_jump.beam b/bootstrap/lib/compiler/ebin/beam_jump.beam Binary files differindex 136036ae39..8dd6375403 100644 --- a/bootstrap/lib/compiler/ebin/beam_jump.beam +++ b/bootstrap/lib/compiler/ebin/beam_jump.beam diff --git a/bootstrap/lib/compiler/ebin/beam_type.beam b/bootstrap/lib/compiler/ebin/beam_type.beam Binary files differindex 4094cec9e6..cb3bc1a8bd 100644 --- a/bootstrap/lib/compiler/ebin/beam_type.beam +++ b/bootstrap/lib/compiler/ebin/beam_type.beam diff --git a/bootstrap/lib/compiler/ebin/cerl_sets.beam b/bootstrap/lib/compiler/ebin/cerl_sets.beam Binary files differnew file mode 100644 index 0000000000..d16543cdbe --- /dev/null +++ b/bootstrap/lib/compiler/ebin/cerl_sets.beam diff --git a/bootstrap/lib/compiler/ebin/compiler.app b/bootstrap/lib/compiler/ebin/compiler.app index dfe077d5b9..c9633c1369 100644 --- a/bootstrap/lib/compiler/ebin/compiler.app +++ b/bootstrap/lib/compiler/ebin/compiler.app @@ -45,6 +45,7 @@ cerl, cerl_clauses, cerl_inline, + cerl_sets, cerl_trees, compile, core_scan, diff --git a/bootstrap/lib/compiler/ebin/sys_core_fold.beam b/bootstrap/lib/compiler/ebin/sys_core_fold.beam Binary files differindex 2ed29957b3..f97931d49c 100644 --- a/bootstrap/lib/compiler/ebin/sys_core_fold.beam +++ b/bootstrap/lib/compiler/ebin/sys_core_fold.beam diff --git a/bootstrap/lib/compiler/ebin/v3_codegen.beam b/bootstrap/lib/compiler/ebin/v3_codegen.beam Binary files differindex 7b33f9cbff..e53f0fcd12 100644 --- a/bootstrap/lib/compiler/ebin/v3_codegen.beam +++ b/bootstrap/lib/compiler/ebin/v3_codegen.beam diff --git a/bootstrap/lib/compiler/ebin/v3_kernel.beam b/bootstrap/lib/compiler/ebin/v3_kernel.beam Binary files differindex 5817c6ae58..f5cdbb6e40 100644 --- a/bootstrap/lib/compiler/ebin/v3_kernel.beam +++ b/bootstrap/lib/compiler/ebin/v3_kernel.beam diff --git a/bootstrap/lib/kernel/ebin/application_controller.beam b/bootstrap/lib/kernel/ebin/application_controller.beam Binary files differindex 9a7907cb38..c4fa46e33e 100644 --- a/bootstrap/lib/kernel/ebin/application_controller.beam +++ b/bootstrap/lib/kernel/ebin/application_controller.beam diff --git a/bootstrap/lib/kernel/ebin/code.beam b/bootstrap/lib/kernel/ebin/code.beam Binary files differindex 9a644b6b48..ac6cb538cd 100644 --- a/bootstrap/lib/kernel/ebin/code.beam +++ b/bootstrap/lib/kernel/ebin/code.beam diff --git a/bootstrap/lib/kernel/ebin/inet_dns.beam b/bootstrap/lib/kernel/ebin/inet_dns.beam Binary files differindex 0c5b6c73e1..1fb789ae81 100644 --- a/bootstrap/lib/kernel/ebin/inet_dns.beam +++ b/bootstrap/lib/kernel/ebin/inet_dns.beam diff --git a/bootstrap/lib/kernel/ebin/inet_parse.beam b/bootstrap/lib/kernel/ebin/inet_parse.beam Binary files differindex 6d28aa3c95..294afcea30 100644 --- a/bootstrap/lib/kernel/ebin/inet_parse.beam +++ b/bootstrap/lib/kernel/ebin/inet_parse.beam diff --git a/bootstrap/lib/kernel/ebin/user_drv.beam b/bootstrap/lib/kernel/ebin/user_drv.beam Binary files differindex 54cf78d6ce..726a130b55 100644 --- a/bootstrap/lib/kernel/ebin/user_drv.beam +++ b/bootstrap/lib/kernel/ebin/user_drv.beam diff --git a/bootstrap/lib/stdlib/ebin/erl_anno.beam b/bootstrap/lib/stdlib/ebin/erl_anno.beam Binary files differindex a07f730e99..4807dac5f9 100644 --- a/bootstrap/lib/stdlib/ebin/erl_anno.beam +++ b/bootstrap/lib/stdlib/ebin/erl_anno.beam diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam Binary files differindex 5a8bc2a7f1..a5722e5daa 100644 --- a/bootstrap/lib/stdlib/ebin/erl_lint.beam +++ b/bootstrap/lib/stdlib/ebin/erl_lint.beam diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index ea94a4e82b..98d05dc7de 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -1322,13 +1322,14 @@ <item> <p>Verbose.</p> </item> - <tag><c><![CDATA[+W w | i]]></c></tag> + <tag><c><![CDATA[+W w | i | e]]></c></tag> <item> <p>Sets the mapping of warning messages for <c><![CDATA[error_logger]]></c>. Messages sent to the error logger using one of the warning - routines can be mapped either to errors (default), warnings - (<c><![CDATA[+W w]]></c>), or info reports (<c><![CDATA[+W i]]></c>). The current - mapping can be retrieved using + routines can be mapped either to errors (<c><![CDATA[+W e]]></c>), + warnings (<c><![CDATA[+W w]]></c>), or info reports + (<c><![CDATA[+W i]]></c>). The default is warnings. + The current mapping can be retrieved using <c><![CDATA[error_logger:warning_map/0]]></c>. See <seealso marker="kernel:error_logger#warning_map/0">error_logger(3)</seealso> for further information.</p> diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 4bad8b253c..f64381c99d 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -461,8 +461,9 @@ ok independent environment with all its terms is valid until you explicitly invalidates it with <seealso marker="#enif_free_env">enif_free_env</seealso> or <c>enif_send</c>.</p> - <p>All elements of a list/tuple must belong to the same environment as the - list/tuple itself. Terms can be copied between environments with + <p>All contained terms of a list/tuple/map must belong to the same + environment as the list/tuple/map itself. Terms can be copied between + environments with <seealso marker="#enif_make_copy">enif_make_copy</seealso>.</p> </item> <tag><marker id="ErlNifFunc"/>ErlNifFunc</tag> @@ -564,11 +565,11 @@ typedef enum { <funcs> <func><name><ret>void *</ret><nametext>enif_alloc(size_t size)</nametext></name> - <fsummary>Allocate dynamic memory.</fsummary> + <fsummary>Allocate dynamic memory</fsummary> <desc><p>Allocate memory of <c>size</c> bytes. Return NULL if allocation failed.</p></desc> </func> <func><name><ret>int</ret><nametext>enif_alloc_binary(size_t size, ErlNifBinary* bin)</nametext></name> - <fsummary>Create a new binary.</fsummary> + <fsummary>Create a new binary</fsummary> <desc><p>Allocate a new binary of size <c>size</c> bytes. Initialize the structure pointed to by <c>bin</c> to refer to the allocated binary. The binary must either be released by @@ -595,7 +596,7 @@ typedef enum { <desc><p>Allocate a memory managed resource object of type <c>type</c> and size <c>size</c> bytes.</p></desc> </func> <func><name><ret>void</ret><nametext>enif_clear_env(ErlNifEnv* env)</nametext></name> - <fsummary>Clear an environment for reuse.</fsummary> + <fsummary>Clear an environment for reuse</fsummary> <desc><p>Free all terms in an environment and clear it for reuse. The environment must have been allocated with <seealso marker="#enif_alloc_env">enif_alloc_env</seealso>. </p></desc> @@ -683,14 +684,14 @@ typedef enum { <c>size-1</c>.</p></desc> </func> <func><name><ret>int</ret><nametext>enif_get_atom_length(ErlNifEnv* env, ERL_NIF_TERM term, unsigned* len, ErlNifCharEncoding encode)</nametext></name> - <fsummary>Get the length of atom <c>term</c>.</fsummary> + <fsummary>Get the length of atom <c>term</c></fsummary> <desc><p>Set <c>*len</c> to the length (number of bytes excluding terminating null character) of the atom <c>term</c> with encoding <c>encode</c>. Return true on success or false if <c>term</c> is not an atom.</p></desc> </func> <func><name><ret>int</ret><nametext>enif_get_double(ErlNifEnv* env, ERL_NIF_TERM term, double* dp)</nametext></name> - <fsummary>Read a floating-point number term.</fsummary> + <fsummary>Read a floating-point number term</fsummary> <desc><p>Set <c>*dp</c> to the floating point value of <c>term</c>. Return true on success or false if <c>term</c> is not a float.</p></desc> </func> @@ -719,17 +720,28 @@ typedef enum { non-empty list.</p></desc> </func> <func><name><ret>int</ret><nametext>enif_get_list_length(ErlNifEnv* env, ERL_NIF_TERM term, unsigned* len)</nametext></name> - <fsummary>Get the length of list <c>term</c>.</fsummary> + <fsummary>Get the length of list <c>term</c></fsummary> <desc><p>Set <c>*len</c> to the length of list <c>term</c> and return true, or return false if <c>term</c> is not a list.</p></desc> </func> <func><name><ret>int</ret><nametext>enif_get_long(ErlNifEnv* env, ERL_NIF_TERM term, long int* ip)</nametext></name> - <fsummary>Read an long integer term.</fsummary> + <fsummary>Read an long integer term</fsummary> <desc><p>Set <c>*ip</c> to the long integer value of <c>term</c> and return true, or return false if <c>term</c> is not an integer or is outside the bounds of type <c>long int</c>.</p></desc> </func> - <func><name><ret>int</ret><nametext>enif_get_resource(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifResourceType* type, void** objp)</nametext></name> + <func><name><ret>int</ret><nametext>enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, size_t *size)</nametext></name> + <fsummary>Read the size of a map term</fsummary> + <desc><p>Set <c>*size</c> to the number of key-value pairs in the map <c>term</c> and + return true, or return false if <c>term</c> is not a map.</p></desc> + </func> + <func><name><ret>int</ret><nametext>enif_get_map_value(ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)</nametext></name> + <fsummary>Get the value of a key in a map</fsummary> + <desc><p>Set <c>*value</c> to the value associated with <c>key</c> in the + map <c>map</c> and return true. Return false if <c>map</c> is not a map + or if <c>map</c> does not contain <c>key</c>.</p></desc> + </func> + <func><name><ret>int</ret><nametext>enif_get_resource(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifResourceType* type, void** objp)</nametext></name> <fsummary>Get the pointer to a resource object</fsummary> <desc><p>Set <c>*objp</c> to point to the resource object referred to by <c>term</c>.</p> <p>Return true on success or false if <c>term</c> is not a handle to a resource object @@ -738,7 +750,7 @@ typedef enum { <func><name><ret>int</ret><nametext>enif_get_string(ErlNifEnv* env, ERL_NIF_TERM list, char* buf, unsigned size, ErlNifCharEncoding encode)</nametext></name> - <fsummary>Get a C-string from a list.</fsummary> + <fsummary>Get a C-string from a list</fsummary> <desc><p>Write a null-terminated string, in the buffer pointed to by <c>buf</c> with size <c>size</c>, consisting of the characters in the string <c>list</c>. The characters are written using encoding @@ -751,7 +763,7 @@ typedef enum { <c>size</c> is less than 1.</p></desc> </func> <func><name><ret>int</ret><nametext>enif_get_tuple(ErlNifEnv* env, ERL_NIF_TERM term, int* arity, const ERL_NIF_TERM** array)</nametext></name> - <fsummary>Inspect the elements of a tuple.</fsummary> + <fsummary>Inspect the elements of a tuple</fsummary> <desc><p>If <c>term</c> is a tuple, set <c>*array</c> to point to an array containing the elements of the tuple and set <c>*arity</c> to the number of elements. Note that the array @@ -761,28 +773,35 @@ typedef enum { tuple.</p></desc> </func> <func><name><ret>int</ret><nametext>enif_get_uint(ErlNifEnv* env, ERL_NIF_TERM term, unsigned int* ip)</nametext></name> - <fsummary>Read an unsigned integer term.</fsummary> + <fsummary>Read an unsigned integer term</fsummary> <desc><p>Set <c>*ip</c> to the unsigned integer value of <c>term</c> and return true, or return false if <c>term</c> is not an unsigned integer or is outside the bounds of type <c>unsigned int</c>.</p></desc> </func> <func><name><ret>int</ret><nametext>enif_get_uint64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifUInt64* ip)</nametext></name> - <fsummary>Read an unsigned 64-bit integer term.</fsummary> + <fsummary>Read an unsigned 64-bit integer term</fsummary> <desc><p>Set <c>*ip</c> to the unsigned integer value of <c>term</c> and return true, or return false if <c>term</c> is not an unsigned integer or is outside the bounds of an unsigned 64-bit integer.</p></desc> </func> <func><name><ret>int</ret><nametext>enif_get_ulong(ErlNifEnv* env, ERL_NIF_TERM term, unsigned long* ip)</nametext></name> - <fsummary>Read an unsigned integer term.</fsummary> + <fsummary>Read an unsigned integer term</fsummary> <desc><p>Set <c>*ip</c> to the unsigned long integer value of <c>term</c> and return true, or return false if <c>term</c> is not an unsigned integer or is outside the bounds of type <c>unsigned long</c>.</p></desc> </func> - <func><name><ret>int</ret><nametext>enif_has_pending_exception(ErlNifEnv* env)</nametext></name> - <fsummary>Check if an exception has been raised.</fsummary> + <func><name><ret>int</ret><nametext>enif_has_pending_exception(ErlNifEnv* env, ERL_NIF_TERM* reason)</nametext></name> + <fsummary>Check if an exception has been raised</fsummary> <desc><p>Return true if a pending exception is associated - with the environment <c>env</c>. The only possible exception is currently - <c>badarg</c> (see <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>).</p></desc> + with the environment <c>env</c>. If <c>reason</c> is a null pointer, ignore it. + Otherwise, if there's a pending exception associated with <c>env</c>, set the ERL_NIF_TERM + to which <c>reason</c> points to the value of the exception's term. For example, if + <seealso marker="#enif_make_badarg">enif_make_badarg</seealso> is called to set a + pending <c>badarg</c> exception, a subsequent call to <c>enif_has_pending_exception(env, &reason)</c> + will set <c>reason</c> to the atom <c>badarg</c>, then return true.</p> + <p>See also: <seealso marker="#enif_make_badarg">enif_make_badarg</seealso> + and <seealso marker="#enif_raise_exception">enif_raise_exception</seealso>.</p> + </desc> </func> <func><name><ret>int</ret><nametext>enif_inspect_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, ErlNifBinary* bin)</nametext></name> <fsummary>Inspect the content of a binary</fsummary> @@ -817,6 +836,10 @@ typedef enum { <fsummary>Determine if a term is an exception</fsummary> <desc><p>Return true if <c>term</c> is an exception.</p></desc> </func> + <func><name><ret>int</ret><nametext>enif_is_map(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> + <fsummary>Determine if a term is a map</fsummary> + <desc><p>Return true if <c>term</c> is a map, false otherwise.</p></desc> + </func> <func><name><ret>int</ret><nametext>enif_is_number(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> <fsummary>Determine if a term is a number (integer or float)</fsummary> <desc><p>Return true if <c>term</c> is a number.</p></desc> @@ -890,18 +913,19 @@ typedef enum { </p></desc> </func> <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_badarg(ErlNifEnv* env)</nametext></name> - <fsummary>Make a badarg exception.</fsummary> + <fsummary>Make a badarg exception</fsummary> <desc><p>Make a badarg exception to be returned from a NIF, and associate it with the environment <c>env</c>. Once a NIF or any function it calls invokes <c>enif_make_badarg</c>, the runtime ensures that a <c>badarg</c> exception is raised when the NIF returns, even if the NIF attempts to return a non-exception term instead. - The return value from <c>enif_make_badarg</c> may only be used as - return value from the NIF that invoked it (direct or indirectly) + The return value from <c>enif_make_badarg</c> may be used only as the + return value from the NIF that invoked it (directly or indirectly) or be passed to <seealso marker="#enif_is_exception">enif_is_exception</seealso>, but not to any other NIF API function.</p> - <p>See also: <seealso marker="#enif_has_pending_exception">enif_has_pending_exception</seealso>. + <p>See also: <seealso marker="#enif_has_pending_exception">enif_has_pending_exception</seealso> + and <seealso marker="#enif_raise_exception">enif_raise_exception</seealso> </p> <note><p>In earlier versions (older than erts-7.0, OTP 18) the return value from <c>enif_make_badarg</c> had to be returned from the NIF. This @@ -909,14 +933,14 @@ typedef enum { if <c>enif_make_badarg</c> has been invoked.</p></note></desc> </func> <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_binary(ErlNifEnv* env, ErlNifBinary* bin)</nametext></name> - <fsummary>Make a binary term.</fsummary> + <fsummary>Make a binary term</fsummary> <desc><p>Make a binary term from <c>bin</c>. Any ownership of the binary data will be transferred to the created term and <c>bin</c> should be considered read-only for the rest of the NIF call and then as released.</p></desc> </func> <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_copy(ErlNifEnv* dst_env, ERL_NIF_TERM src_term)</nametext></name> - <fsummary>Make a copy of a term.</fsummary> + <fsummary>Make a copy of a term</fsummary> <desc><p>Make a copy of term <c>src_term</c>. The copy will be created in environment <c>dst_env</c>. The source term may be located in any environment.</p></desc> @@ -957,7 +981,7 @@ typedef enum { <desc><p>Create an integer term from a signed 64-bit integer.</p></desc> </func> <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list(ErlNifEnv* env, unsigned cnt, ...)</nametext></name> - <fsummary>Create a list term.</fsummary> + <fsummary>Create a list term</fsummary> <desc><p>Create an ordinary list term of length <c>cnt</c>. Expects <c>cnt</c> number of arguments (after <c>cnt</c>) of type ERL_NIF_TERM as the elements of the list. An empty list is returned if <c>cnt</c> is 0.</p></desc> @@ -971,28 +995,21 @@ typedef enum { <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list7(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7)</nametext></name> <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list8(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8)</nametext></name> <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list9(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9)</nametext></name> - <fsummary>Create a list term.</fsummary> + <fsummary>Create a list term</fsummary> <desc><p>Create an ordinary list term with length indicated by the function name. Prefer these functions (macros) over the variadic <c>enif_make_list</c> to get a compile time error if the number of arguments does not match.</p></desc> </func> <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list_cell(ErlNifEnv* env, ERL_NIF_TERM head, ERL_NIF_TERM tail)</nametext></name> - <fsummary>Create a list cell.</fsummary> + <fsummary>Create a list cell</fsummary> <desc><p>Create a list cell <c>[head | tail]</c>.</p></desc> </func> <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list_from_array(ErlNifEnv* env, const ERL_NIF_TERM arr[], unsigned cnt)</nametext></name> - <fsummary>Create a list term from an array.</fsummary> + <fsummary>Create a list term from an array</fsummary> <desc><p>Create an ordinary list containing the elements of array <c>arr</c> of length <c>cnt</c>. An empty list is returned if <c>cnt</c> is 0.</p></desc> </func> - <func><name><ret>int</ret><nametext>enif_make_reverse_list(ErlNifEnv* env, ERL_NIF_TERM term, ERL_NIF_TERM *list)</nametext></name> - <fsummary>Create the reverse list of the list <c>term</c>.</fsummary> - <desc><p>Set <c>*list</c> to the reverse list of the list <c>term</c> and return true, - or return false if <c>term</c> is not a list. This function should only be used on - short lists as a copy will be created of the list which will not be released until after the - nif returns.</p></desc> - </func> <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_long(ErlNifEnv* env, long int i)</nametext></name> <fsummary>Create an integer term from a long int</fsummary> <desc><p>Create an integer term from a <c>long int</c>.</p></desc> @@ -1007,12 +1024,42 @@ typedef enum { reallocated.</p><p>Return a pointer to the raw binary data and set <c>*termp</c> to the binary term.</p></desc> </func> + <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_new_map(ErlNifEnv* env)</nametext></name> + <fsummary>Make an empty map term</fsummary> + <desc><p>Make an empty map term.</p></desc> + </func> + <func><name><ret>int</ret><nametext>enif_make_map_put(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)</nametext></name> + <fsummary>Insert key-value pair in map</fsummary> + <desc><p>Make a copy of map <c>map_in</c> and insert <c>key</c> with + <c>value</c>. If <c>key</c> already exists in <c>map_in</c>, the old + associated value is replaced by <c>value</c>. If successful set + <c>*map_out</c> to the new map and return true. Return false if + <c>map_in</c> is not a map.</p> + <p>The <c>map_in</c> term must belong to the environment <c>env</c>.</p></desc> + </func> + <func><name><ret>int</ret><nametext>enif_make_map_update(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM new_value, ERL_NIF_TERM* map_out)</nametext></name> + <fsummary>Replace value for key in map</fsummary> + <desc><p>Make a copy of map <c>map_in</c> and replace the old associated + value for <c>key</c> with <c>new_value</c>. If successful set + <c>*map_out</c> to the new map and return true. Return false if + <c>map_in</c> is not a map or if it does no contain <c>key</c>.</p> + <p>The <c>map_in</c> term must belong to the environment <c>env</c>.</p></desc> + </func> + <func><name><ret>int</ret><nametext>enif_make_map_remove(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out)</nametext></name> + <fsummary>Remove key from map</fsummary> + <desc><p>If map <c>map_in</c> contains <c>key</c>, make a copy of + <c>map_in</c> in <c>*map_out</c> and remove <c>key</c> and associated + value. If map <c>map_in</c> does not contain <c>key</c>, set + <c>*map_out</c> to <c>map_in</c>. Return true for success or false if + <c>map_in</c> is not a map.</p> + <p>The <c>map_in</c> term must belong to the environment <c>env</c>.</p></desc> + </func> <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_pid(ErlNifEnv* env, const ErlNifPid* pid)</nametext></name> <fsummary>Make a pid term</fsummary> <desc><p>Make a pid term from <c>*pid</c>.</p></desc> </func> <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_ref(ErlNifEnv* env)</nametext></name> - <fsummary>Create a reference.</fsummary> + <fsummary>Create a reference</fsummary> <desc><p>Create a reference like <seealso marker="erlang#make_ref-0">erlang:make_ref/0</seealso>.</p></desc> </func> <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_resource(ErlNifEnv* env, void* obj)</nametext></name> @@ -1050,20 +1097,28 @@ typedef enum { <seealso marker="#enif_release_resource">enif_release_resource</seealso>.</p> </desc> </func> + <func><name><ret>int</ret><nametext>enif_make_reverse_list(ErlNifEnv* env, ERL_NIF_TERM list_in, ERL_NIF_TERM *list_out)</nametext></name> + <fsummary>Create the reverse of a list</fsummary> + <desc><p>Set <c>*list_out</c> to the reverse list of the list <c>list_in</c> and return true, + or return false if <c>list_in</c> is not a list. This function should only be used on + short lists as a copy will be created of the list which will not be released until after the + nif returns.</p> + <p>The <c>list_in</c> term must belong to the environment <c>env</c>.</p></desc> + </func> <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_string(ErlNifEnv* env, const char* string, ErlNifCharEncoding encoding)</nametext></name> - <fsummary>Create a string.</fsummary> + <fsummary>Create a string</fsummary> <desc><p>Create a list containing the characters of the null-terminated string <c>string</c> with encoding <seealso marker="#ErlNifCharEncoding">encoding</seealso>.</p></desc> </func> <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_string_len(ErlNifEnv* env, const char* string, size_t len, ErlNifCharEncoding encoding)</nametext></name> - <fsummary>Create a string.</fsummary> + <fsummary>Create a string</fsummary> <desc><p>Create a list containing the characters of the string <c>string</c> with length <c>len</c> and encoding <seealso marker="#ErlNifCharEncoding">encoding</seealso>. Null-characters are treated as any other characters.</p></desc> </func> <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_sub_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, size_t pos, size_t size)</nametext></name> - <fsummary>Make a subbinary term.</fsummary> + <fsummary>Make a subbinary term</fsummary> <desc><p>Make a subbinary of binary <c>bin_term</c>, starting at zero-based position <c>pos</c> with a length of <c>size</c> bytes. <c>bin_term</c> must be a binary or bitstring and @@ -1071,7 +1126,7 @@ typedef enum { bytes in <c>bin_term</c>.</p></desc> </func> <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple(ErlNifEnv* env, unsigned cnt, ...)</nametext></name> - <fsummary>Create a tuple term.</fsummary> + <fsummary>Create a tuple term</fsummary> <desc><p>Create a tuple term of arity <c>cnt</c>. Expects <c>cnt</c> number of arguments (after <c>cnt</c>) of type ERL_NIF_TERM as the elements of the tuple.</p></desc> @@ -1085,14 +1140,14 @@ typedef enum { <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple7(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7)</nametext></name> <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple8(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8)</nametext></name> <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple9(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9)</nametext></name> - <fsummary>Create a tuple term.</fsummary> + <fsummary>Create a tuple term</fsummary> <desc><p>Create a tuple term with length indicated by the function name. Prefer these functions (macros) over the variadic <c>enif_make_tuple</c> to get a compile time error if the number of arguments does not match.</p></desc> </func> <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple_from_array(ErlNifEnv* env, const ERL_NIF_TERM arr[], unsigned cnt)</nametext></name> - <fsummary>Create a tuple term from an array.</fsummary> + <fsummary>Create a tuple term from an array</fsummary> <desc><p>Create a tuple containing the elements of array <c>arr</c> of length <c>cnt</c>.</p></desc> </func> @@ -1108,6 +1163,72 @@ typedef enum { <fsummary>Create an integer term from an unsigned long int</fsummary> <desc><p>Create an integer term from an <c>unsigned long int</c>.</p></desc> </func> + <func><name><ret>int</ret><nametext>enif_map_iterator_create(ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry)</nametext></name> + <fsummary>Create a map iterator</fsummary> + <desc><p>Create an iterator for the map <c>map</c> by initializing the + structure pointed to by <c>iter</c>. The <c>entry</c> argument determines + the start position of the iterator: <c>ERL_NIF_MAP_ITERATOR_FIRST</c> or + <c>ERL_NIF_MAP_ITERATOR_LAST</c>. Return true on success or false if + <c>map</c> is not a map.</p> + <p>A map iterator is only useful during the lifetime of the environment + <c>env</c> that the <c>map</c> belongs to. The iterator must be destroyed by + calling <seealso marker="#enif_map_iterator_destroy"> + enif_map_iterator_destroy</seealso>.</p> + <code type="none"> +ERL_NIF_TERM key, value; +ErlNifMapIterator iter; +enif_map_iterator_create(env, my_map, ERL_NIF_MAP_ITERATOR_FIRST); + +while (enif_map_iterator_get_pair(env, &iter, &key, &value)) { + do_something(key,value); + enif_map_iterator_next(env, &iter); +} +enif_map_iterator_destroy(env, &iter); + </code> + <note><p>The key-value pairs of a map have no defined iteration + order. The only guarantee is that the iteration order of a single map + instance is preserved during the lifetime of the environment that the map + belongs to.</p> + </note> + </desc> + </func> + <func><name><ret>void</ret><nametext>enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name> + <fsummary>Destroy a map iterator</fsummary> + <desc><p>Destroy a map iterator created by + <seealso marker="#enif_map_iterator_create">enif_map_iterator_create</seealso>. + </p></desc> + </func> + <func><name><ret>int</ret><nametext>enif_map_iterator_get_pair(ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value)</nametext></name> + <fsummary>Get key and value at current map iterator position</fsummary> + <desc><p>Get key and value terms at current map iterator position. + On success set <c>*key</c> and <c>*value</c> and return true. + Return false if the iterator is positioned at head (before first entry) + or tail (beyond last entry).</p></desc> + </func> + <func><name><ret>int</ret><nametext>enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name> + <fsummary>Check if map iterator is positioned before first</fsummary> + <desc><p>Return true if map iterator <c>iter</c> is positioned + before first entry.</p></desc> + </func> + <func><name><ret>int</ret><nametext>enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name> + <fsummary>Check if map iterator is positioned after last</fsummary> + <desc><p>Return true if map iterator <c>iter</c> is positioned + after last entry.</p></desc> + </func> + <func><name><ret>int</ret><nametext>enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name> + <fsummary>Increment map iterator to point to next entry</fsummary> + <desc><p>Increment map iterator to point to next key-value entry. + Return true if the iterator is now positioned at a valid key-value entry, + or false if the iterator is positioned at the tail (beyond the last + entry).</p></desc> + </func> + <func><name><ret>int</ret><nametext>enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name> + <fsummary>Decrement map iterator to point to previous entry</fsummary> + <desc><p>Decrement map iterator to point to previous key-value entry. + Return true if the iterator is now positioned at a valid key-value entry, + or false if the iterator is positioned at the head (before the first + entry).</p></desc> + </func> <func><name><ret>ErlNifMutex *</ret><nametext>enif_mutex_create(char *name)</nametext></name> <fsummary></fsummary> <desc><p>Same as <seealso marker="erl_driver#erl_drv_mutex_create">erl_drv_mutex_create</seealso>. @@ -1168,19 +1289,32 @@ typedef enum { <c>reload</c> or <c>upgrade</c>.</p> <p>Was previously named <c>enif_get_data</c>.</p></desc> </func> + <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_raise_exception(ErlNifEnv* env, ERL_NIF_TERM reason)</nametext></name> + <fsummary>Raise a NIF error exception</fsummary> + <desc><p>Create an error exception with the term <c>reason</c> to be returned from a NIF, + and associate it with the environment <c>env</c>. Once a NIF or any function it calls + invokes <c>enif_raise_exception</c>, the runtime ensures that the exception it creates + is raised when the NIF returns, even if the NIF attempts to return a non-exception + term instead. The return value from <c>enif_raise_exception</c> may be used only as + the return value from the NIF that invoked it (directly or indirectly) or be passed + to <seealso marker="#enif_is_exception">enif_is_exception</seealso>, but + not to any other NIF API function.</p> + <p>See also: <seealso marker="#enif_has_pending_exception">enif_has_pending_exception</seealso> + and <seealso marker="#enif_make_badarg">enif_make_badarg</seealso>.</p></desc> + </func> <func><name><ret>int</ret><nametext>enif_realloc_binary(ErlNifBinary* bin, size_t size)</nametext></name> - <fsummary>Change the size of a binary.</fsummary> + <fsummary>Change the size of a binary</fsummary> <desc><p>Change the size of a binary <c>bin</c>. The source binary may be read-only, in which case it will be left untouched and a mutable copy is allocated and assigned to <c>*bin</c>. Return true on success, false if memory allocation failed.</p></desc> </func> <func><name><ret>void</ret><nametext>enif_release_binary(ErlNifBinary* bin)</nametext></name> - <fsummary>Release a binary.</fsummary> + <fsummary>Release a binary</fsummary> <desc><p>Release a binary obtained from <c>enif_alloc_binary</c>.</p></desc> </func> <func><name><ret>void</ret><nametext>enif_release_resource(void* obj)</nametext></name> - <fsummary>Release a resource object.</fsummary> + <fsummary>Release a resource object</fsummary> <desc><p>Remove a reference to resource object <c>obj</c>obtained from <seealso marker="#enif_alloc_resource">enif_alloc_resource</seealso>. The resource object will be destructed when the last reference is removed. @@ -1256,12 +1390,12 @@ typedef enum { </desc> </func> <func><name><ret>ErlNifPid *</ret><nametext>enif_self(ErlNifEnv* caller_env, ErlNifPid* pid)</nametext></name> - <fsummary>Get the pid of the calling process.</fsummary> + <fsummary>Get the pid of the calling process</fsummary> <desc><p>Initialize the pid variable <c>*pid</c> to represent the calling process. Return <c>pid</c>.</p></desc> </func> <func><name><ret>int</ret><nametext>enif_send(ErlNifEnv* env, ErlNifPid* to_pid, ErlNifEnv* msg_env, ERL_NIF_TERM msg)</nametext></name> - <fsummary>Send a message to a process.</fsummary> + <fsummary>Send a message to a process</fsummary> <desc><p>Send a message to a process.</p> <taglist> <tag><c>env</c></tag> diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 988ff0e2b5..33417833a9 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -625,7 +625,7 @@ void erts_usage(void) erts_fprintf(stderr, "-v turn on chatty mode (GCs will be reported etc)\n"); - erts_fprintf(stderr, "-W<i|w> set error logger warnings mapping,\n"); + erts_fprintf(stderr, "-W<i|w|e> set error logger warnings mapping,\n"); erts_fprintf(stderr, " see error_logger documentation for details\n"); erts_fprintf(stderr, "-zdbbl size set the distribution buffer busy limit in kilobytes\n"); erts_fprintf(stderr, " valid range is [1-%d]\n", INT_MAX/1024); @@ -1253,7 +1253,7 @@ erl_start(int argc, char **argv) verbose = DEBUG_DEFAULT; #endif - erts_error_logger_warnings = am_error; + erts_error_logger_warnings = am_warning; while (i < argc) { if (argv[i][0] != '-') { @@ -1991,11 +1991,12 @@ erl_start(int argc, char **argv) case 'i': erts_error_logger_warnings = am_info; break; + case 'e': + erts_error_logger_warnings = am_error; + break; case 'w': erts_error_logger_warnings = am_warning; break; - case 'e': /* The default */ - erts_error_logger_warnings = am_error; default: erts_fprintf(stderr, "unrecognized warning_map option %s\n", arg); erts_usage(); diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 426a00304e..45fc949b81 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -445,7 +445,7 @@ int enif_is_list(ErlNifEnv* env, ERL_NIF_TERM term) int enif_is_exception(ErlNifEnv* env, ERL_NIF_TERM term) { - return term == THE_NON_VALUE; + return env->exception_thrown && term == THE_NON_VALUE; } int enif_is_number(ErlNifEnv* env, ERL_NIF_TERM term) @@ -737,12 +737,21 @@ Eterm enif_make_sub_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, Eterm enif_make_badarg(ErlNifEnv* env) { + return enif_raise_exception(env, am_badarg); +} + +Eterm enif_raise_exception(ErlNifEnv* env, ERL_NIF_TERM reason) +{ env->exception_thrown = 1; - BIF_ERROR(env->proc, BADARG); + env->proc->fvalue = reason; + BIF_ERROR(env->proc, EXC_ERROR); } -int enif_has_pending_exception(ErlNifEnv* env) +int enif_has_pending_exception(ErlNifEnv* env, ERL_NIF_TERM* reason) { + if (env->exception_thrown && reason != NULL) { + *reason = env->proc->fvalue; + } return env->exception_thrown; } @@ -1538,12 +1547,13 @@ int enif_consume_timeslice(ErlNifEnv* env, int percent) * NIF exports need a few more items than the Export struct provides, * including the erl_module_nif* and a NIF function pointer, so the * NifExport below adds those. The Export member must be first in the - * struct. The saved_mfa, saved_argc, nif_level, alloced_argv_sz and argv - * members are used to track the MFA and arguments of the top NIF in case a - * chain of one or more enif_schedule_nif() calls results in an exception, - * since in that case the original MFA and registers have to be restored - * before returning to Erlang to ensure stacktrace information associated - * with the exception is correct. + * struct. The saved_mfa, exception_thrown, saved_argc, rootset_extra, and + * rootset members are used to track the MFA, any pending exception, and + * arguments of the top NIF in case a chain of one or more + * enif_schedule_nif() calls results in an exception, since in that case + * the original MFA and registers have to be restored before returning to + * Erlang to ensure stacktrace information associated with the exception is + * correct. */ typedef ERL_NIF_TERM (*NativeFunPtr)(ErlNifEnv*, int, const ERL_NIF_TERM[]); @@ -1552,25 +1562,28 @@ typedef struct { struct erl_module_nif* m; NativeFunPtr fp; Eterm saved_mfa[3]; + int exception_thrown; int saved_argc; - int alloced_argv_sz; - Eterm argv[1]; + int rootset_extra; + Eterm rootset[1]; } NifExport; /* * If a process has saved arguments, they need to be part of the GC * rootset. The function below is called from setup_rootset() in - * erl_gc.c. This function is declared in erl_process.h. + * erl_gc.c. This function is declared in erl_process.h. Any exception term + * saved in the NifExport is also made part of the GC rootset here; it + * always resides in rootset[0]. */ int erts_setup_nif_gc(Process* proc, Eterm** objv, int* nobj) { NifExport* ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); - int gc = (ep && ep->saved_argc > 0); + int gc = ep && (ep->saved_argc > 0 || ep->rootset[0] != NIL); if (gc) { - *objv = ep->argv; - *nobj = ep->saved_argc; + *objv = ep->rootset; + *nobj = 1 + ep->saved_argc; } return gc; } @@ -1582,14 +1595,14 @@ static NifExport* allocate_nif_sched_data(Process* proc, int argc) { NifExport* ep; - size_t argv_extra, total; + size_t total; int i; - argv_extra = argc > 1 ? sizeof(Eterm)*(argc-1) : 0; - total = sizeof(NifExport) + argv_extra; + total = sizeof(NifExport) + argc*sizeof(Eterm); ep = erts_alloc(ERTS_ALC_T_NIF_TRAP_EXPORT, total); sys_memset((void*) ep, 0, total); - ep->alloced_argv_sz = argc; + ep->rootset_extra = argc; + ep->rootset[0] = NIL; for (i=0; i<ERTS_NUM_CODE_IX; i++) { ep->exp.addressv[i] = &ep->exp.code[3]; } @@ -1630,15 +1643,22 @@ init_nif_sched_data(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirec ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); if (!ep) ep = allocate_nif_sched_data(proc, argc); - else if (need_save && ep->alloced_argv_sz < argc) { + else if (need_save && ep->rootset_extra < argc) { NifExport* new_ep = allocate_nif_sched_data(proc, argc); destroy_nif_export(ep); ep = new_ep; } + if (env->exception_thrown) { + ep->exception_thrown = 1; + ep->rootset[0] = env->proc->fvalue; + } else { + ep->exception_thrown = 0; + ep->rootset[0] = NIL; + } ERTS_VBUMP_ALL_REDS(proc); for (i = 0; i < argc; i++) { if (need_save) - ep->argv[i] = reg[i]; + ep->rootset[i+1] = reg[i]; reg[i] = (Eterm) argv[i]; } if (need_save) { @@ -1674,7 +1694,7 @@ restore_nif_mfa(Process* proc, NifExport* ep, int exception) proc->current[2] = ep->saved_mfa[2]; if (exception) for (i = 0; i < ep->saved_argc; i++) - reg[i] = ep->argv[i]; + reg[i] = ep->rootset[i+1]; ep->saved_argc = 0; ep->saved_mfa[0] = THE_NON_VALUE; } @@ -1699,6 +1719,7 @@ dirty_nif_finalizer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ASSERT(!ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data)); ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); ASSERT(ep); + ASSERT(!ep->exception_thrown); if (ep->fp) restore_nif_mfa(proc, ep, 0); return argv[0]; @@ -1716,9 +1737,10 @@ dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ASSERT(!ERTS_SCHEDULER_IS_DIRTY(env->proc->scheduler_data)); ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); ASSERT(ep); + ASSERT(ep->exception_thrown); if (ep->fp) restore_nif_mfa(proc, ep, 1); - return enif_make_badarg(env); + return enif_raise_exception(env, ep->rootset[0]); } /* @@ -1843,6 +1865,7 @@ execute_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) NifExport* ep; ERL_NIF_TERM result; + ASSERT(!env->exception_thrown); ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc); ASSERT(ep); ep->fp = NULL; @@ -1855,7 +1878,7 @@ execute_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) * which case we need to restore the original NIF MFA. */ if (ep->fp == NULL) - restore_nif_mfa(proc, ep, is_non_value(result) && proc->freason != TRAP); + restore_nif_mfa(proc, ep, env->exception_thrown); return result; } @@ -2024,8 +2047,8 @@ int enif_map_iterator_create(ErlNifEnv *env, size_t offset; switch (entry) { - case ERL_NIF_MAP_ITERATOR_HEAD: offset = 0; break; - case ERL_NIF_MAP_ITERATOR_TAIL: offset = flatmap_get_size(mp) - 1; break; + case ERL_NIF_MAP_ITERATOR_FIRST: offset = 0; break; + case ERL_NIF_MAP_ITERATOR_LAST: offset = flatmap_get_size(mp) - 1; break; default: goto error; } @@ -2048,12 +2071,12 @@ int enif_map_iterator_create(ErlNifEnv *env, WSTACK_INIT(iter->u.hash.wstack, ERTS_ALC_T_NIF); switch (entry) { - case ERL_NIF_MAP_ITERATOR_HEAD: + case ERL_NIF_MAP_ITERATOR_FIRST: iter->idx = 1; hashmap_iterator_init(&iter->u.hash.wstack->ws, map, 0); iter->u.hash.kv = hashmap_iterator_next(&iter->u.hash.wstack->ws); break; - case ERL_NIF_MAP_ITERATOR_TAIL: + case ERL_NIF_MAP_ITERATOR_LAST: iter->idx = hashmap_size(map); hashmap_iterator_init(&iter->u.hash.wstack->ws, map, 1); iter->u.hash.kv = hashmap_iterator_prev(&iter->u.hash.wstack->ws); diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index c4fdfd4187..af806736fd 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -218,8 +218,12 @@ typedef struct /* All fields all internal and may change */ } ErlNifMapIterator; typedef enum { - ERL_NIF_MAP_ITERATOR_HEAD = 1, - ERL_NIF_MAP_ITERATOR_TAIL = 2 + ERL_NIF_MAP_ITERATOR_FIRST = 1, + ERL_NIF_MAP_ITERATOR_LAST = 2, + + /* deprecated synonyms (undocumented in 17 and 18-rc) */ + ERL_NIF_MAP_ITERATOR_HEAD = ERL_NIF_MAP_ITERATOR_FIRST, + ERL_NIF_MAP_ITERATOR_TAIL = ERL_NIF_MAP_ITERATOR_LAST } ErlNifMapIteratorEntry; #if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index bdcbb32c46..e38a016958 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -156,7 +156,8 @@ ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_next, (ErlNifEnv *env, ErlNifMapIte ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_prev, (ErlNifEnv *env, ErlNifMapIterator *iter)); ERL_NIF_API_FUNC_DECL(int, enif_map_iterator_get_pair, (ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value)); ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_schedule_nif,(ErlNifEnv*,const char*,int,ERL_NIF_TERM (*)(ErlNifEnv*,int,const ERL_NIF_TERM[]),int,const ERL_NIF_TERM[])); -ERL_NIF_API_FUNC_DECL(int, enif_has_pending_exception, (ErlNifEnv *env)); +ERL_NIF_API_FUNC_DECL(int, enif_has_pending_exception, (ErlNifEnv *env, ERL_NIF_TERM* reason)); +ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM, enif_raise_exception, (ErlNifEnv *env, ERL_NIF_TERM reason)); /* ** ADD NEW ENTRIES HERE (before this comment) !!! @@ -307,6 +308,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); # define enif_map_iterator_get_pair ERL_NIF_API_FUNC_MACRO(enif_map_iterator_get_pair) # define enif_schedule_nif ERL_NIF_API_FUNC_MACRO(enif_schedule_nif) # define enif_has_pending_exception ERL_NIF_API_FUNC_MACRO(enif_has_pending_exception) +# define enif_raise_exception ERL_NIF_API_FUNC_MACRO(enif_raise_exception) /* ** ADD NEW ENTRIES HERE (before this comment) diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 5196eb51c6..e001f31932 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -293,6 +293,10 @@ static BOOL (WINAPI *fpSetHandleInformation)(HANDLE,DWORD,DWORD); static unsigned long zero_value = 0; static unsigned long one_value = 1; +#define TCP_SHUT_WR SD_SEND +#define TCP_SHUT_RD SD_RECEIVE +#define TCP_SHUT_RDWR SD_BOTH + #elif defined (__OSE__) /* @@ -421,6 +425,10 @@ typedef unsigned long u_long; inet_driver_select((d), (flags), (onoff)); \ } while(0) +#define TCP_SHUT_WR SHUT_WR +#define TCP_SHUT_RD SHUT_RD +#define TCP_SHUT_RDWR SHUT_RDWR + #else /* !__OSE__ && !__WIN32__ */ #include <sys/time.h> @@ -691,6 +699,9 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) inet_driver_select((d)->port, (ErlDrvEvent)(long)(d)->event, (flags), (onoff)); \ } while(0) +#define TCP_SHUT_WR SHUT_WR +#define TCP_SHUT_RD SHUT_RD +#define TCP_SHUT_RDWR SHUT_RDWR #endif /* !__WIN32__ && !__OSE__ */ @@ -820,6 +831,10 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define TCP_ADDF_CLOSE_SENT 2 /* Close sent (active mode only) */ #define TCP_ADDF_DELAYED_CLOSE_RECV 4 /* If receive fails, report {error,closed} (passive mode) */ #define TCP_ADDF_DELAYED_CLOSE_SEND 8 /* If send fails, report {error,closed} (passive mode) */ +#define TCP_ADDF_PENDING_SHUT_WR 16 /* Call shutdown(sock, SHUT_WR) when queue empties */ +#define TCP_ADDF_PENDING_SHUT_RDWR 32 /* Call shutdown(sock, SHUT_RDWR) when queue empties */ +#define TCP_ADDF_PENDING_SHUTDOWN \ + (TCP_ADDF_PENDING_SHUT_WR | TCP_ADDF_PENDING_SHUT_RDWR) /* *_REQ_* replies */ #define INET_REP_ERROR 0 @@ -1407,6 +1422,8 @@ static int tcp_sendv(tcp_descriptor* desc, ErlIOVec* ev); static int tcp_recv(tcp_descriptor* desc, int request_len); static int tcp_deliver(tcp_descriptor* desc, int len); +static int tcp_shutdown_error(tcp_descriptor* desc, int err); + static int tcp_inet_output(tcp_descriptor* desc, HANDLE event); static int tcp_inet_input(tcp_descriptor* desc, HANDLE event); @@ -9473,10 +9490,18 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, return ctl_error(EINVAL, rbuf, rsize); } how = buf[0]; - if (sock_shutdown(INETP(desc)->s, how) == 0) { + if (how != TCP_SHUT_RD && driver_sizeq(desc->inet.port) > 0) { + if (how == TCP_SHUT_WR) { + desc->tcp_add_flags |= TCP_ADDF_PENDING_SHUT_WR; + } else if (how == TCP_SHUT_RDWR) { + desc->tcp_add_flags |= TCP_ADDF_PENDING_SHUT_RDWR; + } return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); - } else { + } + if (IS_SOCKET_ERROR(sock_shutdown(INETP(desc)->s, how))) { return ctl_error(sock_errno(), rbuf, rsize); + } else { + return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } } default: @@ -9613,6 +9638,8 @@ static void tcp_inet_commandv(ErlDrvData e, ErlIOVec* ev) else inet_reply_error(INETP(desc), ENOTCONN); } + else if (desc->tcp_add_flags & TCP_ADDF_PENDING_SHUTDOWN) + tcp_shutdown_error(desc, EPIPE); else if (tcp_sendv(desc, ev) == 0) inet_reply_ok(INETP(desc)); DEBUGF(("tcp_inet_commandv(%ld) }\r\n", (long)desc->inet.port)); @@ -10506,7 +10533,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) return ret; } -static int tcp_send_error(tcp_descriptor* desc, int err) +static int tcp_send_or_shutdown_error(tcp_descriptor* desc, int err) { /* * If the port is busy, we must do some clean-up before proceeding. @@ -10563,6 +10590,16 @@ static int tcp_send_error(tcp_descriptor* desc, int err) return -1; } +static int tcp_send_error(tcp_descriptor* desc, int err) +{ + return tcp_send_or_shutdown_error(desc, err); +} + +static int tcp_shutdown_error(tcp_descriptor* desc, int err) +{ + return tcp_send_or_shutdown_error(desc, err); +} + /* ** Send non-blocking vector data */ @@ -10763,6 +10800,19 @@ static int tcp_send(tcp_descriptor* desc, char* ptr, ErlDrvSizeT len) return 0; } +/* shutdown on the socket: +** Assume caller has confirmed TCP_ADDF_PENDING_SHUTDOWN is set. +*/ +static void tcp_shutdown_async(tcp_descriptor* desc) +{ + int how; + + how = (desc->tcp_add_flags & TCP_ADDF_PENDING_SHUT_WR) ? + TCP_SHUT_WR : TCP_SHUT_RDWR; + if (IS_SOCKET_ERROR(sock_shutdown(INETP(desc)->s, how))) + tcp_shutdown_error(desc, sock_errno()); +} + #ifdef __OSE__ static void tcp_inet_drv_output_ose(ErlDrvData data, ErlDrvEvent event) @@ -10891,6 +10941,8 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) if ((iov = driver_peekq(ix, &vsize)) == NULL) { sock_select(INETP(desc), FD_WRITE, 0); send_empty_out_q_msgs(INETP(desc)); + if (desc->tcp_add_flags & TCP_ADDF_PENDING_SHUTDOWN) + tcp_shutdown_async(desc); goto done; } vsize = vsize > MAX_VSIZE ? MAX_VSIZE : vsize; diff --git a/erts/emulator/hipe/hipe_bif0.c b/erts/emulator/hipe/hipe_bif0.c index 099f4f90de..a4311d22e2 100644 --- a/erts/emulator/hipe/hipe_bif0.c +++ b/erts/emulator/hipe/hipe_bif0.c @@ -1745,13 +1745,11 @@ BIF_RETTYPE hipe_bifs_check_crc_1(BIF_ALIST_1) BIF_RET(am_false); } -BIF_RETTYPE hipe_bifs_system_crc_1(BIF_ALIST_1) +BIF_RETTYPE hipe_bifs_system_crc_0(BIF_ALIST_0) { Uint crc; - if (!term_to_Uint(BIF_ARG_1, &crc)) - BIF_ERROR(BIF_P, BADARG); - crc ^= (HIPE_SYSTEM_CRC ^ HIPE_LITERALS_CRC); + crc = HIPE_SYSTEM_CRC; BIF_RET(Uint_to_term(crc, BIF_P)); } diff --git a/erts/emulator/hipe/hipe_bif0.tab b/erts/emulator/hipe/hipe_bif0.tab index a3e04802df..4271a78de3 100644 --- a/erts/emulator/hipe/hipe_bif0.tab +++ b/erts/emulator/hipe/hipe_bif0.tab @@ -74,7 +74,7 @@ bif hipe_bifs:set_native_address_in_fe/2 bif hipe_bifs:find_na_or_make_stub/2 bif hipe_bifs:check_crc/1 -bif hipe_bifs:system_crc/1 +bif hipe_bifs:system_crc/0 bif hipe_bifs:get_rts_param/1 #bif hipe_bifs:tuple_to_float/1 diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c index ed355ce264..49e8d39360 100644 --- a/erts/emulator/hipe/hipe_mkliterals.c +++ b/erts/emulator/hipe/hipe_mkliterals.c @@ -648,8 +648,7 @@ static int do_e(FILE *fp, const char* this_exe) fprintf(fp, "-define(HIPE_SYSTEM_CRC, %u).\n", system_crc); } else { - fprintf(fp, "-define(HIPE_SYSTEM_CRC, hipe_bifs:system_crc(%u)).\n", - literals_crc); + fprintf(fp, "-define(HIPE_SYSTEM_CRC, hipe_bifs:system_crc()).\n"); } return 0; } diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl index dc215b1529..7326dfceb1 100644 --- a/erts/emulator/test/monitor_SUITE.erl +++ b/erts/emulator/test/monitor_SUITE.erl @@ -665,98 +665,86 @@ list_cleanup(Config) when is_list(Config) -> mixer(doc) -> "Test mixing of internal and external monitors."; mixer(Config) when is_list(Config) -> - ?line PA = filename:dirname(code:which(?MODULE)), - ?line NN = [j0,j1,j2,j3], -% ?line NN = [j0,j1], - ?line NL0 = [begin - {ok, J} = test_server:start_node - (X, slave, [{args, "-pa " ++ PA}]), - J - end || X <- NN], - ?line NL1 = lists:duplicate(2,node()) ++ NL0, - ?line Perm = perm(NL1), - ?line lists:foreach( - fun(NL) -> - ?line Js = [ start_jeeves({[],M}) || M <- (NL ++ NL) ], - ?line [ask_jeeves(P,{monitor_process,self()}) || P <- Js], - ?line {monitored_by,MB} = - process_info(self(),monitored_by), - ?line MBL = lists:sort(MB), - ?line JsL = lists:sort(Js), - ?line MBL = JsL, - ?line {monitors,[]} = process_info(self(),monitors), - ?line [tell_jeeves(P,{exit,flaff}) || P <- Js], - ?line wait_for_m([],[],200) - end, - Perm), - ?line lists:foreach( - fun(NL) -> - ?line Js = [ start_jeeves({[],M}) || M <- (NL ++ NL) ], - ?line Rs = [begin - {monitor_process,Ref} = - ask_jeeves(P,{monitor_process,self()}), - {P,Ref} - end - || P <- Js], - ?line {monitored_by,MB} = - process_info(self(),monitored_by), - ?line MBL = lists:sort(MB), - ?line JsL = lists:sort(Js), - ?line MBL = JsL, - ?line {monitors,[]} = process_info(self(),monitors), - ?line [ask_jeeves(P,{demonitor,Ref}) || {P,Ref} <- Rs], - ?line wait_for_m([],[],200), - ?line [tell_jeeves(P,{exit,flaff}) || P <- Js] - end, - Perm), - ?line lists:foreach( - fun(NL) -> - ?line Js = [ start_jeeves({[],M}) || M <- (NL ++ NL) ], - ?line [ask_jeeves(P,{monitor_process,self()}) || P <- Js], - ?line [erlang:monitor(process,P) || P <- Js], - ?line {monitored_by,MB} = - process_info(self(),monitored_by), - ?line MBL = lists:sort(MB), - ?line JsL = lists:sort(Js), - ?line MBL = JsL, - ?line {monitors,M} = - process_info(self(),monitors), - ?line ML = lists:sort([P||{process,P} <- M]), - ?line ML = JsL, - ?line [begin - tell_jeeves(P,{exit,flaff}), - receive {'DOWN',_,process,P,_} -> ok end - end || P <- Js], - ?line wait_for_m([],[],200) - end, - Perm), - ?line lists:foreach( - fun(NL) -> - ?line Js = [ start_jeeves({[],M}) || M <- (NL ++ NL) ], - ?line Rs = [begin - {monitor_process,Ref} = - ask_jeeves(P,{monitor_process,self()}), - {P,Ref} - end - || P <- Js], - ?line R2s = [{P,erlang:monitor(process,P)} || P <- Js], - ?line {monitored_by,MB} = - process_info(self(),monitored_by), - ?line MBL = lists:sort(MB), - ?line JsL = lists:sort(Js), - ?line MBL = JsL, - ?line {monitors,M} = - process_info(self(),monitors), - ?line ML = lists:sort([P||{process,P} <- M]), - ?line ML = JsL, - ?line [ask_jeeves(P,{demonitor,Ref}) || {P,Ref} <- Rs], - ?line wait_for_m(lists:sort(M),[],200), - ?line [erlang:demonitor(Ref) || {_P,Ref} <- R2s], - ?line wait_for_m([],[],200), - ?line [tell_jeeves(P,{exit,flaff}) || P <- Js] - end, - Perm), - [test_server:stop_node(K) || K <- NL0 ], + PA = filename:dirname(code:which(?MODULE)), + NN = [j0,j1,j2], + NL0 = [begin + {ok, J} = test_server:start_node(X,slave,[{args, "-pa " ++ PA}]), + J + end || X <- NN], + NL1 = lists:duplicate(2,node()) ++ NL0, + Perm = perm(NL1), + lists:foreach( + fun(NL) -> + Js = [start_jeeves({[],M}) || M <- (NL ++ NL)], + [ask_jeeves(P,{monitor_process,self()}) || P <- Js], + {monitored_by,MB} = process_info(self(),monitored_by), + MBL = lists:sort(MB), + JsL = lists:sort(Js), + MBL = JsL, + {monitors,[]} = process_info(self(),monitors), + [tell_jeeves(P,{exit,flaff}) || P <- Js], + wait_for_m([],[],200) + end, + Perm), + lists:foreach( + fun(NL) -> + Js = [start_jeeves({[],M}) || M <- (NL ++ NL)], + Rs = [begin + {monitor_process,Ref} = ask_jeeves(P,{monitor_process,self()}), + {P,Ref} + end || P <- Js], + {monitored_by,MB} = process_info(self(),monitored_by), + MBL = lists:sort(MB), + JsL = lists:sort(Js), + MBL = JsL, + {monitors,[]} = process_info(self(),monitors), + [ask_jeeves(P,{demonitor,Ref}) || {P,Ref} <- Rs], + wait_for_m([],[],200), + [tell_jeeves(P,{exit,flaff}) || P <- Js] + end, + Perm), + lists:foreach( + fun(NL) -> + Js = [start_jeeves({[],M}) || M <- (NL ++ NL)], + [ask_jeeves(P,{monitor_process,self()}) || P <- Js], + [erlang:monitor(process,P) || P <- Js], + {monitored_by,MB} = process_info(self(),monitored_by), + MBL = lists:sort(MB), + JsL = lists:sort(Js), + MBL = JsL, + {monitors,M} = process_info(self(),monitors), + ML = lists:sort([P||{process,P} <- M]), + ML = JsL, + [begin + tell_jeeves(P,{exit,flaff}), + receive {'DOWN',_,process,P,_} -> ok end + end || P <- Js], + wait_for_m([],[],200) + end, + Perm), + lists:foreach( + fun(NL) -> + Js = [start_jeeves({[],M}) || M <- (NL ++ NL)], + Rs = [begin + {monitor_process,Ref} = ask_jeeves(P,{monitor_process,self()}), + {P,Ref} + end || P <- Js], + R2s = [{P,erlang:monitor(process,P)} || P <- Js], + {monitored_by,MB} = process_info(self(),monitored_by), + MBL = lists:sort(MB), + JsL = lists:sort(Js), + MBL = JsL, + {monitors,M} = process_info(self(),monitors), + ML = lists:sort([P||{process,P} <- M]), + ML = JsL, + [ask_jeeves(P,{demonitor,Ref}) || {P,Ref} <- Rs], + wait_for_m(lists:sort(M),[],200), + [erlang:demonitor(Ref) || {_P,Ref} <- R2s], + wait_for_m([],[],200), + [tell_jeeves(P,{exit,flaff}) || P <- Js] + end, + Perm), + [test_server:stop_node(K) || K <- NL0], ok. named_down(doc) -> ["Test that DOWN message for a named monitor isn't" diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index c35c71dd5b..778f6fd087 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -39,8 +39,9 @@ get_length/1, make_atom/1, make_string/1, reverse_list_test/1, otp_9828/1, otp_9668/1, consume_timeslice/1, dirty_nif/1, dirty_nif_send/1, - dirty_nif_exception/1, nif_schedule/1, - nif_exception/1, nif_nan_and_inf/1, nif_atom_too_long/1 + dirty_nif_exception/1, call_dirty_nif_exception/1, nif_schedule/1, + nif_exception/1, call_nif_exception/1, + nif_nan_and_inf/1, nif_atom_too_long/1 ]). -export([many_args_100/100]). @@ -1387,7 +1388,7 @@ is_checks(Config) when is_list(Config) -> self(), hd(erlang:ports()), [], [1,9,9,8], {hejsan, "hejsan", [$h,"ejs",<<"an">>]}, -18446744073709551616.2e2), try - ?line error = check_is_exception(), + ?line check_is_exception(), ?line throw(expected_badarg) catch error:badarg -> @@ -1599,9 +1600,9 @@ dirty_nif_exception(Config) when is_list(Config) -> N when is_integer(N) -> ensure_lib_loaded(Config), try - %% this checks that the expected exception - %% occurs when the NIF returns the result - %% of enif_make_badarg directly + %% this checks that the expected exception occurs when the + %% dirty NIF returns the result of enif_make_badarg + %% directly call_dirty_nif_exception(1), ?t:fail(expected_badarg) catch @@ -1611,10 +1612,9 @@ dirty_nif_exception(Config) when is_list(Config) -> ok end, try - %% this checks that the expected exception - %% occurs when the NIF calls enif_make_badarg - %% at some point but then returns a value that - %% isn't an exception + %% this checks that the expected exception occurs when the + %% dirty NIF calls enif_make_badarg at some point but then + %% returns a value that isn't an exception call_dirty_nif_exception(0), ?t:fail(expected_badarg) catch @@ -1622,7 +1622,10 @@ dirty_nif_exception(Config) when is_list(Config) -> [{?MODULE,call_dirty_nif_exception,[0],_}|_] = erlang:get_stacktrace(), ok - end + end, + %% this checks that a dirty NIF can raise various terms as + %% exceptions + ok = nif_raise_exceptions(call_dirty_nif_exception) catch error:badarg -> {skipped,"No dirty scheduler support"} @@ -1631,12 +1634,18 @@ dirty_nif_exception(Config) when is_list(Config) -> nif_exception(Config) when is_list(Config) -> ensure_lib_loaded(Config), try - call_nif_exception(), + %% this checks that the expected exception occurs when the NIF + %% calls enif_make_badarg at some point but then tries to return a + %% value that isn't an exception + call_nif_exception(0), ?t:fail(expected_badarg) catch error:badarg -> ok - end. + end, + %% this checks that a NIF can raise various terms as exceptions + ok = nif_raise_exceptions(call_nif_exception), + ok. nif_nan_and_inf(Config) when is_list(Config) -> ensure_lib_loaded(Config), @@ -1758,7 +1767,20 @@ check(Exp,Got,Line) -> io:format("CHECK at ~p: Expected ~p but got ~p\n",[Line,Exp,Got]), Got end. - + +nif_raise_exceptions(NifFunc) -> + ExcTerms = [{error, test}, "a string", <<"a binary">>, + 42, [1,2,3,4,5], [{p,1},{p,2},{p,3}]], + lists:foldl(fun(Term, ok) -> + try + erlang:apply(?MODULE,NifFunc,[Term]), + ?t:fail({expected,Term}) + catch + error:Term -> + [{?MODULE,NifFunc,[Term],_}|_] = erlang:get_stacktrace(), + ok + end + end, ok, ExcTerms). %% The NIFs: lib_version() -> undefined. @@ -1814,7 +1836,7 @@ call_dirty_nif(_,_,_) -> ?nif_stub. send_from_dirty_nif(_) -> ?nif_stub. call_dirty_nif_exception(_) -> ?nif_stub. call_dirty_nif_zero_args() -> ?nif_stub. -call_nif_exception() -> ?nif_stub. +call_nif_exception(_) -> ?nif_stub. call_nif_nan_or_inf(_) -> ?nif_stub. call_nif_atom_too_long(_) -> ?nif_stub. diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 3cc9f51ef8..1639e47d61 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -883,16 +883,19 @@ static ERL_NIF_TERM check_is(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] * * This function is separate from check_is because it calls enif_make_badarg * and so it must return the badarg exception as its return value. Thus, the - * badarg exception indicates success. Failure is indicated by returning an - * error atom. + * badarg exception indicates success. */ static ERL_NIF_TERM check_is_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + ERL_NIF_TERM badarg, exc_term; ERL_NIF_TERM error_atom = enif_make_atom(env, "error"); - ERL_NIF_TERM badarg = enif_make_badarg(env); - if (enif_is_exception(env, error_atom)) return error_atom; - if (!enif_is_exception(env, badarg)) return error_atom; - if (!enif_has_pending_exception(env)) return error_atom; + ERL_NIF_TERM badarg_atom = enif_make_atom(env, "badarg"); + assert(!enif_is_exception(env, error_atom)); + badarg = enif_make_badarg(env); + assert(enif_is_exception(env, badarg)); + assert(enif_has_pending_exception(env, NULL)); + assert(enif_has_pending_exception(env, &exc_term)); + assert(enif_is_identical(exc_term, badarg_atom)); return badarg; } @@ -1536,9 +1539,12 @@ static ERL_NIF_TERM nif_sched1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv static ERL_NIF_TERM call_nif_schedule(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + ERL_NIF_TERM result; if (argc != 2) return enif_make_atom(env, "false"); - return enif_schedule_nif(env, "nif_sched1", 0, nif_sched1, argc, argv); + result = enif_schedule_nif(env, "nif_sched1", 0, nif_sched1, argc, argv); + assert(!enif_is_exception(env, result)); + return result; } #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT @@ -1612,13 +1618,18 @@ static ERL_NIF_TERM call_dirty_nif_exception(ErlNifEnv* env, int argc, const ERL { switch (argc) { case 1: { - ERL_NIF_TERM args[255]; - int i; - args[0] = argv[0]; - for (i = 1; i < 255; i++) - args[i] = enif_make_int(env, i); - return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND, - call_dirty_nif_exception, 255, argv); + int arg; + if (enif_get_int(env, argv[0], &arg) && arg < 2) { + ERL_NIF_TERM args[255]; + int i; + args[0] = argv[0]; + for (i = 1; i < 255; i++) + args[i] = enif_make_int(env, i); + return enif_schedule_nif(env, "call_dirty_nif_exception", ERL_NIF_DIRTY_JOB_CPU_BOUND, + call_dirty_nif_exception, 255, args); + } else { + return enif_raise_exception(env, argv[0]); + } } case 2: { int return_badarg_directly; @@ -1651,14 +1662,32 @@ static ERL_NIF_TERM call_dirty_nif_zero_args(ErlNifEnv* env, int argc, const ERL #endif /* - * Call enif_make_badarg, but don't return its return value. Instead, - * return ok. Result should still be a badarg exception for the erlang - * caller. + * If argv[0] is the integer 0, call enif_make_badarg, but don't return its + * return value. Instead, return ok. Result should still be a badarg + * exception for the erlang caller. + * + * For any other value of argv[0], use it as an exception term and return + * the exception. */ static ERL_NIF_TERM call_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - /* ignore return value */ enif_make_badarg(env); - return enif_make_atom(env, "ok"); + ERL_NIF_TERM exc_term; + ERL_NIF_TERM badarg_atom = enif_make_atom(env, "badarg"); + int arg; + + if (enif_get_int(env, argv[0], &arg) && arg == 0) { + /* ignore return value */ enif_make_badarg(env); + assert(enif_has_pending_exception(env, NULL)); + assert(enif_has_pending_exception(env, &exc_term)); + assert(enif_is_identical(badarg_atom, exc_term)); + return enif_make_atom(env, "ok"); + } else { + ERL_NIF_TERM exc_retval = enif_raise_exception(env, argv[0]); + assert(enif_has_pending_exception(env, NULL)); + assert(enif_has_pending_exception(env, &exc_term)); + assert(enif_is_identical(argv[0], exc_term)); + return exc_retval; + } } #if !defined(NAN) || !defined(INFINITY) @@ -1693,7 +1722,7 @@ static ERL_NIF_TERM call_nif_nan_or_inf(ErlNifEnv* env, int argc, const ERL_NIF_ } res = enif_make_double(env, val); assert(enif_is_exception(env, res)); - assert(enif_has_pending_exception(env)); + assert(enif_has_pending_exception(env, NULL)); if (strcmp(arg, "tuple") == 0) { return enif_make_tuple2(env, argv[0], res); } else { @@ -1802,7 +1831,7 @@ static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ER if (argc != 1 && !enif_is_map(env, map)) return enif_make_int(env, __LINE__); - if(!enif_map_iterator_create(env, map, &iter_f, ERL_NIF_MAP_ITERATOR_HEAD)) + if(!enif_map_iterator_create(env, map, &iter_f, ERL_NIF_MAP_ITERATOR_FIRST)) return enif_make_int(env, __LINE__); cnt = 0; @@ -1817,7 +1846,7 @@ static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ER if (cnt && next_ret) return enif_make_int(env, __LINE__); - if(!enif_map_iterator_create(env, map, &iter_b, ERL_NIF_MAP_ITERATOR_TAIL)) + if(!enif_map_iterator_create(env, map, &iter_b, ERL_NIF_MAP_ITERATOR_LAST)) return enif_make_int(env, __LINE__); cnt = 0; @@ -1913,7 +1942,7 @@ static ErlNifFunc nif_funcs[] = {"call_dirty_nif_exception", 1, call_dirty_nif_exception, ERL_NIF_DIRTY_JOB_IO_BOUND}, {"call_dirty_nif_zero_args", 0, call_dirty_nif_zero_args, ERL_NIF_DIRTY_JOB_CPU_BOUND}, #endif - {"call_nif_exception", 0, call_nif_exception}, + {"call_nif_exception", 1, call_nif_exception}, {"call_nif_nan_or_inf", 1, call_nif_nan_or_inf}, {"call_nif_atom_too_long", 1, call_nif_atom_too_long}, {"is_map_nif", 1, is_map_nif}, diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 23226909a7..d6544a2829 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -1172,7 +1172,7 @@ usage_aux(void) "[+S NO_SCHEDULERS:NO_SCHEDULERS_ONLINE] " "[+SP PERCENTAGE_SCHEDULERS:PERCENTAGE_SCHEDULERS_ONLINE] " "[+T LEVEL] [+V] [+v] " - "[+W<i|w>] [+z MISC_OPTION] [args ...]\n"); + "[+W<i|w|e>] [+z MISC_OPTION] [args ...]\n"); exit(1); } diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index 79ff013c77..622e1be869 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -127,37 +127,18 @@ drv2protocol(_) -> undefined. %% TODO: shutdown equivalent for SCTP %% shutdown(S, read) when is_port(S) -> - shutdown_2(S, 0); + shutdown_1(S, 0); shutdown(S, write) when is_port(S) -> shutdown_1(S, 1); shutdown(S, read_write) when is_port(S) -> shutdown_1(S, 2). shutdown_1(S, How) -> - case subscribe(S, [subs_empty_out_q]) of - {ok,[{subs_empty_out_q,N}]} when N > 0 -> - shutdown_pend_loop(S, N); %% wait for pending output to be sent - _Other -> ok - end, - shutdown_2(S, How). - -shutdown_2(S, How) -> case ctl_cmd(S, ?TCP_REQ_SHUTDOWN, [How]) of {ok, []} -> ok; {error,_}=Error -> Error end. -shutdown_pend_loop(S, N0) -> - receive - {empty_out_q,S} -> ok - after ?INET_CLOSE_TIMEOUT -> - case getstat(S, [send_pend]) of - {ok,[{send_pend,N0}]} -> ok; - {ok,[{send_pend,N}]} -> shutdown_pend_loop(S, N); - _ -> ok - end - end. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% CLOSE(insock()) -> ok diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk index c909c908d6..d4c46863a3 100644 --- a/lib/asn1/vsn.mk +++ b/lib/asn1/vsn.mk @@ -1 +1 @@ -ASN1_VSN = 3.0.4 +ASN1_VSN = 4.0 diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml index 5fccdcdcb5..a271729c82 100644 --- a/lib/compiler/doc/src/compile.xml +++ b/lib/compiler/doc/src/compile.xml @@ -32,15 +32,15 @@ <modulesummary>Erlang Compiler</modulesummary> <description> <p>This module provides an interface to the standard Erlang - compiler. It can generate either a new file which contains - the object code, or return a binary which can be loaded directly. + compiler. It can generate either a new file, which contains + the object code, or return a binary, which can be loaded directly. </p> </description> <funcs> <func> <name>file(File)</name> - <fsummary>Compile a file</fsummary> + <fsummary>Compiles a file.</fsummary> <desc> <p>Is the same as <c>file(File, [verbose,report_errors,report_warnings])</c>. @@ -50,7 +50,7 @@ <func> <name>file(File, Options) -> CompRet</name> - <fsummary>Compile a file</fsummary> + <fsummary>Compiles a file.</fsummary> <type> <v>CompRet = ModRet | BinRet | ErrRet</v> <v>ModRet = {ok,ModuleName} | {ok,ModuleName,Warnings}</v> @@ -64,39 +64,38 @@ <p>Returns <c>{ok,ModuleName}</c> if successful, or <c>error</c> if there are errors. An object code file is created if - the compilation succeeds with no errors. It is considered + the compilation succeeds without errors. It is considered to be an error if the module name in the source code is not the same as the basename of the output file.</p> - <p><marker id="type-option"/>Here follows first all elements of <c>Options</c> that in - some way control the behavior of the compiler.</p> + <p><marker id="type-option"/>Available options:</p> <taglist> <tag><c>basic_validation</c></tag> <item> - <p>This option is fast way to test whether a module will - compile successfully (mainly useful for code generators - that want to verify the code they emit). No code will + <p>This option is a fast way to test whether a module will + compile successfully. This is useful for code generators + that want to verify the code that they emit. No code is generated. If warnings are enabled, warnings generated by the <c>erl_lint</c> module (such as warnings for unused - variables and functions) will be returned too.</p> + variables and functions) are also returned.</p> - <p>Use the <c>strong_validation</c> option to generate all + <p>Use option <c>strong_validation</c> to generate all warnings that the compiler would generate.</p> </item> <tag><c>strong_validation</c></tag> <item> - <p>Similar to the <c>basic_validation</c> option, no code - will be generated, but more compiler passes will be run - to ensure also warnings generated by the optimization - passes are generated (such as clauses that will not match + <p>Similar to option <c>basic_validation</c>. No code + is generated, but more compiler passes are run + to ensure that warnings generated by the optimization + passes are generated (such as clauses that will not match, or expressions that are guaranteed to fail with an - exception at run-time).</p> + exception at runtime).</p> </item> <tag><c>binary</c></tag> <item> - <p>Causes the compiler to return the object code in a + <p>The compiler returns the object code in a binary instead of creating an object file. If successful, the compiler returns <c>{ok,ModuleName,Binary}</c>.</p> </item> @@ -105,7 +104,9 @@ <item> <p>The compiler will emit informational warnings about binary matching optimizations (both successful and unsuccessful). - See the <em>Efficiency Guide</em> for further information.</p> + For more information, see the section about + <seealso marker="doc/efficiency_guide:binaryhandling#bin_opt_info">bin_opt_info</seealso> + in the Efficiency Guide.</p> </item> <tag><c>compressed</c></tag> @@ -117,20 +118,19 @@ <tag><c>debug_info</c></tag> <item> <marker id="debug_info"></marker> - <p>Include debug information in the form of abstract code + <p>Includes debug information in the form of abstract code (see <seealso marker="erts:absform">The Abstract Format</seealso> in ERTS User's Guide) in the compiled beam module. Tools - such as Debugger, Xref and Cover require the debug - information to be included.</p> + such as <c>Debugger</c>, <c>Xref</c>, and <c>Cover</c> require + the debug information to be included.</p> <p><em>Warning</em>: Source code can be reconstructed from the debug information. Use encrypted debug information - (see below) to prevent this.</p> + (<c>encrypt_debug_info</c>) to prevent this.</p> - <p>See - <seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso> - for details.</p> + <p>For details, see + <seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso>.</p> </item> <tag><c>{debug_info_key,KeyString}</c></tag> @@ -138,65 +138,61 @@ <tag><c>{debug_info_key,{Mode,KeyString}}</c></tag> <item> <marker id="debug_info_key"></marker> - <p>Include debug information, but encrypt it, so that it + <p>Includes debug information, but encrypts it so that it cannot be accessed without supplying the key. (To give - the <c>debug_info</c> option as well is allowed, but is + option <c>debug_info</c> as well is allowed, but not necessary.) Using this option is a good way to always have the debug information available during testing, yet - protect the source code.</p> + protecting the source code.</p> <p><c>Mode</c> is the type of crypto algorithm to be used - for encrypting the debug information. The default type -- - and currently the only type -- is <c>des3_cbc</c>.</p> - <p>See - <seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso> - for details.</p> + for encrypting the debug information. The default + (and currently the only) type is <c>des3_cbc</c>.</p> + <p>For details, see + <seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso>.</p> </item> <tag><c>encrypt_debug_info</c></tag> <item> <marker id="encrypt_debug_info"></marker> - <p>Like the <c>debug_info_key</c> option above, except that - the key will be read from an <c>.erlang.crypt</c> file. + <p>Similar to the <c>debug_info_key</c> option, but + the key is read from an <c>.erlang.crypt</c> file. </p> - <p>See - <seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso> - for details.</p> + <p>For details, see + <seealso marker="stdlib:beam_lib#debug_info">beam_lib(3)</seealso>.</p> </item> <tag><c>makedep</c></tag> <item> - <p>Produce a Makefile rule to track headers dependencies. + <p>Produces a Makefile rule to track headers dependencies. No object file is produced. </p> <p>By default, this rule is written to - <c><![CDATA[<File>.Pbeam]]></c>. However, if the option + <c><![CDATA[<File>.Pbeam]]></c>. However, if option <c>binary</c> is set, nothing is written and the rule is returned in <c>Binary</c>. </p> - <p>For instance, if one has the following module: + <p>For example, if you have the following module: </p> <code> -module(module). -include_lib("eunit/include/eunit.hrl"). --include("header.hrl"). - </code> - <p>Here is the Makefile rule generated by this option: +-include("header.hrl").</code> + <p>The Makefile rule generated by this option looks as follows: </p> <code> module.beam: module.erl \ /usr/local/lib/erlang/lib/eunit/include/eunit.hrl \ - header.hrl - </code> + header.hrl</code> </item> <tag><c>{makedep_output, Output}</c></tag> <item> - <p>Write generated rule(s) to <c>Output</c> instead of the + <p>Writes generated rules to <c>Output</c> instead of the default <c><![CDATA[<File>.Pbeam]]></c>. <c>Output</c> can be a filename or an <c>io_device()</c>. To write to - stdout, use <c>standard_io</c>. However if <c>binary</c> + stdout, use <c>standard_io</c>. However, if <c>binary</c> is set, nothing is written to <c>Output</c> and the result is returned to the caller with <c>{ok, ModuleName, Binary}</c>. @@ -205,7 +201,7 @@ module.beam: module.erl \ <tag><c>{makedep_target, Target}</c></tag> <item> - <p>Change the name of the rule emitted to <c>Target</c>. + <p>Changes the name of the rule emitted to <c>Target</c>. </p> </item> @@ -217,20 +213,20 @@ module.beam: module.erl \ <tag><c>makedep_add_missing</c></tag> <item> - <p>Consider missing headers as generated files and add them to the + <p>Considers missing headers as generated files and adds them to the dependencies. </p> </item> <tag><c>makedep_phony</c></tag> <item> - <p>Add a phony target for each dependency. + <p>Adds a phony target for each dependency. </p> </item> <tag><c>'P'</c></tag> <item> - <p>Produces a listing of the parsed code after preprocessing + <p>Produces a listing of the parsed code, after preprocessing and parse transforms, in the file <c><![CDATA[<File>.P]]></c>. No object file is produced. </p> @@ -238,7 +234,7 @@ module.beam: module.erl \ <tag><c>'E'</c></tag> <item> - <p>Produces a listing of the code after all source code + <p>Produces a listing of the code, after all source code transformations have been performed, in the file <c><![CDATA[<File>.E]]></c>. No object file is produced. </p> @@ -258,21 +254,21 @@ module.beam: module.erl \ <tag><c>report</c></tag> <item> - <p>This is a short form for both <c>report_errors</c> and + <p>A short form for both <c>report_errors</c> and <c>report_warnings</c>.</p> </item> <tag><c>return_errors</c></tag> <item> - <p>If this flag is set, then + <p>If this flag is set, <c>{error,ErrorList,WarningList}</c> is returned when there are errors.</p> </item> <tag><c>return_warnings</c></tag> <item> - <p>If this flag is set, then an extra field containing - <c>WarningList</c> is added to the tuples returned on + <p>If this flag is set, an extra field, containing + <c>WarningList</c>, is added to the tuples returned on success.</p> </item> @@ -284,13 +280,13 @@ module.beam: module.erl \ <tag><c>return</c></tag> <item> - <p>This is a short form for both <c>return_errors</c> and + <p>A short form for both <c>return_errors</c> and <c>return_warnings</c>.</p> </item> <tag><c>verbose</c></tag> <item> - <p>Causes more verbose information from the compiler + <p>Causes more verbose information from the compiler, describing what it is doing.</p> </item> @@ -314,7 +310,7 @@ module.beam: module.erl \ <tag><c>{i,Dir}</c></tag> <item> - <p>Add <c>Dir</c> to the list of directories to be searched + <p>Adds <c>Dir</c> to the list of directories to be searched when including a file. When encountering an <c>-include</c> or <c>-include_lib</c> directive, the compiler searches for header files in the following @@ -322,14 +318,14 @@ module.beam: module.erl \ <list type="ordered"> <item> <p><c>"."</c>, the current working directory of - the file server;</p> + the file server</p> </item> <item> - <p>the base name of the compiled file;</p> + <p>The base name of the compiled file</p> </item> <item> - <p>the directories specified using the <c>i</c> option. - The directory specified last is searched first.</p> + <p>The directories specified using option <c>i</c>; + the directory specified last is searched first</p> </item> </list> </item> @@ -353,15 +349,15 @@ module.beam: module.erl \ <tag><c>from_asm</c></tag> <item> <p>The input file is expected to be assembler code (default - file suffix ".S"). Note that the format of assembler files - is not documented, and may change between releases.</p> + file suffix ".S"). Notice that the format of assembler files + is not documented, and can change between releases.</p> </item> <tag><c>from_core</c></tag> <item> <p>The input file is expected to be core code (default - file suffix ".core"). Note that the format of core files - is not documented, and may change between releases.</p> + file suffix ".core"). Notice that the format of core files + is not documented, and can change between releases.</p> </item> <tag><c>no_strict_record_tests</c></tag> @@ -369,9 +365,9 @@ module.beam: module.erl \ <p>This option is not recommended.</p> <p>By default, the generated code for - the <c>Record#record_tag.field</c> operation verifies that - the tuple <c>Record</c> is of the correct size for - the record and that the first element is the tag + operation <c>Record#record_tag.field</c> verifies that + the tuple <c>Record</c> has the correct size for + the record, and that the first element is the tag <c>record_tag</c>. Use this option to omit the verification code.</p> </item> @@ -390,79 +386,87 @@ module.beam: module.erl \ <tag><c>{no_auto_import,[{F,A}, ...]}</c></tag> <item> <p>Makes the function <c>F/A</c> no longer being - auto-imported from the module <c>erlang</c>, which resolves - BIF name clashes. This option has to be used to resolve name - clashes with BIFs auto-imported before R14A, if one wants to + auto-imported from the <c>erlang</c> module, which resolves + BIF name clashes. This option must be used to resolve name + clashes with BIFs auto-imported before R14A, if it is needed to call the local function with the same name as an auto-imported BIF without module prefix.</p> <note> - <p>From R14A and forward, the compiler resolves calls + <p>As from R14A and forward, the compiler resolves calls without module prefix to local or imported functions before - trying auto-imported BIFs. If the BIF is to be + trying with auto-imported BIFs. If the BIF is to be called, use the <c>erlang</c> module prefix in the call, not - <c>{ no_auto_import,[{F,A}, ...]}</c></p> + <c>{ no_auto_import,[{F,A}, ...]}</c>.</p> </note> <p>If this option is written in the source code, as a <c>-compile</c> directive, the syntax <c>F/A</c> can be used instead - of <c>{F,A}</c>. Example:</p> + of <c>{F,A}</c>, for example:</p> <code>-compile({no_auto_import,[error/1]}).</code> </item> <tag><c>no_auto_import</c></tag> <item> - <p>Do not auto import any functions from the module <c>erlang</c>.</p> + <p>Do not auto-import any functions from <c>erlang</c> module.</p> </item> <tag><c>no_line_info</c></tag> <item> - <p>Omit line number information in order to produce a slightly + <p>Omits line number information to produce a slightly smaller output file. </p> </item> </taglist> - <p>If warnings are turned on (the <c>report_warnings</c> option - described above), the following options control what type of - warnings that will be generated. + <p>If warnings are turned on (option <c>report_warnings</c> + described earlier), the following options control what type of + warnings that are generated. <marker id="erl_lint_options"></marker> - With the exception of <c>{warn_format,Verbosity}</c> all - options below have two forms; one <c>warn_xxx</c> form to - turn on the warning and one <c>nowarn_xxx</c> form to turn off - the warning. In the description that follows, the form that - is used to change the default value is listed.</p> + Except from <c>{warn_format,Verbosity}</c>, the following options + have two forms:</p> + <list type="bulleted"> + <item>A <c>warn_xxx</c> form, to turn on the warning.</item> + <item>A <c>nowarn_xxx</c> form, to turn off the warning.</item> + </list> + <p>In the descriptions that follow, the form that is used to change + the default value are listed.</p> <taglist> <tag><c>{warn_format, Verbosity}</c></tag> <item> <p>Causes warnings to be emitted for malformed format strings as arguments to <c>io:format</c> and similar - functions. <c>Verbosity</c> selects the amount of - warnings: 0 = no warnings; 1 = warnings for invalid - format strings and incorrect number of arguments; 2 = - warnings also when the validity could not be checked - (for example, when the format string argument is a - variable). The default verbosity is 1. Verbosity 0 can - also be selected by the option <c>nowarn_format</c>.</p> + functions.</p> + <p><c>Verbosity</c> selects the number of warnings:</p> + <list type="bulleted"> + <item><c>0</c> = No warnings</item> + <item><c>1</c> = Warnings for invalid format strings and incorrect + number of arguments</item> + <item><c>2</c> = Warnings also when the validity cannot + be checked, for example, when the format string argument is a + variable.</item> + </list> + <p>The default verbosity is <c>1</c>. Verbosity <c>0</c> can + also be selected by option <c>nowarn_format</c>.</p> </item> <tag><c>nowarn_bif_clash</c></tag> <item> - <p>This option is removed, it will generate a fatal error if used.</p> + <p>This option is removed, it generates a fatal error if used.</p> <warning> - <p>Beginning with R14A, the compiler no longer calls the + <p>As from beginning with R14A, the compiler no longer calls the auto-imported BIF if the name clashes with a local or - explicitly imported function and a call without explicit - module name is issued. Instead the local or imported - function is called. Still accepting <c>nowarn_bif_clash</c> would makes a - module calling functions clashing with autoimported BIFs + explicitly imported function, and a call without explicit + module name is issued. Instead, the local or imported + function is called. Still accepting <c>nowarn_bif_clash</c> would + make a module calling functions clashing with auto-imported BIFs compile with both the old and new compilers, but with - completely different semantics, why the option was removed.</p> + completely different semantics. This is why the option is removed.</p> - <p>The use of this option has always been strongly discouraged. - From OTP R14A and forward it's an error to use it.</p> + <p>The use of this option has always been discouraged. + As from R14A, it is an error to use it.</p> <p>To resolve BIF clashes, use explicit module names or the <c>{no_auto_import,[F/A]}</c> compiler directive.</p> </warning> @@ -470,11 +474,11 @@ module.beam: module.erl \ <tag><c>{nowarn_bif_clash, FAs}</c></tag> <item> - <p>This option is removed, it will generate a fatal error if used.</p> + <p>This option is removed, it generates a fatal error if used.</p> <warning> - <p>The use of this option has always been strongly discouraged. - From OTP R14A and forward it's an error to use it.</p> + <p>The use of this option has always been discouraged. + As from R14A, it is an error to use it.</p> <p>To resolve BIF clashes, use explicit module names or the <c>{no_auto_import,[F/A]}</c> compiler directive.</p> </warning> @@ -482,35 +486,29 @@ module.beam: module.erl \ <tag><c>warn_export_all</c></tag> <item> - <p>Causes a warning to be emitted if the <c>export_all</c> - option has also been given.</p> + <p>Emits a warning if option <c>export_all</c> is also given.</p> </item> <tag><c>warn_export_vars</c></tag> <item> - <p>Causes warnings to be emitted for all implicitly - exported variables referred to after the primitives - where they were first defined. No warnings for exported - variables unless they are referred to in some pattern, - which is the default, can be selected by the option - <c>nowarn_export_vars</c>.</p> + <p>Emits warnings for all implicitly exported variables + referred to after the primitives where they were first defined. + By default, the compiler only emits warnings for exported + variables referred to in a pattern.</p> </item> - <tag><c>warn_shadow_vars</c></tag> + <tag><c>nowarn_shadow_vars</c></tag> <item> - <p>Causes warnings to be emitted for "fresh" variables - in functional objects or list comprehensions with the same - name as some already defined variable. The default is to - warn for such variables. No warnings for shadowed - variables can be selected by the option - <c>nowarn_shadow_vars</c>.</p> + <p>Turns off warnings for "fresh" variables + in functional objects or list comprehensions with the same + name as some already defined variable. Default is to + emit warnings for such variables.</p> </item> <tag><c>nowarn_unused_function</c></tag> <item> - <p>Turns off warnings for unused local functions. - By default (<c>warn_unused_function</c>), warnings are - emitted for all local functions that are not called + <p>Turns off warnings for unused local functions. Default + is to emit warnings for all local functions that are not called directly or indirectly by an exported function. The compiler does not include unused local functions in the generated beam file, but the warning is still useful @@ -519,148 +517,142 @@ module.beam: module.erl \ <tag><c>{nowarn_unused_function, FAs}</c></tag> <item> - <p>Turns off warnings for unused local functions as - <c>nowarn_unused_function</c> but only for the mentioned + <p>Turns off warnings for unused local functions like + <c>nowarn_unused_function</c> does, but only for the mentioned local functions. <c>FAs</c> is a tuple <c>{Name,Arity}</c> or a list of such tuples.</p> </item> <tag><c>nowarn_deprecated_function</c></tag> <item> - <p>Turns off warnings for calls to deprecated functions. By - default (<c>warn_deprecated_function</c>), warnings are - emitted for every call to a function known by the compiler - to be deprecated. Note that the compiler does not know - about the <c>-deprecated()</c> attribute but uses an + <p>Turns off warnings for calls to deprecated functions. Default + is to emit warnings for every call to a function known by the + compiler to be deprecated. Notice that the compiler does not know + about attribute <c>-deprecated()</c>, but uses an assembled list of deprecated functions in Erlang/OTP. To - do a more general check the <c>Xref</c> tool can be used. + do a more general check, the <c>Xref</c> tool can be used. See also <seealso marker="tools:xref#deprecated_function">xref(3)</seealso> and the function - <seealso marker="tools:xref#m/1">xref:m/1</seealso> also - accessible through - the <seealso marker="stdlib:c#xm/1">c:xm/1</seealso> - function.</p> + <seealso marker="tools:xref#m/1">xref:m/1</seealso>, also + accessible through the function + <seealso marker="stdlib:c#xm/1">c:xm/1</seealso>.</p> </item> <tag><c>{nowarn_deprecated_function, MFAs}</c></tag> <item> - <p>Turns off warnings for calls to deprecated functions as - <c>nowarn_deprecated_function</c> but only for + <p>Turns off warnings for calls to deprecated functions like + <c>nowarn_deprecated_function</c> does, but only for the mentioned functions. <c>MFAs</c> is a tuple <c>{Module,Name,Arity}</c> or a list of such tuples.</p> </item> <tag><c>nowarn_deprecated_type</c></tag> <item> - <p>Turns off warnings for uses of deprecated types. By - default (<c>warn_deprecated_type</c>), warnings are - emitted for every use of a type known by the compiler - to be deprecated.</p> + <p>Turns off warnings for use of deprecated types. Default + is to emit warnings for every use of a type known by the compiler + to be deprecated.</p> </item> <tag><c>warn_obsolete_guard</c></tag> <item> - <p>Causes warnings to be emitted for calls to old type - testing BIFs such as <c>pid/1</c> and <c>list/1</c>. See - the - <seealso marker="doc/reference_manual:expressions#guards">Erlang Reference Manual</seealso> + <p>Emits warnings for calls to old type testing BIFs, + such as <c>pid/1</c> and <c>list/1</c>. See the + <seealso marker="doc/reference_manual:expressions#guards">Erlang Reference Manual</seealso> for a complete list of type testing BIFs and their old - equivalents. No warnings for calls to old type testing - BIFs, which is the default, can be selected by the option - <c>nowarn_obsolete_guard</c>.</p> + equivalents. Default is to emit no warnings for calls to + old type testing BIFs.</p> </item> <tag><c>warn_unused_import</c></tag> <item> - <p>Causes warnings to be emitted for unused imported - functions. No warnings for unused imported functions, - which is the default, can be selected by the option - <c>nowarn_unused_import</c>. </p> + <p>Emits warnings for unused imported functions. + Default is to emit no warnings for unused imported functions.</p> </item> <tag><c>nowarn_unused_vars</c></tag> <item> - <p>By default, warnings are emitted for variables which - are not used, with the exception of variables beginning - with an underscore ("Prolog style warnings"). + <p>By default, warnings are emitted for unused variables, + except for variables beginning with an underscore + ("Prolog style warnings"). Use this option to turn off this kind of warnings.</p> </item> <tag><c>nowarn_unused_record</c></tag> <item> - <p>Turns off warnings for unused record types. By - default (<c>warn_unused_records</c>), warnings are - emitted for unused locally defined record types.</p> + <p>Turns off warnings for unused record types. Default is to + emit warnings for unused locally defined record types.</p> </item> </taglist> <p>Another class of warnings is generated by the compiler during optimization and code generation. They warn about patterns that will never match (such as <c>a=b</c>), guards - that will always evaluate to false, and expressions that will + that always evaluate to false, and expressions that always fail (such as <c>atom+42</c>).</p> - - <p>Note that the compiler does not warn for expressions that it - does not attempt to optimize. For instance, the compiler tries - to evaluate <c>1/0</c>, notices that it will cause an - exception and emits a warning. On the other hand, - the compiler is silent about the similar expression - <c>X/0</c>; because of the variable in it, the compiler does - not even try to evaluate and therefore it emits no warnings. - </p> - - <p>Currently, those warnings cannot be disabled (except by + <p>Those warnings cannot be disabled (except by disabling all warnings).</p> + <note> + <p>The compiler does not warn for expressions that it + does not attempt to optimize. For example, the compiler tries + to evaluate <c>1/0</c>, detects that it will cause an + exception, and emits a warning. However, + the compiler is silent about the similar expression, + <c>X/0</c>, because of the variable in it. Thus, the compiler does + not even try to evaluate and therefore it emits no warnings.</p> + </note> + <warning> - <p>Obviously, the absence of warnings does not mean that + <p>The absence of warnings does not mean that there are no remaining errors in the code.</p> </warning> - - <p>Note that all the options except the include path - (<c>{i,Dir}</c>) can also be given in the file with a - <c>-compile([Option,...])</c>. attribute. - The <c>-compile()</c> attribute is allowed after function + + <note> + <p>All options, except the include path + (<c>{i,Dir}</c>), can also be given in the file with attribute + <c>-compile([Option,...])</c>. + Attribute <c>-compile()</c> is allowed after the function definitions.</p> - - <p>Note also that the <c>{nowarn_unused_function, FAs}</c>, + </note> + + <note> + <p>The options <c>{nowarn_unused_function, FAs}</c>, <c>{nowarn_bif_clash, FAs}</c>, and - <c>{nowarn_deprecated_function, MFAs}</c> options are only + <c>{nowarn_deprecated_function, MFAs}</c> are only recognized when given in files. They are not affected by - the <c>warn_unused_function</c>, <c>warn_bif_clash</c>, or - <c>warn_deprecated_function</c> options.</p> + options <c>warn_unused_function</c>, <c>warn_bif_clash</c>, or + <c>warn_deprecated_function</c>.</p> + </note> <p>For debugging of the compiler, or for pure curiosity, the intermediate code generated by each compiler pass can be inspected. - A complete list of the options to produce list files can be - printed by typing <c>compile:options()</c> at the Erlang - shell prompt. - The options will be printed in order that the passes are + To print a complete list of the options to produce list files, + type <c>compile:options()</c> at the Erlang shell prompt. + The options are printed in the order that the passes are executed. If more than one listing option is used, the one representing the earliest pass takes effect.</p> - <p><em>Unrecognized options are ignored.</em></p> + <p>Unrecognized options are ignored.</p> <p>Both <c>WarningList</c> and <c>ErrorList</c> have the following format:</p> <code> -[{FileName,[ErrorInfo]}]. - </code> - - <p><c>ErrorInfo</c> is described below. The file name has been - included here as the compiler uses the Erlang pre-processor - <c>epp</c>, which allows the code to be included in other - files. For this reason, it is important to know to - <em>which</em> file an error or warning line number refers. +[{FileName,[ErrorInfo]}].</code> + + <p><c>ErrorInfo</c> is described later in this section. + The filename is included here, as the compiler uses the + Erlang pre-processor <c>epp</c>, which allows the code to be + included in other files. It is therefore important to know to + <em>which</em> file the line number of an error or a warning refers. </p> </desc> </func> <func> <name>forms(Forms)</name> - <fsummary>Compile a list of forms</fsummary> + <fsummary>Compiles a list of forms.</fsummary> <desc> <p>Is the same as <c>forms(File, [verbose,report_errors,report_warnings])</c>. @@ -670,7 +662,7 @@ module.beam: module.erl \ <func> <name>forms(Forms, Options) -> CompRet</name> - <fsummary>Compile a list of forms</fsummary> + <fsummary>Compiles a list of forms.</fsummary> <type> <v>Forms = [Form]</v> <v>CompRet = BinRet | ErrRet</v> @@ -681,48 +673,49 @@ module.beam: module.erl \ <desc> <p>Analogous to <c>file/1</c>, but takes a list of forms (in the Erlang abstract format representation) as first argument. - The option <c>binary</c> is implicit; i.e., no object code - file is produced. Options that would ordinarily produce a - listing file, such as 'E', will instead cause the internal - format for that compiler pass (an Erlang term; usually not a - binary) to be returned instead of a binary.</p> + Option <c>binary</c> is implicit, that is, no object code + file is produced. For options that normally produce a listing + file, such as 'E', the internal format for that compiler pass + (an Erlang term, usually not a binary) is returned instead of + a binary.</p> </desc> </func> <func> <name>format_error(ErrorDescriptor) -> chars()</name> - <fsummary>Format an error descriptor</fsummary> + <fsummary>Formats an error descriptor.</fsummary> <type> <v>ErrorDescriptor = errordesc()</v> </type> <desc> <p>Uses an <c>ErrorDescriptor</c> and returns a deep list of - characters which describes the error. This function is - usually called implicitly when an <c>ErrorInfo</c> structure - is processed. See below.</p> + characters that describes the error. This function is + usually called implicitly when an <c>ErrorInfo</c> structure + (described in section + <seealso marker="#error_information">Error Information</seealso>) is processed.</p> </desc> </func> <func> <name>output_generated(Options) -> true | false</name> - <fsummary>Determine whether the compile will generate an output file</fsummary> + <fsummary>Determines whether the compiler generates an output file.</fsummary> <type> <v>Options = [term()]</v> </type> <desc> - <p>Determines whether the compiler would generate a <c>beam</c> + <p>Determines whether the compiler generates a <c>beam</c> file with the given options. <c>true</c> means that a <c>beam</c> - file would be generated; <c>false</c> means that the compiler - would generate some listing file, return a binary, or merely - check the syntax of the source code.</p> + file is generated. <c>false</c> means that the compiler + generates some listing file, returns a binary, or merely + checks the syntax of the source code.</p> </desc> </func> <func> <name>noenv_file(File, Options) -> CompRet</name> - <fsummary>Compile a file (ignoring ERL_COMPILER_OPTIONS)</fsummary> + <fsummary>Compiles a file (ignoring <c>ERL_COMPILER_OPTIONS)</c>.</fsummary> <desc> - <p>Works exactly like <seealso marker="#file/2">file/2</seealso>, + <p>Works like <seealso marker="#file/2">file/2</seealso>, except that the environment variable <c>ERL_COMPILER_OPTIONS</c> is not consulted.</p> </desc> @@ -730,9 +723,9 @@ module.beam: module.erl \ <func> <name>noenv_forms(Forms, Options) -> CompRet</name> - <fsummary>Compile a list of forms (ignoring ERL_COMPILER_OPTIONS)</fsummary> + <fsummary>Compiles a list of forms (ignoring <c>ERL_COMPILER_OPTIONS)</c>.</fsummary> <desc> - <p>Works exactly like <seealso marker="#forms/2">forms/2</seealso>, + <p>Works like <seealso marker="#forms/2">forms/2</seealso>, except that the environment variable <c>ERL_COMPILER_OPTIONS</c> is not consulted.</p> </desc> @@ -740,12 +733,13 @@ module.beam: module.erl \ <func> <name>noenv_output_generated(Options) -> true | false</name> - <fsummary>Determine whether the compile will generate an output file (ignoring ERL_COMPILER_OPTIONS)</fsummary> + <fsummary>Determines whether the compiler generates an output file + (ignoring <c>ERL_COMPILER_OPTIONS)</c>.</fsummary> <type> <v>Options = [term()]</v> </type> <desc> - <p>Works exactly like + <p>Works like <seealso marker="#output_generated/1">output_generated/1</seealso>, except that the environment variable <c>ERL_COMPILER_OPTIONS</c> is not consulted.</p> @@ -755,14 +749,14 @@ module.beam: module.erl \ </funcs> <section> - <title>Default compiler options</title> + <title>Default Compiler Options</title> <p>The (host operating system) environment variable <c>ERL_COMPILER_OPTIONS</c> can be used to give default compiler options. Its value must be a valid Erlang term. If the value is a - list, it will be used as is. If it is not a list, it will be put + list, it is used as is. If it is not a list, it is put into a list.</p> - <p>The list will be appended to any options given to + <p>The list is appended to any options given to <seealso marker="#file/2">file/2</seealso>, <seealso marker="#forms/2">forms/2</seealso>, and <seealso marker="#output_generated/1">output_generated/2</seealso>. @@ -770,9 +764,9 @@ module.beam: module.erl \ <seealso marker="#noenv_file/2">noenv_file/2</seealso>, <seealso marker="#noenv_forms/2">noenv_forms/2</seealso>, or <seealso marker="#noenv_output_generated/1">noenv_output_generated/2</seealso> - if you don't want the environment variable to be consulted - (for instance, if you are calling the compiler recursively from - inside a parse transform).</p> + if you do not want the environment variable to be consulted, + for example, if you are calling the compiler recursively from + inside a parse transform.</p> </section> <section> @@ -781,31 +775,31 @@ module.beam: module.erl \ module. Inlining means that a call to a function is replaced with the function body with the arguments replaced with the actual values. The semantics are preserved, except if exceptions are - generated in the inlined code. Exceptions will be reported as + generated in the inlined code. Exceptions are reported as occurring in the function the body was inlined into. Also, - <c>function_clause</c> exceptions will be converted to similar + <c>function_clause</c> exceptions are converted to similar <c>case_clause</c> exceptions.</p> - <p>When a function is inlined, the original function will be + <p>When a function is inlined, the original function is kept if it is exported (either by an explicit export or if the - <c>export_all</c> option was given) or if not all calls to the - function were inlined.</p> + option <c>export_all</c> was given) or if not all calls to the + function are inlined.</p> <p>Inlining does not necessarily improve running time. - For instance, inlining may increase Beam stack usage which will - probably be detrimental to performance for recursive functions. + For example, inlining can increase Beam stack use, which + probably is detrimental to performance for recursive functions. </p> - <p>Inlining is never default; it must be explicitly enabled with a + <p>Inlining is never default. It must be explicitly enabled with a compiler option or a <c>-compile()</c> attribute in the source module.</p> - <p>To enable inlining, either use the <c>inline</c> option to - let the compiler decide which functions to inline or + <p>To enable inlining, either use the option <c>inline</c> to + let the compiler decide which functions to inline, or <c>{inline,[{Name,Arity},...]}</c> to have the compiler inline all calls to the given functions. If the option is given inside a <c>compile</c> directive in an Erlang module, <c>{Name,Arity}</c> - may be written as <c>Name/Arity</c>.</p> + can be written as <c>Name/Arity</c>.</p> <p>Example of explicit inlining:</p> @@ -817,33 +811,30 @@ pi() -> 3.1416. <p>Example of implicit inlining:</p> <pre> --compile(inline). - </pre> +-compile(inline).</pre> - <p>The <c>{inline_size,Size}</c> option controls how large functions - that are allowed to be inlined. Default is <c>24</c>, which will - keep the size of the inlined code roughly the same as - the un-inlined version (only relatively small functions will be + <p>The option <c>{inline_size,Size}</c> controls how large functions + that are allowed to be inlined. Default is <c>24</c>, which + keeps the size of the inlined code roughly the same as + the un-inlined version (only relatively small functions are inlined).</p> <p>Example:</p> <pre> %% Aggressive inlining - will increase code size. -compile(inline). --compile({inline_size,100}). - </pre> +-compile({inline_size,100}).</pre> </section> <section> - <title>Inlining of list functions</title> - <p>The compiler can also inline a variety of list manipulation functions - from the stdlib's lists module.</p> + <title>Inlining of List Functions</title> + <p>The compiler can also inline various list manipulation functions + from the module <c>list</c> in <c>STDLIB</c>.</p> <p>This feature must be explicitly enabled with a compiler option or a <c>-compile()</c> attribute in the source module.</p> - <p>To enable inlining of list functions, use the <c>inline_list_funcs</c> - option.</p> + <p>To enable inlining of list functions, use option <c>inline_list_funcs</c>.</p> <p>The following functions are inlined:</p> <list type="bulleted"> @@ -869,24 +860,23 @@ pi() -> 3.1416. </section> <section> + <marker id="error_information"></marker> <title>Error Information</title> - <p>The <c>ErrorInfo</c> mentioned above is the standard - <c>ErrorInfo</c> structure which is returned from all IO modules. + <p>The <c>ErrorInfo</c> mentioned earlier is the standard + <c>ErrorInfo</c> structure, which is returned from all I/O modules. It has the following format:</p> <code> -{ErrorLine, Module, ErrorDescriptor} - </code> +{ErrorLine, Module, ErrorDescriptor}</code> - <p><c>ErrorLine</c> will be the atom <c>none</c> if the error does - not correspond to a specific line (e.g. if the source file does - not exist).</p> + <p><c>ErrorLine</c> is the atom <c>none</c> if the error does + not correspond to a specific line, for example, if the source file does + not exist.</p> <p>A string describing the error is obtained with the following call:</p> <code> -Module:format_error(ErrorDescriptor) - </code> +Module:format_error(ErrorDescriptor)</code> </section> <section> diff --git a/lib/compiler/doc/src/ref_man.xml b/lib/compiler/doc/src/ref_man.xml index 6478ad4b11..6584e79c4e 100644 --- a/lib/compiler/doc/src/ref_man.xml +++ b/lib/compiler/doc/src/ref_man.xml @@ -29,7 +29,7 @@ <file>application.sgml</file> </header> <description> - <p>The <em>Compiler</em> application compiles Erlang + <p>The <c>Compiler</c> application compiles Erlang code to byte-code. The highly compact byte-code is executed by the Erlang emulator.</p> </description> diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile index 7c4cebdc28..78efc8dff0 100644 --- a/lib/compiler/src/Makefile +++ b/lib/compiler/src/Makefile @@ -70,6 +70,7 @@ MODULES = \ cerl \ cerl_clauses \ cerl_inline \ + cerl_sets \ cerl_trees \ compile \ core_lib \ diff --git a/lib/compiler/src/beam_dead.erl b/lib/compiler/src/beam_dead.erl index 5932d8ce1d..bbe607cf19 100644 --- a/lib/compiler/src/beam_dead.erl +++ b/lib/compiler/src/beam_dead.erl @@ -96,7 +96,7 @@ move_move_into_block([], Acc) -> reverse(Acc). %%% forward(Is, Lc) -> - forward(Is, gb_trees:empty(), Lc, []). + forward(Is, #{}, Lc, []). forward([{move,_,_}=Move|[{label,L}|_]=Is], D, Lc, Acc) -> %% move/2 followed by jump/1 is optimized by backward/3. @@ -115,19 +115,20 @@ forward([{label,Lbl}=LblI,{block,[{set,[Dst],[Lit],move}|BlkIs]}=Blk|Is], D, Lc, %% cannot be reached in any other way than through the select_val/3 %% instruction (i.e. there can be no fallthrough to such label and %% it cannot be referenced by, for example, a jump/1 instruction). - Block = case gb_trees:lookup({Lbl,Dst}, D) of - {value,Lit} -> {block,BlkIs}; %Safe to remove move instruction. - _ -> Blk %Must keep move instruction. - end, + Key = {Lbl,Dst}, + Block = case D of + #{Key := Lit} -> {block,BlkIs}; %Safe to remove move instruction. + _ -> Blk %Must keep move instruction. + end, forward([Block|Is], D, Lc, [LblI|Acc]); forward([{label,Lbl}=LblI|[{move,Lit,Dst}|Is1]=Is0], D, Lc, Acc) -> %% Assumption: The target labels in a select_val/3 instruction %% cannot be reached in any other way than through the select_val/3 %% instruction (i.e. there can be no fallthrough to such label and %% it cannot be referenced by, for example, a jump/1 instruction). - Is = case gb_trees:lookup({Lbl,Dst}, D) of - {value,Lit} -> Is1; %Safe to remove move instruction. - _ -> Is0 %Keep move instruction. + Is = case maps:find({Lbl,Dst}, D) of + {ok,Lit} -> Is1; %Safe to remove move instruction. + _ -> Is0 %Keep move instruction. end, forward(Is, D, Lc, [LblI|Acc]); forward([{test,is_eq_exact,_,[Same,Same]}|Is], D, Lc, Acc) -> @@ -156,11 +157,11 @@ forward([], _, Lc, Acc) -> {Acc,Lc}. update_value_dict([Lit,{f,Lbl}|T], Reg, D0) -> Key = {Lbl,Reg}, - D = case gb_trees:lookup(Key, D0) of - none -> gb_trees:insert(Key, Lit, D0); %New. - {value,inconsistent} -> D0; %Inconsistent. - {value,_} -> gb_trees:update(Key, inconsistent, D0) - end, + D = case D0 of + #{Key := inconsistent} -> D0; + #{Key := _} -> D0#{Key := inconsistent}; + _ -> D0#{Key => Lit} + end, update_value_dict(T, Reg, D); update_value_dict([], _, D) -> D. diff --git a/lib/compiler/src/beam_dict.erl b/lib/compiler/src/beam_dict.erl index 68dc104dd3..b1aa98278e 100644 --- a/lib/compiler/src/beam_dict.erl +++ b/lib/compiler/src/beam_dict.erl @@ -31,22 +31,22 @@ -type index() :: non_neg_integer(). --type atom_tab() :: gb_trees:tree(atom(), index()). +-type atom_tab() :: #{atom() => index()}. -type import_tab() :: gb_trees:tree(mfa(), index()). --type fname_tab() :: gb_trees:tree(Name :: term(), index()). --type line_tab() :: gb_trees:tree({Fname :: index(), Line :: term()}, index()). +-type fname_tab() :: #{Name :: term() => index()}. +-type line_tab() :: #{{Fname :: index(), Line :: term()} => index()}. -type literal_tab() :: dict:dict(Literal :: term(), index()). -record(asm, - {atoms = gb_trees:empty() :: atom_tab(), + {atoms = #{} :: atom_tab(), exports = [] :: [{label(), arity(), label()}], locals = [] :: [{label(), arity(), label()}], imports = gb_trees:empty() :: import_tab(), strings = <<>> :: binary(), %String pool lambdas = [], %[{...}] literals = dict:new() :: literal_tab(), - fnames = gb_trees:empty() :: fname_tab(), - lines = gb_trees:empty() :: line_tab(), + fnames = #{} :: fname_tab(), + lines = #{} :: line_tab(), num_lines = 0 :: non_neg_integer(), %Number of line instructions next_import = 0 :: non_neg_integer(), string_offset = 0 :: non_neg_integer(), @@ -77,14 +77,12 @@ highest_opcode(#asm{highest_opcode=Op}) -> Op. %% atom(Atom, Dict) -> {Index,Dict'} -spec atom(atom(), bdict()) -> {pos_integer(), bdict()}. -atom(Atom, #asm{atoms=Atoms0}=Dict) when is_atom(Atom) -> - case gb_trees:lookup(Atom, Atoms0) of - {value,Index} -> - {Index,Dict}; - none -> - NextIndex = gb_trees:size(Atoms0) + 1, - Atoms = gb_trees:insert(Atom, NextIndex, Atoms0), - {NextIndex,Dict#asm{atoms=Atoms}} +atom(Atom, #asm{atoms=Atoms}=Dict) when is_atom(Atom) -> + case Atoms of + #{ Atom := Index} -> {Index,Dict}; + _ -> + NextIndex = maps:size(Atoms) + 1, + {NextIndex,Dict#asm{atoms=Atoms#{Atom=>NextIndex}}} end. %% Remembers an exported function. @@ -177,26 +175,22 @@ line([], #asm{num_lines=N}=Dict) -> %% No location available. Return the special pre-defined %% index 0. {0,Dict#asm{num_lines=N+1}}; -line([{location,Name,Line}], #asm{lines=Lines0,num_lines=N}=Dict0) -> +line([{location,Name,Line}], #asm{lines=Lines,num_lines=N}=Dict0) -> {FnameIndex,Dict1} = fname(Name, Dict0), - case gb_trees:lookup({FnameIndex,Line}, Lines0) of - {value,Index} -> - {Index,Dict1#asm{num_lines=N+1}}; - none -> - Index = gb_trees:size(Lines0) + 1, - Lines = gb_trees:insert({FnameIndex,Line}, Index, Lines0), - Dict = Dict1#asm{lines=Lines,num_lines=N+1}, - {Index,Dict} + Key = {FnameIndex,Line}, + case Lines of + #{Key := Index} -> {Index,Dict1#asm{num_lines=N+1}}; + _ -> + Index = maps:size(Lines) + 1, + {Index, Dict1#asm{lines=Lines#{Key=>Index},num_lines=N+1}} end. -fname(Name, #asm{fnames=Fnames0}=Dict) -> - case gb_trees:lookup(Name, Fnames0) of - {value,Index} -> - {Index,Dict}; - none -> - Index = gb_trees:size(Fnames0), - Fnames = gb_trees:insert(Name, Index, Fnames0), - {Index,Dict#asm{fnames=Fnames}} +fname(Name, #asm{fnames=Fnames}=Dict) -> + case Fnames of + #{Name := Index} -> {Index,Dict}; + _ -> + Index = maps:size(Fnames), + {Index,Dict#asm{fnames=Fnames#{Name=>Index}}} end. %% Returns the atom table. @@ -204,14 +198,12 @@ fname(Name, #asm{fnames=Fnames0}=Dict) -> -spec atom_table(bdict()) -> {non_neg_integer(), [[non_neg_integer(),...]]}. atom_table(#asm{atoms=Atoms}) -> - NumAtoms = gb_trees:size(Atoms), - Sorted = lists:keysort(2, gb_trees:to_list(Atoms)), - Fun = fun({A,_}) -> - L = atom_to_list(A), - [length(L)|L] - end, - AtomTab = lists:map(Fun, Sorted), - {NumAtoms,AtomTab}. + NumAtoms = maps:size(Atoms), + Sorted = lists:keysort(2, maps:to_list(Atoms)), + {NumAtoms,[begin + L = atom_to_list(A), + [length(L)|L] + end || {A,_} <- Sorted]}. %% Returns the table of local functions. %% local_table(Dict) -> {NumLocals, [{Function, Arity, Label}...]} @@ -273,11 +265,11 @@ my_term_to_binary(Term) -> non_neg_integer(),[{non_neg_integer(),non_neg_integer()}]}. line_table(#asm{fnames=Fnames0,lines=Lines0,num_lines=NumLineInstrs}) -> - NumFnames = gb_trees:size(Fnames0), - Fnames1 = lists:keysort(2, gb_trees:to_list(Fnames0)), + NumFnames = maps:size(Fnames0), + Fnames1 = lists:keysort(2, maps:to_list(Fnames0)), Fnames = [Name || {Name,_} <- Fnames1], - NumLines = gb_trees:size(Lines0), - Lines1 = lists:keysort(2, gb_trees:to_list(Lines0)), + NumLines = maps:size(Lines0), + Lines1 = lists:keysort(2, maps:to_list(Lines0)), Lines = [L || {L,_} <- Lines1], {NumLineInstrs,NumFnames,Fnames,NumLines,Lines}. diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl index 52b6464c7f..80b2998ddc 100644 --- a/lib/compiler/src/beam_jump.erl +++ b/lib/compiler/src/beam_jump.erl @@ -152,14 +152,14 @@ function({function,Name,Arity,CLabel,Asm0}) -> share(Is0) -> %% We will get more sharing if we never fall through to a label. Is = eliminate_fallthroughs(Is0, []), - share_1(Is, dict:new(), [], []). + share_1(Is, #{}, [], []). share_1([{label,_}=Lbl|Is], Dict, [], Acc) -> share_1(Is, Dict, [], [Lbl|Acc]); share_1([{label,L}=Lbl|Is], Dict0, Seq, Acc) -> - case dict:find(Seq, Dict0) of + case maps:find(Seq, Dict0) of error -> - Dict = dict:store(Seq, L, Dict0), + Dict = maps:put(Seq, L, Dict0), share_1(Is, Dict, [], [Lbl|Seq ++ Acc]); {ok,Label} -> share_1(Is, Dict0, [], [Lbl,{jump,{f,Label}}|Acc]) @@ -188,7 +188,7 @@ clean_non_sharable(Dict) -> %% a sequence inside the 'try' block is a sequence that ends %% with an instruction that causes an exception. Any sequence %% that causes an exception must contain a line/1 instruction. - dict:filter(fun(K, _V) -> sharable_with_try(K) end, Dict). + maps:filter(fun(K, _V) -> sharable_with_try(K) end, Dict). sharable_with_try([{line,_}|_]) -> %% This sequence may cause an exception and may potentially @@ -268,13 +268,13 @@ extract_seq_1(_, _) -> no. -record(st, {fc, %Label for function class errors. entry, %Entry label (must not be moved). mlbl, %Moved labels. - labels %Set of referenced labels. + labels :: cerl_sets:set() %Set of referenced labels. }). opt([{label,Fc}|_]=Is0, CLabel) -> Lbls = initial_labels(Is0), find_fixpoint(fun(Is) -> - St = #st{fc=Fc,entry=CLabel,mlbl=dict:new(), + St = #st{fc=Fc,entry=CLabel,mlbl=#{}, labels=Lbls}, opt(Is, [], St) end, Is0). @@ -320,11 +320,11 @@ opt([{test,_,{f,_}=Lbl,_,_,_}=I|Is], Acc, St) -> opt([{select,_,_R,Fail,Vls}=I|Is], Acc, St) -> skip_unreachable(Is, [I|Acc], label_used([Fail|Vls], St)); opt([{label,Lbl}=I|Is], Acc, #st{mlbl=Mlbl}=St0) -> - case dict:find(Lbl, Mlbl) of + case maps:find(Lbl, Mlbl) of {ok,Lbls} -> %% Essential to remove the list of labels from the dictionary, %% since we will rescan the inserted labels. We MUST rescan. - St = St0#st{mlbl=dict:erase(Lbl, Mlbl)}, + St = St0#st{mlbl=maps:remove(Lbl, Mlbl)}, insert_labels([Lbl|Lbls], Is, Acc, St); error -> opt(Is, [I|Acc], St0) end; @@ -339,7 +339,7 @@ opt([{jump,{f,L}=Lbl}=I|Is], Acc0, #st{mlbl=Mlbl0}=St0) -> St = case Lbls of [] -> St0; [_|_] -> - Mlbl = dict:append_list(L, Lbls, Mlbl0), + Mlbl = maps_append_list(L, Lbls, Mlbl0), St0#st{mlbl=Mlbl} end, skip_unreachable(Is, [I|Acc], label_used(Lbl, St)); @@ -363,14 +363,20 @@ opt([I|Is], Acc, #st{labels=Used0}=St0) -> end; opt([], Acc, #st{fc=Fc,mlbl=Mlbl}) -> Code = reverse(Acc), - case dict:find(Fc, Mlbl) of + case maps:find(Fc, Mlbl) of {ok,Lbls} -> insert_fc_labels(Lbls, Mlbl, Code); error -> Code end. +maps_append_list(K,Vs,M) -> + case M of + #{K:=Vs0} -> M#{K:=Vs0++Vs}; % same order as dict + _ -> M#{K => Vs} + end. + insert_fc_labels([L|Ls], Mlbl, Acc0) -> Acc = [{label,L}|Acc0], - case dict:find(L, Mlbl) of + case maps:find(L, Mlbl) of error -> insert_fc_labels(Ls, Mlbl, Acc); {ok,Lbls} -> @@ -434,7 +440,7 @@ skip_unreachable([], Acc, St) -> %% Add one or more label to the set of used labels. -label_used({f,L}, St) -> St#st{labels=gb_sets:add(L, St#st.labels)}; +label_used({f,L}, St) -> St#st{labels=cerl_sets:add_element(L,St#st.labels)}; label_used([H|T], St0) -> label_used(T, label_used(H, St0)); label_used([], St) -> St; label_used(_Other, St) -> St. @@ -442,7 +448,7 @@ label_used(_Other, St) -> St. %% Test if label is used. is_label_used(L, St) -> - gb_sets:is_member(L, St#st.labels). + cerl_sets:is_element(L, St#st.labels). %% is_unreachable_after(Instruction) -> boolean() %% Test whether the code after Instruction is unreachable. @@ -472,14 +478,14 @@ is_exit_instruction(_) -> false. %% (including inside blocks). is_label_used_in(Lbl, Is) -> - is_label_used_in_1(Is, Lbl, gb_sets:empty()). + is_label_used_in_1(Is, Lbl, cerl_sets:new()). is_label_used_in_1([{block,Block}|Is], Lbl, Empty) -> lists:any(fun(I) -> is_label_used_in_block(I, Lbl) end, Block) orelse is_label_used_in_1(Is, Lbl, Empty); is_label_used_in_1([I|Is], Lbl, Empty) -> Used = ulbl(I, Empty), - gb_sets:is_member(Lbl, Used) orelse is_label_used_in_1(Is, Lbl, Empty); + cerl_sets:is_element(Lbl, Used) orelse is_label_used_in_1(Is, Lbl, Empty); is_label_used_in_1([], _, _) -> false. is_label_used_in_block({set,_,_,Info}, Lbl) -> @@ -506,7 +512,7 @@ remove_unused_labels(Is) -> rem_unused(Is, Used, []). rem_unused([{label,Lbl}=I|Is0], Used, [Prev|_]=Acc) -> - case gb_sets:is_member(Lbl, Used) of + case cerl_sets:is_element(Lbl, Used) of false -> Is = case is_unreachable_after(Prev) of true -> drop_upto_label(Is0); @@ -528,7 +534,7 @@ initial_labels([{line,_}|Is], Acc) -> initial_labels([{label,Lbl}|Is], Acc) -> initial_labels(Is, [Lbl|Acc]); initial_labels([{func_info,_,_,_},{label,Lbl}|_], Acc) -> - gb_sets:from_list([Lbl|Acc]). + cerl_sets:from_list([Lbl|Acc]). drop_upto_label([{label,_}|_]=Is) -> Is; drop_upto_label([_|Is]) -> drop_upto_label(Is); @@ -576,10 +582,10 @@ ulbl({get_map_elements,Lbl,_Src,_List}, Used) -> ulbl(_, Used) -> Used. mark_used({f,0}, Used) -> Used; -mark_used({f,L}, Used) -> gb_sets:add(L, Used). +mark_used({f,L}, Used) -> cerl_sets:add_element(L, Used). mark_used_list([{f,L}|T], Used) -> - mark_used_list(T, gb_sets:add(L, Used)); + mark_used_list(T, cerl_sets:add_element(L, Used)); mark_used_list([_|T], Used) -> mark_used_list(T, Used); mark_used_list([], Used) -> Used. diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl index 4731b5e78e..7ab548152e 100644 --- a/lib/compiler/src/beam_type.erl +++ b/lib/compiler/src/beam_type.erl @@ -554,10 +554,10 @@ flush(Rs, [{set,[_],[],{put_tuple,_}}|_]=Is0, Acc0) -> Acc = flush_all(Rs, Is0, Acc0), {[],Acc}; flush(Rs0, [{set,Ds,Ss,_Op}|_], Acc0) -> - Save = gb_sets:from_list(Ss), + Save = cerl_sets:from_list(Ss), Acc = save_regs(Rs0, Save, Acc0), Rs1 = foldl(fun(S, A) -> mark(S, A, clean) end, Rs0, Ss), - Kill = gb_sets:from_list(Ds), + Kill = cerl_sets:from_list(Ds), Rs = kill_regs(Rs1, Kill), {Rs,Acc}; flush(Rs0, Is, Acc0) -> @@ -580,7 +580,7 @@ save_regs(Rs, Save, Acc) -> foldl(fun(R, A) -> save_reg(R, Save, A) end, Acc, Rs). save_reg({I,V,dirty}, Save, Acc) -> - case gb_sets:is_member(V, Save) of + case cerl_sets:is_element(V, Save) of true -> [{set,[V],[{fr,I}],fmove}|checkerror(Acc)]; false -> Acc end; @@ -590,7 +590,7 @@ kill_regs(Rs, Kill) -> [kill_reg(R, Kill) || R <- Rs]. kill_reg({_,V,_}=R, Kill) -> - case gb_sets:is_member(V, Kill) of + case cerl_sets:is_element(V, Kill) of true -> free; false -> R end; diff --git a/lib/compiler/src/cerl_sets.erl b/lib/compiler/src/cerl_sets.erl new file mode 100644 index 0000000000..4df78dc432 --- /dev/null +++ b/lib/compiler/src/cerl_sets.erl @@ -0,0 +1,206 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2000-2015. 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(cerl_sets). + +%% Standard interface. +-export([new/0,is_set/1,size/1,to_list/1,from_list/1]). +-export([is_element/2,add_element/2,del_element/2]). +-export([union/2,union/1,intersection/2,intersection/1]). +-export([is_disjoint/2]). +-export([subtract/2,is_subset/2]). +-export([fold/3,filter/2]). + +-export_type([set/0, set/1]). + +%%------------------------------------------------------------------------------ + +-type set() :: set(_). +-opaque set(Element) :: #{Element => 'ok'}. + +%%------------------------------------------------------------------------------ + +%% new() -> Set +-spec new() -> set(). + +new() -> #{}. + +%% is_set(Set) -> boolean(). +%% Return 'true' if Set is a set of elements, else 'false'. +-spec is_set(Set) -> boolean() when + Set :: term(). + +is_set(S) when is_map(S) -> true; +is_set(_) -> false. + +%% size(Set) -> int(). +%% Return the number of elements in Set. +-spec size(Set) -> non_neg_integer() when + Set :: set(). + +size(S) -> maps:size(S). + +%% to_list(Set) -> [Elem]. +%% Return the elements in Set as a list. +-spec to_list(Set) -> List when + Set :: set(Element), + List :: [Element]. + +to_list(S) -> maps:keys(S). + +%% from_list([Elem]) -> Set. +%% Build a set from the elements in List. +-spec from_list(List) -> Set when + List :: [Element], + Set :: set(Element). +from_list(Ls) -> maps:from_list([{K,ok}||K<-Ls]). + +%% is_element(Element, Set) -> boolean(). +%% Return 'true' if Element is an element of Set, else 'false'. +-spec is_element(Element, Set) -> boolean() when + Set :: set(Element). + +is_element(E,S) -> + case S of + #{E := _} -> true; + _ -> false + end. + +%% add_element(Element, Set) -> Set. +%% Return Set with Element inserted in it. +-spec add_element(Element, Set1) -> Set2 when + Set1 :: set(Element), + Set2 :: set(Element). + +add_element(E,S) -> S#{E=>ok}. + +-spec del_element(Element, Set1) -> Set2 when + Set1 :: set(Element), + Set2 :: set(Element). + +%% del_element(Element, Set) -> Set. +%% Return Set but with Element removed. +del_element(E,S) -> maps:remove(E,S). + +%% union(Set1, Set2) -> Set +%% Return the union of Set1 and Set2. +-spec union(Set1, Set2) -> Set3 when + Set1 :: set(Element), + Set2 :: set(Element), + Set3 :: set(Element). + +union(S1,S2) -> maps:merge(S1,S2). + +%% union([Set]) -> Set +%% Return the union of the list of sets. +-spec union(SetList) -> Set when + SetList :: [set(Element)], + Set :: set(Element). + +union([S1,S2|Ss]) -> + union1(union(S1, S2), Ss); +union([S]) -> S; +union([]) -> new(). + +union1(S1, [S2|Ss]) -> + union1(union(S1, S2), Ss); +union1(S1, []) -> S1. + +%% intersection(Set1, Set2) -> Set. +%% Return the intersection of Set1 and Set2. +-spec intersection(Set1, Set2) -> Set3 when + Set1 :: set(Element), + Set2 :: set(Element), + Set3 :: set(Element). + +intersection(S1, S2) -> + filter(fun (E) -> is_element(E, S1) end, S2). + +%% intersection([Set]) -> Set. +%% Return the intersection of the list of sets. +-spec intersection(SetList) -> Set when + SetList :: [set(Element),...], + Set :: set(Element). + +intersection([S1,S2|Ss]) -> + intersection1(intersection(S1, S2), Ss); +intersection([S]) -> S. + +intersection1(S1, [S2|Ss]) -> + intersection1(intersection(S1, S2), Ss); +intersection1(S1, []) -> S1. + +%% is_disjoint(Set1, Set2) -> boolean(). +%% Check whether Set1 and Set2 are disjoint. +-spec is_disjoint(Set1, Set2) -> boolean() when + Set1 :: set(Element), + Set2 :: set(Element). + +is_disjoint(S1, S2) when map_size(S1) < map_size(S2) -> + fold(fun (_, false) -> false; + (E, true) -> not is_element(E, S2) + end, true, S1); +is_disjoint(S1, S2) -> + fold(fun (_, false) -> false; + (E, true) -> not is_element(E, S1) + end, true, S2). + +%% subtract(Set1, Set2) -> Set. +%% Return all and only the elements of Set1 which are not also in +%% Set2. +-spec subtract(Set1, Set2) -> Set3 when + Set1 :: set(Element), + Set2 :: set(Element), + Set3 :: set(Element). + +subtract(S1, S2) -> + filter(fun (E) -> not is_element(E, S2) end, S1). + +%% is_subset(Set1, Set2) -> boolean(). +%% Return 'true' when every element of Set1 is also a member of +%% Set2, else 'false'. +-spec is_subset(Set1, Set2) -> boolean() when + Set1 :: set(Element), + Set2 :: set(Element). + +is_subset(S1, S2) -> + fold(fun (E, Sub) -> Sub andalso is_element(E, S2) end, true, S1). + +%% fold(Fun, Accumulator, Set) -> Accumulator. +%% Fold function Fun over all elements in Set and return Accumulator. +-spec fold(Function, Acc0, Set) -> Acc1 when + Function :: fun((Element, AccIn) -> AccOut), + Set :: set(Element), + Acc0 :: Acc, + Acc1 :: Acc, + AccIn :: Acc, + AccOut :: Acc. + +fold(F, Init, D) -> + lists:foldl(fun(E,Acc) -> F(E,Acc) end,Init,maps:keys(D)). + +%% filter(Fun, Set) -> Set. +%% Filter Set with Fun. +-spec filter(Pred, Set1) -> Set2 when + Pred :: fun((Element) -> boolean()), + Set1 :: set(Element), + Set2 :: set(Element). + +filter(F, D) -> + maps:from_list(lists:filter(fun({K,_}) -> F(K) end, maps:to_list(D))). diff --git a/lib/compiler/src/compiler.app.src b/lib/compiler/src/compiler.app.src index 17d1bd91ce..0bfd998301 100644 --- a/lib/compiler/src/compiler.app.src +++ b/lib/compiler/src/compiler.app.src @@ -45,6 +45,7 @@ cerl, cerl_clauses, cerl_inline, + cerl_sets, cerl_trees, compile, core_scan, diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index 6f8279f65e..102a6951e8 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -92,10 +92,10 @@ -endif. %% Variable value info. --record(sub, {v=[], %Variable substitutions - s=[], %Variables in scope - t=[], %Types - in_guard=false}). %In guard or not. +-record(sub, {v=[], %Variable substitutions + s=cerl_sets:new() :: cerl_sets:set(), %Variables in scope + t=#{} :: map(), %Types + in_guard=false}). %In guard or not. -type type_info() :: cerl:cerl() | 'bool' | 'integer'. -type yes_no_maybe() :: 'yes' | 'no' | 'maybe'. @@ -1123,7 +1123,7 @@ let_substs(Vs0, As0, Sub0) -> {Vs2,As1,Ss} = let_substs_1(Vs1, As0, Sub1), Sub2 = sub_add_scope([V || #c_var{name=V} <- Vs2], Sub1), {Vs2,As1, - foldl(fun ({V,S}, Sub) -> sub_set_name(V, S, Sub) end, Sub2, Ss)}. + foldl(fun ({V,S}, Sub) -> sub_set_name(V, S, Sub) end, Sub2, Ss)}. let_substs_1(Vs, #c_values{es=As}, Sub) -> let_subst_list(Vs, As, Sub); @@ -1242,10 +1242,10 @@ is_subst(_) -> false. %% to force renaming if variables in the scope occurs as pattern %% variables. -sub_new() -> #sub{v=orddict:new(),s=gb_trees:empty(),t=[]}. +sub_new() -> #sub{v=orddict:new(),s=cerl_sets:new(),t=#{}}. sub_new(#sub{}=Sub) -> - Sub#sub{v=orddict:new(),t=[]}. + Sub#sub{v=orddict:new(),t=#{}}. sub_new_preserve_types(#sub{}=Sub) -> Sub#sub{v=orddict:new()}. @@ -1262,16 +1262,16 @@ sub_set_var(#c_var{name=V}, Val, Sub) -> sub_set_name(V, Val, #sub{v=S,s=Scope,t=Tdb0}=Sub) -> Tdb1 = kill_types(V, Tdb0), Tdb = copy_type(V, Val, Tdb1), - Sub#sub{v=orddict:store(V, Val, S),s=gb_sets:add(V, Scope),t=Tdb}. + Sub#sub{v=orddict:store(V, Val, S),s=cerl_sets:add_element(V, Scope),t=Tdb}. sub_del_var(#c_var{name=V}, #sub{v=S,s=Scope,t=Tdb}=Sub) -> %% Profiling shows that for programs with many record operations, %% sub_del_var/2 is a bottleneck. Since the scope contains all %% variables that are live, we know that V cannot be present in S %% if it is not in the scope. - case gb_sets:is_member(V, Scope) of + case cerl_sets:is_element(V, Scope) of false -> - Sub#sub{s=gb_sets:insert(V, Scope)}; + Sub#sub{s=cerl_sets:add_element(V, Scope)}; true -> Sub#sub{v=orddict:erase(V, S),t=kill_types(V, Tdb)} end. @@ -1282,12 +1282,12 @@ sub_subst_var(#c_var{name=V}, Val, #sub{v=S0}) -> sub_add_scope(Vs, #sub{s=Scope0}=Sub) -> Scope = foldl(fun(V, S) when is_integer(V); is_atom(V) -> - gb_sets:add(V, S) + cerl_sets:add_element(V, S) end, Scope0, Vs), Sub#sub{s=Scope}. sub_subst_scope(#sub{v=S0,s=Scope}=Sub) -> - S = [{-1,#c_var{name=Sv}} || Sv <- gb_sets:to_list(Scope)]++S0, + S = [{-1,#c_var{name=Sv}} || Sv <- cerl_sets:to_list(Scope)]++S0, Sub#sub{v=S}. sub_is_val(#c_var{name=V}, #sub{v=S,s=Scope}) -> @@ -1295,7 +1295,7 @@ sub_is_val(#c_var{name=V}, #sub{v=S,s=Scope}) -> %% became the new bottleneck. Since the scope contains all %% live variables, a variable V can only be the target for %% a substitution if it is in the scope. - gb_sets:is_member(V, Scope) andalso v_is_value(V, S). + cerl_sets:is_element(V, Scope) andalso v_is_value(V, S). v_is_value(Var, [{_,#c_var{name=Var}}|_]) -> true; v_is_value(Var, [_|T]) -> v_is_value(Var, T); @@ -1760,8 +1760,9 @@ case_opt_compiler_generated(Core) -> %% return Expr0 unchanged. %% case_expand_var(E, #sub{t=Tdb}) -> - case orddict:find(cerl:var_name(E), Tdb) of - {ok,T0} -> + Key = cerl:var_name(E), + case Tdb of + #{Key:=T0} -> case cerl:is_c_tuple(T0) of false -> E; @@ -1785,7 +1786,7 @@ case_expand_var(E, #sub{t=Tdb}) -> E end end; - error -> + _ -> E end. @@ -2147,7 +2148,7 @@ is_bool_expr_list([], _) -> true. %% functions, or is_record/2). %% is_safe_bool_expr(Core, Sub) -> - is_safe_bool_expr_1(Core, Sub, gb_sets:empty()). + is_safe_bool_expr_1(Core, Sub, cerl_sets:new()). is_safe_bool_expr_1(#c_call{module=#c_literal{val=erlang}, name=#c_literal{val=is_record}, @@ -2193,7 +2194,7 @@ is_safe_bool_expr_1(#c_let{vars=Vars,arg=Arg,body=B}, Sub, BoolVars) -> true -> case {is_safe_bool_expr_1(Arg, Sub, BoolVars),Vars} of {true,[#c_var{name=V}]} -> - is_safe_bool_expr_1(B, Sub, gb_sets:add(V, BoolVars)); + is_safe_bool_expr_1(B, Sub, cerl_sets:add_element(V, BoolVars)); {false,_} -> is_safe_bool_expr_1(B, Sub, BoolVars) end; @@ -2202,7 +2203,7 @@ is_safe_bool_expr_1(#c_let{vars=Vars,arg=Arg,body=B}, Sub, BoolVars) -> is_safe_bool_expr_1(#c_literal{val=Val}, _Sub, _) -> is_boolean(Val); is_safe_bool_expr_1(#c_var{name=V}, _Sub, BoolVars) -> - gb_sets:is_element(V, BoolVars); + cerl_sets:is_element(V, BoolVars); is_safe_bool_expr_1(_, _, _) -> false. is_safe_bool_expr_list([C|Cs], Sub, BoolVars) -> @@ -2236,7 +2237,7 @@ move_let_into_expr(#c_let{vars=InnerVs0,body=InnerBody0}=Inner, %% in <InnerBody> %% Arg = body(Arg0, Sub0), - ScopeSub0 = sub_subst_scope(Sub0#sub{t=[]}), + ScopeSub0 = sub_subst_scope(Sub0#sub{t=#{}}), {OuterVs,ScopeSub} = pattern_list(OuterVs0, ScopeSub0), OuterBody = body(OuterBody0, ScopeSub), @@ -2275,15 +2276,15 @@ move_let_into_expr(#c_let{vars=Lvs0,body=Lbody0}=Let, CaVars0 = Ca0#c_clause.pats, G0 = Ca0#c_clause.guard, B0 = Ca0#c_clause.body, - ScopeSub0 = sub_subst_scope(Sub0#sub{t=[]}), + ScopeSub0 = sub_subst_scope(Sub0#sub{t=#{}}), {CaVars,ScopeSub} = pattern_list(CaVars0, ScopeSub0), G = guard(G0, ScopeSub), B1 = body(B0, ScopeSub), {Lvs,B2,Sub1} = let_substs(Lvs0, B1, Sub0), - Sub2 = Sub1#sub{s=gb_sets:union(ScopeSub#sub.s, - Sub1#sub.s)}, + Sub2 = Sub1#sub{s=cerl_sets:union(ScopeSub#sub.s, + Sub1#sub.s)}, Lbody = body(Lbody0, Sub2), B = Let#c_let{vars=Lvs,arg=core_lib:make_values(B2),body=Lbody}, @@ -2574,7 +2575,7 @@ move_case_into_arg(#c_case{arg=#c_let{vars=OuterVars0,arg=OuterArg, %% let <OuterVars> = <OuterArg> %% in case <InnerArg> of <InnerClauses> end %% - ScopeSub0 = sub_subst_scope(Sub#sub{t=[]}), + ScopeSub0 = sub_subst_scope(Sub#sub{t=#{}}), {OuterVars,ScopeSub} = pattern_list(OuterVars0, ScopeSub0), InnerArg = body(InnerArg0, ScopeSub), Outer#c_let{vars=OuterVars,arg=OuterArg, @@ -2603,7 +2604,7 @@ move_case_into_arg(#c_case{arg=#c_case{arg=OuterArg, %% <OuterCb> %% end %% - ScopeSub0 = sub_subst_scope(Sub#sub{t=[]}), + ScopeSub0 = sub_subst_scope(Sub#sub{t=#{}}), {OuterPats,ScopeSub} = pattern_list(OuterPats0, ScopeSub0), OuterGuard = guard(OuterGuard0, ScopeSub), InnerArg = body(InnerArg0, ScopeSub), @@ -2688,9 +2689,9 @@ is_any_var_used([], _) -> false. -spec get_type(cerl:cerl(), #sub{}) -> type_info() | 'none'. get_type(#c_var{name=V}, #sub{t=Tdb}) -> - case orddict:find(V, Tdb) of - {ok,Type} -> Type; - error -> none + case Tdb of + #{V:=Type} -> Type; + _ -> none end; get_type(C, _) -> case cerl:type(C) of @@ -2805,35 +2806,38 @@ update_types_1(#c_var{name=V,anno=Anno}, Pat, Types) -> update_types_1(_, _, Types) -> Types. update_types_2(V, [#c_tuple{}=P], Types) -> - orddict:store(V, P, Types); + Types#{V=>P}; update_types_2(V, [#c_literal{val=Bool}], Types) when is_boolean(Bool) -> - orddict:store(V, bool, Types); + Types#{V=>bool}; update_types_2(V, [Type], Types) when is_atom(Type) -> - orddict:store(V, Type, Types); + Types#{V=>Type}; update_types_2(_, _, Types) -> Types. %% kill_types(V, Tdb) -> Tdb' %% Kill any entries that references the variable, %% either in the key or in the value. -kill_types(V, [{V,_}|Tdb]) -> - kill_types(V, Tdb); -kill_types(V, [{_,#c_tuple{}=Tuple}=Entry|Tdb]) -> +kill_types(V, Tdb) -> + maps:from_list(kill_types2(V,maps:to_list(Tdb))). + +kill_types2(V, [{V,_}|Tdb]) -> + kill_types2(V, Tdb); +kill_types2(V, [{_,#c_tuple{}=Tuple}=Entry|Tdb]) -> case core_lib:is_var_used(V, Tuple) of - false -> [Entry|kill_types(V, Tdb)]; - true -> kill_types(V, Tdb) + false -> [Entry|kill_types2(V, Tdb)]; + true -> kill_types2(V, Tdb) end; -kill_types(V, [{_,Atom}=Entry|Tdb]) when is_atom(Atom) -> - [Entry|kill_types(V, Tdb)]; -kill_types(_, []) -> []. +kill_types2(V, [{_,Atom}=Entry|Tdb]) when is_atom(Atom) -> + [Entry|kill_types2(V, Tdb)]; +kill_types2(_, []) -> []. %% copy_type(DestVar, SrcVar, Tdb) -> Tdb' %% If the SrcVar has a type, assign it to DestVar. %% copy_type(V, #c_var{name=Src}, Tdb) -> - case orddict:find(Src, Tdb) of - {ok,Type} -> orddict:store(V, Type, Tdb); - error -> Tdb + case Tdb of + #{Src:=Type} -> Tdb#{V=>Type}; + _ -> Tdb end; copy_type(_, _, Tdb) -> Tdb. @@ -3237,12 +3241,12 @@ format_error(bin_var_used_in_guard) -> verify_scope(E, #sub{s=Scope}) -> Free0 = cerl_trees:free_variables(E), Free = [V || V <- Free0, not is_tuple(V)], %Ignore function names. - case ordsets:is_subset(Free, gb_sets:to_list(Scope)) of + case ordsets:is_subset(Free, cerl_sets:to_list(Scope)) of true -> true; false -> io:format("~p\n", [E]), io:format("~p\n", [Free]), - io:format("~p\n", [gb_sets:to_list(Scope)]), + io:format("~p\n", [cerl_sets:to_list(Scope)]), false end. -endif. diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index aa2ebc0f85..c9b1a45cfc 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -43,7 +43,7 @@ -export([module/2]). -import(lists, [member/2,keymember/3,keysort/2,keydelete/3, - append/1,map/2,flatmap/2,filter/2,foldl/3,foldr/3,mapfoldl/3, + append/1,flatmap/2,filter/2,foldl/3,foldr/3,mapfoldl/3, sort/1,reverse/1,reverse/2]). -import(v3_life, [vdb_find/2]). @@ -57,8 +57,7 @@ break, %Break label recv, %Receive label is_top_block, %Boolean: top block or not - functable=gb_trees:empty(), %Gb tree of local functions: - % {{Name,Arity},Label} + functable=#{}, %Map of local functions: {Name,Arity}=>Label in_catch=false, %Inside a catch or not. need_frame, %Need a stack frame. ultimate_failure %Label for ultimate match failure. @@ -673,9 +672,7 @@ select_val_cg(Type, R, [Val, {f,Lbl}], Tf, Vf, [{label,Lbl}|Sis]) -> [{test,select_type_test(Type),{f,Tf},[R]}, {test,is_eq_exact,{f,Vf},[R,{Type,Val}]}|Sis]; select_val_cg(Type, R, Vls0, Tf, Vf, Sis) -> - Vls1 = map(fun ({f,_Lbl} = F) -> F; - (Value) -> {Type,Value} - end, Vls0), + Vls1 = [case Value of {f,_Lbl} -> Value; _ -> {Type,Value} end || Value <- Vls0], [{test,select_type_test(Type),{f,Tf},[R]}, {select_val,R,{f,Vf},{list,Vls1}}|Sis]. select_type_test(integer) -> is_integer; @@ -1080,7 +1077,7 @@ protected_cg(Ts, Rs, _Fail, I, Vdb, Bef, St0) -> St2#cg{bfail=Pfail}), %%ok = io:fwrite("cg ~w: ~p~n", [?LINE,{Rs,I,Vdb,Aft}]), %% Set return values to false. - Mis = map(fun ({var,V}) -> {move,{atom,false},fetch_var(V, Aft)} end, Rs), + Mis = [{move,{atom,false},fetch_var(V,Aft)}||{var,V} <- Rs], {Tis ++ [{jump,{f,Psucc}}, {label,Pfail}] ++ Mis ++ [{label,Psucc}], Aft,St3#cg{bfail=St0#cg.bfail}}. @@ -1263,13 +1260,12 @@ enter_line(_, _, _) -> local_func_label(Name, Arity, St) -> local_func_label({Name,Arity}, St). -local_func_label(Key, #cg{functable=Tab}=St0) -> - case gb_trees:lookup(Key, Tab) of - {value,Label} -> - {Label,St0}; - none -> +local_func_label(Key, #cg{functable=Map}=St0) -> + case Map of + #{Key := Label} -> {Label,St0}; + _ -> {Label,St} = new_label(St0), - {Label,St#cg{functable=gb_trees:insert(Key, Label, Tab)}} + {Label,St#cg{functable=Map#{Key => Label}}} end. %% need_stack_frame(State) -> State' @@ -1992,25 +1988,28 @@ clear_dead(Sr, Until, Vdb) -> stk=clear_dead_stk(Sr#sr.stk, Until, Vdb)}. clear_dead_reg(Sr, Until, Vdb) -> - Reg = map(fun ({_I,V} = IV) -> - case vdb_find(V, Vdb) of - {V,_,L} when L > Until -> IV; - _ -> free %Remove anything else - end; - ({reserved,_I,_V} = Reserved) -> Reserved; - (free) -> free - end, Sr#sr.reg), + Reg = [case R of + {_I,V} = IV -> + case vdb_find(V, Vdb) of + {V,_,L} when L > Until -> IV; + _ -> free %Remove anything else + end; + {reserved,_I,_V} = Reserved -> Reserved; + free -> free + end || R <- Sr#sr.reg], reserve(Sr#sr.res, Reg, Sr#sr.stk). clear_dead_stk(Stk, Until, Vdb) -> - map(fun ({V} = T) -> - case vdb_find(V, Vdb) of - {V,_,L} when L > Until -> T; - _ -> dead %Remove anything else - end; - (free) -> free; - (dead) -> dead - end, Stk). + [case S of + {V} = T -> + case vdb_find(V, Vdb) of + {V,_,L} when L > Until -> T; + _ -> dead %Remove anything else + end; + free -> free; + dead -> dead + end ||Â S <- Stk]. + %% sr_merge(Sr1, Sr2) -> Sr. %% Merge two stack/register states keeping the longest of both stack diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index 7dff58582e..c21b2a1505 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -114,7 +114,7 @@ copy_anno(Kdst, Ksrc) -> ff, %Current function vcount=0, %Variable counter fcount=0, %Fun counter - ds=[], %Defined variables + ds=cerl_sets:new() :: cerl_sets:set(), %Defined variables funs=[], %Fun functions free=[], %Free variables ws=[] :: [warning()], %Warnings. @@ -148,7 +148,7 @@ include_attribute(_) -> true. function({#c_var{name={F,Arity}=FA},Body}, St0) -> try - St1 = St0#kern{func=FA,ff=undefined,vcount=0,fcount=0,ds=sets:new()}, + St1 = St0#kern{func=FA,ff=undefined,vcount=0,fcount=0,ds=cerl_sets:new()}, {#ifun{anno=Ab,vars=Kvs,body=B0},[],St2} = expr(Body, new_sub(), St1), {B1,_,St3} = ubody(B0, return, St2), %%B1 = B0, St3 = St2, %Null second pass @@ -715,15 +715,15 @@ force_variable(Ke, St0) -> %% handling. pattern(#c_var{anno=A,name=V}, _Isub, Osub, St0) -> - case sets:is_element(V, St0#kern.ds) of + case cerl_sets:is_element(V, St0#kern.ds) of true -> {New,St1} = new_var_name(St0), {#k_var{anno=A,name=New}, set_vsub(V, New, Osub), - St1#kern{ds=sets:add_element(New, St1#kern.ds)}}; + St1#kern{ds=cerl_sets:add_element(New, St1#kern.ds)}}; false -> {#k_var{anno=A,name=V},Osub, - St0#kern{ds=sets:add_element(V, St0#kern.ds)}} + St0#kern{ds=cerl_sets:add_element(V, St0#kern.ds)}} end; pattern(#c_literal{anno=A,val=Val}, _Isub, Osub, St) -> {#k_literal{anno=A,val=Val},Osub,St}; @@ -897,7 +897,7 @@ new_vars(0, St, Vs) -> {Vs,St}. make_vars(Vs) -> [ #k_var{name=V} || V <- Vs ]. add_var_def(V, St) -> - St#kern{ds=sets:add_element(V#k_var.name, St#kern.ds)}. + St#kern{ds=cerl_sets:add_element(V#k_var.name, St#kern.ds)}. %%add_vars_def(Vs, St) -> %% Ds = foldl(fun (#k_var{name=V}, Ds) -> add_element(V, Ds) end, diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl index 4b1f1c3f71..ee0565efb6 100644 --- a/lib/compiler/src/v3_life.erl +++ b/lib/compiler/src/v3_life.erl @@ -411,7 +411,7 @@ is_gc_bif(Bif, Arity) -> %% must be sorted. init_vars(Vs) -> - sort([{V,0,0} || {var,V} <- Vs]). + vdb_new(Vs). new_vars([], _, Vdb) -> Vdb; new_vars([V], I, Vdb) -> vdb_store_new(V, {V,I,I}, Vdb); @@ -430,6 +430,16 @@ use_vars(Vs, I, Vdb) -> vdb_update_vars(Vs, Vdb, I). add_var(V, F, L, Vdb) -> vdb_store_new(V, {V,F,L}, Vdb). +%% is_in_guard() -> true|false. + +is_in_guard() -> + get(guard_refc) > 0. + +%% vdb + +vdb_new(Vs) -> + sort([{V,0,0} || {var,V} <- Vs]). + vdb_find(V, Vdb) -> case lists:keyfind(V, 1, Vdb) of false -> error; @@ -471,8 +481,3 @@ vdb_sub(Min, Max, Vdb) -> [ if L >= Max -> {V,F,locked}; true -> Vd end || {V,F,L}=Vd <- Vdb, F < Min, L >= Min ]. - -%% is_in_guard() -> true|false. - -is_in_guard() -> - get(guard_refc) > 0. diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk index 05e682c893..69f71ba5dd 100644 --- a/lib/compiler/vsn.mk +++ b/lib/compiler/vsn.mk @@ -1 +1 @@ -COMPILER_VSN = 5.0.4 +COMPILER_VSN = 6.0 diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk index 8489b59562..55b1b3e8c4 100644 --- a/lib/crypto/vsn.mk +++ b/lib/crypto/vsn.mk @@ -1 +1 @@ -CRYPTO_VSN = 3.5 +CRYPTO_VSN = 3.6 diff --git a/lib/debugger/test/map_SUITE.erl b/lib/debugger/test/map_SUITE.erl index 12fdd184b8..74847e161f 100644 --- a/lib/debugger/test/map_SUITE.erl +++ b/lib/debugger/test/map_SUITE.erl @@ -1308,7 +1308,7 @@ t_guard_receive(Config) when is_list(Config) -> done = call(Pid, done), ok. --define(t_guard_receive_large_procs, 150). +-define(t_guard_receive_large_procs, 50). t_guard_receive_large(Config) when is_list(Config) -> M = lists:foldl(fun(_,#{procs := Ps } = M) -> @@ -1326,7 +1326,7 @@ guard_receive_large_loop(M) -> receive #{pid := Pid, msg := hello} -> case M of - #{done := Count, procs := #{Pid := 150}} -> + #{done := Count, procs := #{Pid := 15}} -> Pid ! {self(), done}, guard_receive_large_loop(M#{done := Count + 1}); #{procs := #{Pid := Count} = Ps} -> diff --git a/lib/debugger/vsn.mk b/lib/debugger/vsn.mk index b82f0f4e37..b6fd4e8e44 100644 --- a/lib/debugger/vsn.mk +++ b/lib/debugger/vsn.mk @@ -1 +1 @@ -DEBUGGER_VSN = 4.0.3 +DEBUGGER_VSN = 4.1 diff --git a/lib/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml index 5f52906625..fc076c24a6 100644 --- a/lib/dialyzer/doc/src/dialyzer.xml +++ b/lib/dialyzer/doc/src/dialyzer.xml @@ -70,7 +70,7 @@ [--build_plt] [--add_to_plt] [--remove_from_plt] [--check_plt] [--no_check_plt] [--plt_info] [--get_warnings] [--dump_callgraph file] [--no_native] [--fullpath] - [--statistics]</code> + [--statistics] [--no_native_cache]</code> <p>Options:</p> <taglist> <tag><c><![CDATA[files_or_dirs]]></c> (for backwards compatibility also @@ -198,6 +198,11 @@ heuristically performs when dialyzing many files; this avoids the compilation time but it may result in (much) longer analysis time.</item> + <tag><c><![CDATA[--no_native_cache]]></c></tag> + <item>By default, Dialyzer caches the results of native compilation in the + <c>$XDG_CACHE_HOME/erlang/dialyzer_hipe_cache</c> directory. + <c>XDG_CACHE_HOME</c> defaults to <c>$HOME/.cache</c>. + Use this option to disable caching.</item> <tag><c><![CDATA[--fullpath]]></c></tag> <item>Display the full path names of files for which warnings are emitted.</item> <tag><c><![CDATA[--gui]]></c></tag> diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl index 4386a8d52a..55fcd15641 100644 --- a/lib/dialyzer/src/dialyzer_cl.erl +++ b/lib/dialyzer/src/dialyzer_cl.erl @@ -512,32 +512,82 @@ hipe_compile(Files, #options{erlang_mode = ErlangMode} = Options) -> dialyzer_worker], report_native_comp(Options), {T1, _} = statistics(wall_clock), - native_compile(Mods), + Cache = (get(dialyzer_options_native_cache) =/= false), + native_compile(Mods, Cache), {T2, _} = statistics(wall_clock), report_elapsed_time(T1, T2, Options) end end. -native_compile(Mods) -> +native_compile(Mods, Cache) -> case dialyzer_utils:parallelism() > ?MIN_PARALLELISM of true -> Parent = self(), - Pids = [spawn(fun () -> Parent ! {self(), hc(M)} end) || M <- Mods], + Pids = [spawn(fun () -> Parent ! {self(), hc(M, Cache)} end) || M <- Mods], lists:foreach(fun (Pid) -> receive {Pid, Res} -> Res end end, Pids); false -> - lists:foreach(fun (Mod) -> hc(Mod) end, Mods) + lists:foreach(fun (Mod) -> hc(Mod, Cache) end, Mods) end. -hc(Mod) -> +hc(Mod, Cache) -> {module, Mod} = code:ensure_loaded(Mod), case code:is_module_native(Mod) of true -> ok; false -> %% io:format(" ~w", [Mod]), - {ok, Mod} = hipe:c(Mod), - ok + case Cache of + false -> + {ok, Mod} = hipe:c(Mod), + ok; + true -> + hc_cache(Mod) + end end. +hc_cache(Mod) -> + CacheBase = cache_base_dir(), + %% Use HiPE architecture and version in directory name, to avoid + %% clashes between incompatible binaries. + HipeArchVersion = + lists:concat( + [erlang:system_info(hipe_architecture), "-", + hipe:version(), "-", + hipe_bifs:system_crc()]), + CacheDir = filename:join(CacheBase, HipeArchVersion), + OrigBeamFile = code:which(Mod), + {ok, {Mod, <<Checksum:128>>}} = beam_lib:md5(OrigBeamFile), + CachedBeamFile = filename:join(CacheDir, lists:concat([Mod, "-", Checksum, ".beam"])), + ok = filelib:ensure_dir(CachedBeamFile), + ModBin = + case filelib:is_file(CachedBeamFile) of + true -> + {ok, BinFromFile} = file:read_file(CachedBeamFile), + BinFromFile; + false -> + {ok, Mod, CompiledBin} = compile:file(OrigBeamFile, [from_beam, native, binary]), + ok = file:write_file(CachedBeamFile, CompiledBin), + CompiledBin + end, + code:unstick_dir(filename:dirname(OrigBeamFile)), + {module, Mod} = code:load_binary(Mod, CachedBeamFile, ModBin), + true = code:is_module_native(Mod), + ok. + +cache_base_dir() -> + %% http://standards.freedesktop.org/basedir-spec/basedir-spec-0.7.html + %% If XDG_CACHE_HOME is set to an absolute path, use it as base. + XdgCacheHome = os:getenv("XDG_CACHE_HOME"), + CacheHome = + case is_list(XdgCacheHome) andalso filename:pathtype(XdgCacheHome) =:= absolute of + true -> + XdgCacheHome; + false -> + %% Otherwise, the default is $HOME/.cache. + {ok, [[Home]]} = init:get_argument(home), + filename:join(Home, ".cache") + end, + filename:join([CacheHome, "dialyzer_hipe_cache"]). + new_state() -> #cl_state{}. diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl index 21fc424a1b..fae88ed6e8 100644 --- a/lib/dialyzer/src/dialyzer_cl_parse.erl +++ b/lib/dialyzer/src/dialyzer_cl_parse.erl @@ -75,6 +75,9 @@ cl(["-nn"|T]) -> cl(["--no_native"|T]) -> put(dialyzer_options_native, false), cl(T); +cl(["--no_native_cache"|T]) -> + put(dialyzer_options_native_cache, false), + cl(T); cl(["--plt_info"|T]) -> put(dialyzer_options_analysis_type, plt_info), cl(T); @@ -363,7 +366,7 @@ help_message() -> [--build_plt] [--add_to_plt] [--remove_from_plt] [--check_plt] [--no_check_plt] [--plt_info] [--get_warnings] [--dump_callgraph file] [--no_native] [--fullpath] - [--statistics] + [--statistics] [--no_native_cache] Options: files_or_dirs (for backwards compatibility also as: -c files_or_dirs) Use Dialyzer from the command line to detect defects in the @@ -468,6 +471,11 @@ Options: Bypass the native code compilation of some key files that Dialyzer heuristically performs when dialyzing many files; this avoids the compilation time but it may result in (much) longer analysis time. + --no_native_cache + By default, Dialyzer caches the results of native compilation in the + $XDG_CACHE_HOME/erlang/dialyzer_hipe_cache directory. + XDG_CACHE_HOME defaults to $HOME/.cache. Use this option to disable + caching. --fullpath Display the full path names of files for which warnings are emitted. --gui diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk index 527afaf4ef..48e0830109 100644 --- a/lib/dialyzer/vsn.mk +++ b/lib/dialyzer/vsn.mk @@ -1 +1 @@ -DIALYZER_VSN = 2.7.4 +DIALYZER_VSN = 2.8 diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml index 6931788c83..c5df63a7f0 100644 --- a/lib/diameter/doc/src/notes.xml +++ b/lib/diameter/doc/src/notes.xml @@ -42,6 +42,36 @@ first.</p> <!-- ===================================================================== --> +<section><title>diameter 1.9.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix broken relay counters.</p> + <p> + OTP-12654 in OTP 17.5.3 broke counters in the case of + answer messages received in the relay application. + Counters were accumulated as unknown messages or + no_result_code instead of as relayed messages on the + intended Result-Code and 'Experimental-Result' tuples.</p> + <p> + Own Id: OTP-12741</p> + </item> + <item> + <p> + Fix diameter_sctp listener race.</p> + <p> + An oversight in OTP-12428 made it possible to start a + transport process that could not establish associations.</p> + <p> + Own Id: OTP-12744</p> + </item> + </list> + </section> + +</section> + <section><title>diameter 1.9.1</title> <section><title>Known Bugs and Problems</title> @@ -65,7 +95,7 @@ first.</p> received in an answer not setting the E-bit. The correct AVP is now extracted from the incoming message.</p> <p> - Own Id: OTP-12654 Aux Id: seq12851 </p> + Own Id: OTP-12654</p> </item> <item> <p> diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index ffd2c0afa2..eb4bbae931 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -131,11 +131,11 @@ peer_down(TPid) -> %% incr/4 %% --------------------------------------------------------------------------- -incr(Dir, #diameter_packet{header = H}, TPid, Dict) -> - incr(Dir, H, TPid, Dict); +incr(Dir, #diameter_packet{header = H}, TPid, AppDict) -> + incr(Dir, H, TPid, AppDict); -incr(Dir, #diameter_header{} = H, TPid, Dict) -> - incr(TPid, {msg_id(H, Dict), Dir}). +incr(Dir, #diameter_header{} = H, TPid, AppDict) -> + incr(TPid, {msg_id(H, AppDict), Dir}). %% --------------------------------------------------------------------------- %% incr_error/4 @@ -143,26 +143,26 @@ incr(Dir, #diameter_header{} = H, TPid, Dict) -> %% Identify messages using the application dictionary, not the encode %% dictionary, which may differ in the case of answer-message. -incr_error(Dir, T, Pid, {_Dict, AppDict}) -> +incr_error(Dir, T, Pid, {_MsgDict, AppDict}) -> incr_error(Dir, T, Pid, AppDict); %% Decoded message without errors. incr_error(recv, #diameter_packet{errors = []}, _, _) -> ok; -incr_error(recv = D, #diameter_packet{header = H}, TPid, Dict) -> - incr_error(D, H, TPid, Dict); +incr_error(recv = D, #diameter_packet{header = H}, TPid, AppDict) -> + incr_error(D, H, TPid, AppDict); %% Encoded message with errors and an identifiable header ... -incr_error(send = D, {_, _, #diameter_header{} = H}, TPid, Dict) -> - incr_error(D, H, TPid, Dict); +incr_error(send = D, {_, _, #diameter_header{} = H}, TPid, AppDict) -> + incr_error(D, H, TPid, AppDict); %% ... or not. incr_error(send = D, {_,_}, TPid, _) -> incr_error(D, unknown, TPid); -incr_error(Dir, #diameter_header{} = H, TPid, Dict) -> - incr_error(Dir, msg_id(H, Dict), TPid); +incr_error(Dir, #diameter_header{} = H, TPid, AppDict) -> + incr_error(Dir, msg_id(H, AppDict), TPid); incr_error(Dir, Id, TPid, _) -> incr_error(Dir, Id, TPid). @@ -179,18 +179,20 @@ incr_error(Dir, Id, TPid) -> | Reason when Pkt :: #diameter_packet{}, TPid :: pid(), - DictT :: module() | {module(), module(), module()}, + DictT :: module() | {MsgDict :: module(), + AppDict :: module(), + CommonDict:: module()}, Counter :: {'Result-Code', integer()} | {'Experimental-Result', integer(), integer()}, Reason :: atom(). -incr_rc(Dir, Pkt, TPid, {Dict, _, _} = DictT) -> +incr_rc(Dir, Pkt, TPid, {_, AppDict, _} = DictT) -> try incr_result(Dir, Pkt, TPid, DictT) catch exit: {E,_} when E == no_result_code; E == invalid_error_bit -> - incr(TPid, {msg_id(Pkt#diameter_packet.header, Dict), Dir, E}), + incr(TPid, {msg_id(Pkt#diameter_packet.header, AppDict), Dir, E}), E end; @@ -259,7 +261,8 @@ recv(false, #request{ref = Ref, handler = Pid} = Req, _, Pkt, Dict0, _) -> %% any others are discarded. %% ... or not. -recv(false, false, _, _, _, _) -> +recv(false, false, TPid, _, _, _) -> + incr(TPid, {{unknown, 0}, recv, discarded}), ok. %% spawn_request/4 @@ -307,14 +310,14 @@ recv_request(TPid, Pkt, Dict0, RecvData) -> %% from old code %% recv_R/5 -recv_R({#diameter_app{id = Id, dictionary = Dict} = App, Caps}, +recv_R({#diameter_app{id = Id, dictionary = AppDict} = App, Caps}, TPid, Pkt0, Dict0, RecvData) -> - incr(recv, Pkt0, TPid, Dict), - Pkt = errors(Id, diameter_codec:decode(Id, Dict, Pkt0)), - incr_error(recv, Pkt, TPid, Dict), + incr(recv, Pkt0, TPid, AppDict), + Pkt = errors(Id, diameter_codec:decode(Id, AppDict, Pkt0)), + incr_error(recv, Pkt, TPid, AppDict), {Caps, Pkt, App, recv_R(App, TPid, Dict0, Caps, RecvData, Pkt)}; %% Note that the decode is different depending on whether or not Id is %% ?APP_ID_RELAY. @@ -522,14 +525,17 @@ send_A(_, _, _, _) -> %% send_A/6 -send_A(T, TPid, DictT, ReqPkt, EvalPktFs, EvalFs) -> - reply(T, TPid, DictT, EvalPktFs, ReqPkt), +send_A(T, TPid, {AppDict, Dict0} = DictT0, ReqPkt, EvalPktFs, EvalFs) -> + {MsgDict, Pkt} = reply(T, TPid, DictT0, EvalPktFs, ReqPkt), + incr(send, Pkt, TPid, AppDict), + incr_rc(send, Pkt, TPid, {MsgDict, AppDict, Dict0}), %% count outgoing + send(TPid, Pkt), lists:foreach(fun diameter_lib:eval/1, EvalFs). %% answer/6 answer({reply, Ans}, _Caps, _Pkt, App, Dict0, _RecvData) -> - {dict(App#diameter_app.dictionary, Dict0, Ans), Ans}; + {msg_dict(App#diameter_app.dictionary, Dict0, Ans), Ans}; answer({call, Opts}, Caps, Pkt, App, Dict0, RecvData) -> #diameter_caps{origin_host = {OH,_}} @@ -552,27 +558,37 @@ answer({answer_message, RC} = T, Caps, Pkt, App, Dict0, _RecvData) -> orelse ?ERROR({invalid_return, T, handle_request, App}), answer_message(RC, Caps, Dict0, Pkt). -%% dict/3 +%% msg_dict/3 +%% +%% Return the dictionary defining the message grammar in question: the +%% application dictionary or the common dictionary. + +msg_dict(AppDict, Dict0, [Msg]) + when is_list(Msg); + is_tuple(Msg) -> + msg_dict(AppDict, Dict0, Msg); -%% An incoming answer, not yet decoded. -dict(Dict, Dict0, #diameter_packet{header - = #diameter_header{is_request = false, - is_error = E}, - msg = undefined}) -> - if E -> Dict0; true -> Dict end; +msg_dict(AppDict, Dict0, Msg) -> + choose(is_answer_message(Msg, Dict0), Dict0, AppDict). -dict(Dict, Dict0, [Msg]) -> - dict(Dict, Dict0, Msg); +%% Incoming, not yet decoded. +is_answer_message(#diameter_packet{header = #diameter_header{} = H, + msg = undefined}, + Dict0) -> + is_answer_message([H], Dict0); -dict(Dict, Dict0, #diameter_packet{msg = Msg}) -> - dict(Dict, Dict0, Msg); +is_answer_message(#diameter_packet{msg = Msg}, Dict0) -> + is_answer_message(Msg, Dict0); -dict(Dict, Dict0, Msg) -> - choose(is_answer_message(Msg, Dict0), Dict0, Dict). +%% Message sent as a header/avps list. +is_answer_message([#diameter_header{is_request = R, is_error = E} | _], _) -> + E andalso not R; +%% Message sent as a tagged avp/value list. is_answer_message([Name | _], _) -> Name == 'answer-message'; +%% Message sent as a record. is_answer_message(Rec, Dict) -> try 'answer-message' == Dict:rec2msg(element(1,Rec)) @@ -642,7 +658,7 @@ resend(false, %% %% Relay a reply to a relayed request. -%% Answer from the peer: reset the hop by hop identifier and send. +%% Answer from the peer: reset the hop by hop identifier. resend(#diameter_packet{bin = B} = Pkt, _Caps, @@ -681,13 +697,13 @@ is_loop(Code, Vid, OH, Dict0, Avps) -> %% reply/5 %% Local answer ... -reply({Dict, Ans}, TPid, {AppDict, Dict0}, Fs, ReqPkt) -> - local(Ans, TPid, {Dict, AppDict, Dict0}, Fs, ReqPkt); +reply({MsgDict, Ans}, TPid, {AppDict, Dict0}, Fs, ReqPkt) -> + local(Ans, TPid, {MsgDict, AppDict, Dict0}, Fs, ReqPkt); %% ... or relayed. -reply(#diameter_packet{} = Pkt, TPid, _Dict0, Fs, _ReqPkt) -> +reply(#diameter_packet{} = Pkt, _TPid, {AppDict, Dict0}, Fs, _ReqPkt) -> eval_packet(Pkt, Fs), - send(TPid, Pkt). + {msg_dict(AppDict, Dict0, Pkt), Pkt}. %% local/5 %% @@ -700,14 +716,12 @@ local([Msg], TPid, DictT, Fs, ReqPkt) is_tuple(Msg) -> local(Msg, TPid, DictT, Fs, ReqPkt#diameter_packet{errors = []}); -local(Msg, TPid, {Dict, AppDict, Dict0} = DictT, Fs, ReqPkt) -> - Pkt = encode({Dict, AppDict}, +local(Msg, TPid, {MsgDict, AppDict, Dict0}, Fs, ReqPkt) -> + Pkt = encode({MsgDict, AppDict}, TPid, - reset(make_answer_packet(Msg, ReqPkt), Dict, Dict0), + reset(make_answer_packet(Msg, ReqPkt), MsgDict, Dict0), Fs), - incr(send, Pkt, TPid, AppDict), - incr_rc(send, Pkt, TPid, DictT), %% count outgoing - send(TPid, Pkt). + {MsgDict, Pkt}. %% reset/3 @@ -1067,54 +1081,75 @@ find_avp(Code, VId, [_ | Avps]) -> %% Increment a stats counter for result codes in incoming and outgoing %% answers. +%% Message sent as a header/avps list. +incr_result(send = Dir, + #diameter_packet{msg = [#diameter_header{} = H | _]} + = Pkt, + TPid, + DictT) -> + incr_res(Dir, Pkt#diameter_packet{header = H}, TPid, DictT); + %% Outgoing message as binary: don't count. (Sending binaries is only %% partially supported.) -incr_result(_, #diameter_packet{msg = undefined = No}, _, _) -> +incr_result(send, #diameter_packet{header = undefined = No}, _, _) -> No; %% Incoming or outgoing. Outgoing with encode errors never gets here %% since encode fails. -incr_result(Dir, Pkt, TPid, {Dict, AppDict, Dict0}) -> - #diameter_packet{header = #diameter_header{is_error = E} - = Hdr, - errors = Es} - = Pkt, +incr_result(Dir, Pkt, TPid, DictT) -> + incr_res(Dir, Pkt, TPid, DictT). + +incr_res(Dir, + #diameter_packet{header = #diameter_header{is_error = E} + = Hdr, + errors = Es} + = Pkt, + TPid, + DictT) -> + {MsgDict, AppDict, Dict0} = DictT, Id = msg_id(Hdr, AppDict), + %% Could be {relay, 0}, in which case the R-bit is redundant since + %% only answers are being counted. Let it be however, so that the + %% same tuple is in both send/recv and result code counters. %% Count incoming decode errors. recv /= Dir orelse [] == Es orelse incr_error(Dir, Id, TPid, AppDict), %% Exit on a missing result code. - T = rc_counter(Dict, Dir, Pkt), - T == false andalso ?LOGX(no_result_code, {Dict, Dir, Hdr}), + T = rc_counter(MsgDict, Dir, Pkt), + T == false andalso ?LOGX(no_result_code, {MsgDict, Dir, Hdr}), {Ctr, RC, Avp} = T, %% Or on an inappropriate value. is_result(RC, E, Dict0) - orelse ?LOGX(invalid_error_bit, {Dict, Dir, Hdr, Avp}), + orelse ?LOGX(invalid_error_bit, {MsgDict, Dir, Hdr, Avp}), incr(TPid, {Id, Dir, Ctr}), Ctr. %% msg_id/2 -msg_id(#diameter_packet{header = H}, Dict) -> - msg_id(H, Dict); +msg_id(#diameter_packet{header = H}, AppDict) -> + msg_id(H, AppDict); %% Only count on known keys so as not to be vulnerable to attack: %% there are 2^32 (application ids) * 2^24 (command codes) = 2^56 %% pairs for an attacker to choose from. -msg_id(Hdr, Dict) -> +msg_id(Hdr, AppDict) -> {Aid, Code, R} = Id = diameter_codec:msg_id(Hdr), - if Aid == ?APP_ID_RELAY -> + case AppDict:id() of + ?APP_ID_RELAY -> {relay, R}; - true -> - choose(Aid /= Dict:id() orelse '' == Dict:msg_name(Code, 0 == R), - unknown, - Id) + A -> + unknown(A /= Aid orelse '' == AppDict:msg_name(Code, 0 == R), Id) end. +unknown(true, {_, _, R}) -> + {unknown, R}; +unknown(false, Id) -> + Id. + %% No E-bit: can't be 3xxx. is_result(RC, false, _Dict0) -> RC < 3000 orelse 4000 =< RC; @@ -1142,7 +1177,11 @@ incr(TPid, Counter) -> %% applications MUST include either one Result-Code AVP or one %% Experimental-Result AVP. -rc_counter(Dict, recv, #diameter_packet{header = H, avps = As}) -> +rc_counter(Dict, Dir, #diameter_packet{header = H, + avps = As, + msg = Msg}) + when Dir == recv; %% decoded incoming + Msg == undefined -> %% relayed outgoing rc_counter(Dict, [H|As]); rc_counter(Dict, _, #diameter_packet{msg = Msg}) -> @@ -1434,12 +1473,12 @@ fold_record(Rec, R) -> %% send_R/6 send_R(Pkt0, - {TPid, Caps, #diameter_app{dictionary = Dict} = App}, + {TPid, Caps, #diameter_app{dictionary = AppDict} = App}, Opts, {Pid, Ref}, SvcName, Fs) -> - Pkt = encode(Dict, TPid, Pkt0, Fs), + Pkt = encode(AppDict, TPid, Pkt0, Fs), #options{timeout = Timeout} = Opts, @@ -1452,7 +1491,7 @@ send_R(Pkt0, packet = Pkt0}, try - incr(send, Pkt, TPid, Dict), + incr(send, Pkt, TPid, AppDict), TRef = send_request(TPid, Pkt, Req, SvcName, Timeout), Pid ! Ref, %% tell caller a send has been attempted handle_answer(SvcName, @@ -1492,10 +1531,10 @@ handle_answer(SvcName, id = Id} = App, {answer, Req, Dict0, Pkt}) -> - Dict = dict(AppDict, Dict0, Pkt), - handle_A(errors(Id, diameter_codec:decode({Dict, AppDict}, Pkt)), + MsgDict = msg_dict(AppDict, Dict0, Pkt), + handle_A(errors(Id, diameter_codec:decode({MsgDict, AppDict}, Pkt)), SvcName, - Dict, + MsgDict, Dict0, App, Req). @@ -1765,19 +1804,19 @@ retransmit(T, {_, _, App}, _, _, _, _) -> ?ERROR({invalid_return, T, prepare_retransmit, App}). resend_request(Pkt0, - {TPid, Caps, #diameter_app{dictionary = Dict}}, + {TPid, Caps, #diameter_app{dictionary = AppDict}}, Req0, SvcName, Tmo, Fs) -> - Pkt = encode(Dict, TPid, Pkt0, Fs), + Pkt = encode(AppDict, TPid, Pkt0, Fs), Req = Req0#request{transport = TPid, packet = Pkt0, caps = Caps}, ?LOG(retransmission, Pkt#diameter_packet.header), - incr(TPid, {msg_id(Pkt, Dict), send, retransmission}), + incr(TPid, {msg_id(Pkt, AppDict), send, retransmission}), TRef = send_request(TPid, Pkt, Req, SvcName, Tmo), {TRef, Req}. @@ -1887,7 +1926,7 @@ get_avp(Dict, Name, [#diameter_header{} | Avps]) -> find_avp(Code, VId, Avps) of A -> - avp_decode(Dict, Name, ungroup(A)) + (avp_decode(Dict, Name, ungroup(A)))#diameter_avp{name = Name} catch error: _ -> undefined diff --git a/lib/diameter/src/diameter.appup.src b/lib/diameter/src/diameter.appup.src index 0ef0fd35a9..b89859ed24 100644 --- a/lib/diameter/src/diameter.appup.src +++ b/lib/diameter/src/diameter.appup.src @@ -65,11 +65,14 @@ {update, diameter_sup, supervisor}]}, {"1.9", [{load_module, diameter_codec}, %% 17.5 {load_module, diameter_traffic}, + {load_module, diameter_sctp}, {load_module, diameter_gen_base_rfc6733}, {load_module, diameter_gen_acct_rfc6733}, {load_module, diameter_gen_base_rfc3588}, {load_module, diameter_gen_base_accounting}, - {load_module, diameter_gen_relay}]} + {load_module, diameter_gen_relay}]}, + {"1.9.1", [{load_module, diameter_traffic}, %% 17.5.3 + {load_module, diameter_sctp}]} ], [ {"0.9", [{restart_application, diameter}]}, @@ -120,7 +123,10 @@ {load_module, diameter_gen_base_rfc3588}, {load_module, diameter_gen_acct_rfc6733}, {load_module, diameter_gen_base_rfc6733}, + {load_module, diameter_sctp}, {load_module, diameter_traffic}, - {load_module, diameter_codec}]} + {load_module, diameter_codec}]}, + {"1.9.1", [{load_module, diameter_sctp}, + {load_module, diameter_traffic}]} ] }. diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl index 2c8d6f0a14..f80de0a816 100644 --- a/lib/diameter/src/transport/diameter_sctp.erl +++ b/lib/diameter/src/transport/diameter_sctp.erl @@ -223,9 +223,9 @@ init(T) -> i({listen, Ref, {Opts, Addrs}}) -> {[Matches], Rest} = proplists:split(Opts, [accept]), {LAs, Sock} = AS = open(Addrs, Rest, ?DEFAULT_PORT), - proc_lib:init_ack({ok, self(), LAs}), ok = gen_sctp:listen(Sock, true), true = diameter_reg:add_new({?MODULE, listener, {Ref, AS}}), + proc_lib:init_ack({ok, self(), LAs}), start_timer(#listener{ref = Ref, socket = Sock, accept = accept(Matches)}); diff --git a/lib/diameter/test/diameter_3xxx_SUITE.erl b/lib/diameter/test/diameter_3xxx_SUITE.erl index 44fc3a60aa..44cb0cc484 100644 --- a/lib/diameter/test/diameter_3xxx_SUITE.erl +++ b/lib/diameter/test/diameter_3xxx_SUITE.erl @@ -195,13 +195,13 @@ counters(_, _, _, _) -> stats(?CLIENT, E, rfc3588, L) when E == answer; E == answer_3xxx -> - [{{unknown,recv},2}, + [{{{unknown,0},recv},2}, {{{0,257,0},recv},1}, {{{0,257,1},send},1}, {{{0,275,0},recv},6}, {{{0,275,1},send},10}, - {{unknown,recv,{'Result-Code',3001}},1}, - {{unknown,recv,{'Result-Code',3007}},1}, + {{{unknown,0},recv,{'Result-Code',3001}},1}, + {{{unknown,0},recv,{'Result-Code',3007}},1}, {{{0,257,0},recv,{'Result-Code',2001}},1}, {{{0,275,0},recv,{'Result-Code',2001}},1}, {{{0,275,0},recv,{'Result-Code',3008}},2}, @@ -213,15 +213,15 @@ stats(?CLIENT, E, rfc3588, L) stats(?SERVER, E, rfc3588, L) when E == answer; E == answer_3xxx -> - [{{unknown,recv},1}, - {{unknown,send},2}, + [{{{unknown,0},send},2}, + {{{unknown,1},recv},1}, {{{0,257,0},send},1}, {{{0,257,1},recv},1}, {{{0,275,0},send},6}, {{{0,275,1},recv},8}, - {{unknown,recv,error},1}, - {{unknown,send,{'Result-Code',3001}},1}, - {{unknown,send,{'Result-Code',3007}},1}, + {{{unknown,0},send,{'Result-Code',3001}},1}, + {{{unknown,0},send,{'Result-Code',3007}},1}, + {{{unknown,1},recv,error},1}, {{{0,257,0},send,{'Result-Code',2001}},1}, {{{0,275,0},send,{'Result-Code',2001}},1}, {{{0,275,0},send,{'Result-Code',3008}},2}, @@ -232,13 +232,13 @@ stats(?SERVER, E, rfc3588, L) = L; stats(?CLIENT, answer, rfc6733, L) -> - [{{unknown,recv},2}, + [{{{unknown,0},recv},2}, {{{0,257,0},recv},1}, {{{0,257,1},send},1}, {{{0,275,0},recv},8}, {{{0,275,1},send},10}, - {{unknown,recv,{'Result-Code',3001}},1}, - {{unknown,recv,{'Result-Code',3007}},1}, + {{{unknown,0},recv,{'Result-Code',3001}},1}, + {{{unknown,0},recv,{'Result-Code',3007}},1}, {{{0,257,0},recv,{'Result-Code',2001}},1}, {{{0,275,0},recv,{'Result-Code',3008}},2}, {{{0,275,0},recv,{'Result-Code',3999}},1}, @@ -248,15 +248,15 @@ stats(?CLIENT, answer, rfc6733, L) -> = L; stats(?SERVER, answer, rfc6733, L) -> - [{{unknown,recv},1}, - {{unknown,send},2}, + [{{{unknown,0},send},2}, + {{{unknown,1},recv},1}, {{{0,257,0},send},1}, {{{0,257,1},recv},1}, {{{0,275,0},send},8}, {{{0,275,1},recv},8}, - {{unknown,recv,error},1}, - {{unknown,send,{'Result-Code',3001}},1}, - {{unknown,send,{'Result-Code',3007}},1}, + {{{unknown,0},send,{'Result-Code',3001}},1}, + {{{unknown,0},send,{'Result-Code',3007}},1}, + {{{unknown,1},recv,error},1}, {{{0,257,0},send,{'Result-Code',2001}},1}, {{{0,275,0},send,{'Result-Code',3008}},2}, {{{0,275,0},send,{'Result-Code',3999}},1}, @@ -267,13 +267,13 @@ stats(?SERVER, answer, rfc6733, L) -> = L; stats(?CLIENT, answer_3xxx, rfc6733, L) -> - [{{unknown,recv},2}, + [{{{unknown,0},recv},2}, {{{0,257,0},recv},1}, {{{0,257,1},send},1}, {{{0,275,0},recv},8}, {{{0,275,1},send},10}, - {{unknown,recv,{'Result-Code',3001}},1}, - {{unknown,recv,{'Result-Code',3007}},1}, + {{{unknown,0},recv,{'Result-Code',3001}},1}, + {{{unknown,0},recv,{'Result-Code',3007}},1}, {{{0,257,0},recv,{'Result-Code',2001}},1}, {{{0,275,0},recv,{'Result-Code',2001}},1}, {{{0,275,0},recv,{'Result-Code',3008}},2}, @@ -284,15 +284,15 @@ stats(?CLIENT, answer_3xxx, rfc6733, L) -> = L; stats(?SERVER, answer_3xxx, rfc6733, L) -> - [{{unknown,recv},1}, - {{unknown,send},2}, + [{{{unknown,0},send},2}, + {{{unknown,1},recv},1}, {{{0,257,0},send},1}, {{{0,257,1},recv},1}, {{{0,275,0},send},8}, {{{0,275,1},recv},8}, - {{unknown,recv,error},1}, - {{unknown,send,{'Result-Code',3001}},1}, - {{unknown,send,{'Result-Code',3007}},1}, + {{{unknown,0},send,{'Result-Code',3001}},1}, + {{{unknown,0},send,{'Result-Code',3007}},1}, + {{{unknown,1},recv,error},1}, {{{0,257,0},send,{'Result-Code',2001}},1}, {{{0,275,0},send,{'Result-Code',2001}},1}, {{{0,275,0},send,{'Result-Code',3008}},2}, @@ -304,12 +304,12 @@ stats(?SERVER, answer_3xxx, rfc6733, L) -> = L; stats(?CLIENT, callback, rfc3588, L) -> - [{{unknown,recv},1}, + [{{{unknown,0},recv},1}, {{{0,257,0},recv},1}, {{{0,257,1},send},1}, {{{0,275,0},recv},6}, {{{0,275,1},send},10}, - {{unknown,recv,{'Result-Code',3007}},1}, + {{{unknown,0},recv,{'Result-Code',3007}},1}, {{{0,257,0},recv,{'Result-Code',2001}},1}, {{{0,275,0},recv,{'Result-Code',2001}},2}, {{{0,275,0},recv,{'Result-Code',3999}},1}, @@ -318,14 +318,14 @@ stats(?CLIENT, callback, rfc3588, L) -> = L; stats(?SERVER, callback, rfc3588, L) -> - [{{unknown,recv},1}, - {{unknown,send},1}, + [{{{unknown,0},send},1}, + {{{unknown,1},recv},1}, {{{0,257,0},send},1}, {{{0,257,1},recv},1}, {{{0,275,0},send},6}, {{{0,275,1},recv},8}, - {{unknown,recv,error},1}, - {{unknown,send,{'Result-Code',3007}},1}, + {{{unknown,0},send,{'Result-Code',3007}},1}, + {{{unknown,1},recv,error},1}, {{{0,257,0},send,{'Result-Code',2001}},1}, {{{0,275,0},send,{'Result-Code',2001}},2}, {{{0,275,0},send,{'Result-Code',3999}},1}, @@ -335,12 +335,12 @@ stats(?SERVER, callback, rfc3588, L) -> = L; stats(?CLIENT, callback, rfc6733, L) -> - [{{unknown,recv},1}, + [{{{unknown,0},recv},1}, {{{0,257,0},recv},1}, {{{0,257,1},send},1}, {{{0,275,0},recv},8}, {{{0,275,1},send},10}, - {{unknown,recv,{'Result-Code',3007}},1}, + {{{unknown,0},recv,{'Result-Code',3007}},1}, {{{0,257,0},recv,{'Result-Code',2001}},1}, {{{0,275,0},recv,{'Result-Code',2001}},2}, {{{0,275,0},recv,{'Result-Code',3999}},1}, @@ -350,14 +350,14 @@ stats(?CLIENT, callback, rfc6733, L) -> = L; stats(?SERVER, callback, rfc6733, L) -> - [{{unknown,recv},1}, - {{unknown,send},1}, + [{{{unknown,0},send},1}, + {{{unknown,1},recv},1}, {{{0,257,0},send},1}, {{{0,257,1},recv},1}, {{{0,275,0},send},8}, {{{0,275,1},recv},8}, - {{unknown,recv,error},1}, - {{unknown,send,{'Result-Code',3007}},1}, + {{{unknown,0},send,{'Result-Code',3007}},1}, + {{{unknown,1},recv,error},1}, {{{0,257,0},send,{'Result-Code',2001}},1}, {{{0,275,0},send,{'Result-Code',2001}},2}, {{{0,275,0},send,{'Result-Code',3999}},1}, diff --git a/lib/diameter/test/diameter_config_SUITE.erl b/lib/diameter/test/diameter_config_SUITE.erl index bbdf672291..4bcaa8119f 100644 --- a/lib/diameter/test/diameter_config_SUITE.erl +++ b/lib/diameter/test/diameter_config_SUITE.erl @@ -50,7 +50,7 @@ {request_errors, RE}, {call_mutates_state, C}]] || D <- [diameter_gen_base_rfc3588, diameter_gen_base_rfc6733], - M <- [?MODULE, [?MODULE, now()]], + M <- [?MODULE, [?MODULE, diameter_lib:now()]], A <- [0, common, make_ref()], S <- [[], make_ref()], AE <- [report, callback, discard], diff --git a/lib/diameter/test/diameter_relay_SUITE.erl b/lib/diameter/test/diameter_relay_SUITE.erl index 735a908d97..7142239bbb 100644 --- a/lib/diameter/test/diameter_relay_SUITE.erl +++ b/lib/diameter/test/diameter_relay_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2015. 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 @@ -49,6 +49,7 @@ send_timeout_1/1, send_timeout_2/1, info/1, + counters/1, disconnect/1, stop_services/1, stop/1]). @@ -120,6 +121,7 @@ all() -> start_services, connect, {group, all}, + counters, {group, all, [parallel]}, disconnect, stop_services, @@ -201,8 +203,8 @@ send3(_Config) -> send4(_Config) -> call(?SERVER4). -%% Send an ASR that loops between the relays and expect the loop to -%% be detected. +%% Send an ASR that loops between the relays (RELAY1 -> RELAY2 -> +%% RELAY1) and expect the loop to be detected. send_loop(_Config) -> Req = ['ASR', {'Destination-Realm', realm(?SERVER1)}, {'Destination-Host', ?SERVER1}, @@ -227,8 +229,103 @@ send_timeout(Tmo) -> call(Req, [{filter, realm}, {timeout, Tmo}]). info(_Config) -> + %% Wait for RELAY1 to have answered all requests, so that the + %% suite doesn't end before all answers are sent and counted. + receive after 6000 -> ok end, [] = ?util:info(). +counters(_Config) -> + [] = ?util:run([[fun counters/2, K, S] + || K <- [statistics, transport, connections], + S <- ?SERVICES]). + +counters(Key, Svc) -> + counters(Key, Svc, [_|_] = diameter:service_info(Svc, Key)). + +counters(statistics, Svc, Stats) -> + stats(Svc, lists:foldl(fun({K,N},D) -> orddict:update_counter(K, N, D) end, + orddict:new(), + lists:append([L || {P,L} <- Stats, is_pid(P)]))); + +counters(_, _, _) -> + todo. + +stats(?CLIENT, L) -> + [{{{0,257,0},recv},2}, %% CEA + {{{0,257,1},send},2}, %% CER + {{{0,258,0},recv},1}, %% RAA (send_timeout_1) + {{{0,258,1},send},2}, %% RAR (send_timeout_[12]) + {{{0,274,0},recv},1}, %% ASA (send_loop) + {{{0,274,1},send},1}, %% ASR (send_loop) + {{{0,275,0},recv},4}, %% STA (send[1-4]) + {{{0,275,1},send},4}, %% STR (send[1-4]) + {{{unknown,0},recv,discarded},1}, %% RAR (send_timeout_2) + {{{0,257,0},recv,{'Result-Code',2001}},2}, %% CEA + {{{0,258,0},recv,{'Result-Code',3002}},1}, %% RAA (send_timeout_1) + {{{0,274,0},recv,{'Result-Code',3005}},1}, %% ASA (send_loop) + {{{0,275,0},recv,{'Result-Code',2001}},4}] %% STA (send[1-4]) + = L; + +stats(S, L) + when S == ?SERVER1; + S == ?SERVER2; + S == ?SERVER3; + S == ?SERVER4 -> + [{{{0,257,0},send},1}, %% CEA + {{{0,257,1},recv},1}, %% CER + {{{0,275,0},send},1}, %% STA (send[1-4]) + {{{0,275,1},recv},1}, %% STR (send[1-4]) + {{{0,257,0},send,{'Result-Code',2001}},1}, %% CEA + {{{0,275,0},send,{'Result-Code',2001}},1}] %% STA (send[1-4]) + = L; + +stats(?RELAY1, L) -> + [{{{relay,0},recv},3}, %% STA x 2 (send[12]) + %% ASA (send_loop) + {{{relay,0},send},6}, %% STA x 2 (send[12]) + %% ASA x 2 (send_loop) + %% RAA x 2 (send_timeout_[12]) + {{{relay,1},recv},6}, %% STR x 2 (send[12]) + %% ASR x 2 (send_loop) + %% RAR x 2 (send_timeout_[12]) + {{{relay,1},send},5}, %% STR x 2 (send[12]) + %% ASR (send_loop) + %% RAR x 2 (send_timeout_[12]) + {{{0,257,0},recv},3}, %% CEA + {{{0,257,0},send},1}, %% " + {{{0,257,1},recv},1}, %% CER + {{{0,257,1},send},3}, %% " + {{{relay,0},recv,{'Result-Code',2001}},2}, %% STA x 2 (send[34]) + {{{relay,0},recv,{'Result-Code',3005}},1}, %% ASA (send_loop) + {{{relay,0},send,{'Result-Code',2001}},2}, %% STA x 2 (send[34]) + {{{relay,0},send,{'Result-Code',3002}},2}, %% RAA (send_timeout_[12]) + {{{relay,0},send,{'Result-Code',3005}},2}, %% ASA (send_loop) + {{{0,257,0},recv,{'Result-Code',2001}},3}, %% CEA + {{{0,257,0},send,{'Result-Code',2001}},1}] %% " + = L; + +stats(?RELAY2, L) -> + [{{{relay,0},recv},3}, %% STA x 2 (send[34]) + %% ASA (send_loop) + {{{relay,0},send},3}, %% STA x 2 (send[34]) + %% ASA (send_loop) + {{{relay,1},recv},5}, %% STR x 2 (send[34]) + %% RAR x 2 (send_timeout_[12]) + %% ASR (send_loop) + {{{relay,1},send},3}, %% STR x 2 (send[34]) + %% ASR (send_loop) + {{{0,257,0},recv},2}, %% CEA + {{{0,257,0},send},2}, %% " + {{{0,257,1},recv},2}, %% CER + {{{0,257,1},send},2}, %% " + {{{relay,0},recv,{'Result-Code',2001}},2}, %% STA x 2 (send[34]) + {{{relay,0},recv,{'Result-Code',3005}},1}, %% ASA (send_loop) + {{{relay,0},send,{'Result-Code',2001}},2}, %% STA x 2 (send[34]) + {{{relay,0},send,{'Result-Code',3005}},1}, %% ASA (send_loop) + {{{0,257,0},recv,{'Result-Code',2001}},2}, %% CEA + {{{0,257,0},send,{'Result-Code',2001}},2}] %% " + = L. + %% =========================================================================== realm(Host) -> @@ -303,18 +400,24 @@ handle_request(Pkt, OH, {_Ref, #diameter_caps{origin_host = {OH,_}} = Caps}) when OH /= ?CLIENT -> request(Pkt, Caps). -%% RELAY1 routes any ASR or RAR to RELAY2 ... +%% RELAY1 answers ACR after it's timed out at the client. +request(#diameter_packet{header = #diameter_header{cmd_code = 271}}, + #diameter_caps{origin_host = {?RELAY1, _}}) -> + receive after 1000 -> {answer_message, 3004} end; %% TOO_BUSY + +%% RELAY1 routes any ASR or RAR to RELAY2. request(#diameter_packet{header = #diameter_header{cmd_code = C}}, #diameter_caps{origin_host = {?RELAY1, _}}) when C == 274; %% ASR C == 258 -> %% RAR {relay, [{filter, {realm, realm(?RELAY2)}}]}; -%% ... which in turn routes it back. Expect diameter to either answer -%% either with DIAMETER_LOOP_DETECTED/DIAMETER_UNABLE_TO_COMPLY. +%% RELAY2 routes ASR back to RELAY1 to induce DIAMETER_LOOP_DETECTED. request(#diameter_packet{header = #diameter_header{cmd_code = 274}}, #diameter_caps{origin_host = {?RELAY2, _}}) -> {relay, [{filter, {host, ?RELAY1}}]}; + +%% RELAY2 discards RAR to induce DIAMETER_UNABLE_TO_DELIVER. request(#diameter_packet{header = #diameter_header{cmd_code = 258}}, #diameter_caps{origin_host = {?RELAY2, _}}) -> discard; diff --git a/lib/diameter/test/diameter_tls_SUITE.erl b/lib/diameter/test/diameter_tls_SUITE.erl index 55565692ec..e5bbda9c91 100644 --- a/lib/diameter/test/diameter_tls_SUITE.erl +++ b/lib/diameter/test/diameter_tls_SUITE.erl @@ -319,19 +319,19 @@ make_cert(Dir, Base) -> make_cert(Dir, Base ++ "_key.pem", Base ++ "_ca.pem"). make_cert(Dir, Keyfile, Certfile) -> - [K,C] = Paths = [filename:join([Dir, F]) || F <- [Keyfile, Certfile]], + [KP,CP] = [filename:join([Dir, F]) || F <- [Keyfile, Certfile]], - KCmd = join(["openssl genrsa -out", K, "2048"]), - CCmd = join(["openssl req -new -x509 -key", K, "-out", C, "-days 7", - "-subj /C=SE/ST=./L=Stockholm/CN=www.erlang.org"]), + KC = join(["openssl genrsa -out", KP, "2048"]), + CC = join(["openssl req -new -x509 -key", KP, "-out", CP, "-days 7", + "-subj /C=SE/ST=./L=Stockholm/CN=www.erlang.org"]), %% Hope for the best and only check that files are written. - os:cmd(KCmd), - os:cmd(CCmd), + [{_, _, {ok,_}},{_, _, {ok,_}}] + = [{P,O,T} || {P,C} <- [{KP,KC}, {CP,CC}], + O <- [os:cmd(C)], + T <- [file:read_file_info(P)]], - [_,_] = [T || P <- Paths, {ok, T} <- [file:read_file_info(P)]], - - {K,C}. + {KP,CP}. join(Strs) -> string:join(Strs, " "). diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 7ff6ba7ab9..17faf30a9b 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -145,8 +145,12 @@ %% them as binary. -define(STRING_DECODES, [true, false]). +%% Which transport protocol to use. +-define(TRANSPORTS, [tcp, sctp]). + -record(group, - {client_service, + {transport, + client_service, client_encoding, client_dict0, client_strings, @@ -234,19 +238,20 @@ %% =========================================================================== suite() -> - [{timetrap, {seconds, 60}}]. + [{timetrap, {seconds, 10}}]. all() -> [start, result_codes, {group, traffic}, outstanding, empty, stop]. groups() -> Ts = tc(), + Sctp = ?util:have_sctp(), [{?util:name([R,D,A,C]), [parallel], Ts} || R <- ?ENCODINGS, D <- ?RFCS, A <- ?ENCODINGS, C <- ?CONTAINERS] ++ - [{?util:name([R,D,A,C,SD,CD]), + [{?util:name([T,R,D,A,C,SD,CD]), [], [start_services, add_transports, @@ -254,15 +259,19 @@ groups() -> {group, ?util:name([R,D,A,C])}, remove_transports, stop_services]} - || R <- ?ENCODINGS, + || T <- ?TRANSPORTS, + T /= sctp orelse Sctp, + R <- ?ENCODINGS, D <- ?RFCS, A <- ?ENCODINGS, C <- ?CONTAINERS, SD <- ?STRING_DECODES, CD <- ?STRING_DECODES] ++ - [{traffic, [parallel], [{group, ?util:name([R,D,A,C,SD,CD])} - || R <- ?ENCODINGS, + [{traffic, [parallel], [{group, ?util:name([T,R,D,A,C,SD,CD])} + || T <- ?TRANSPORTS, + T /= sctp orelse Sctp, + R <- ?ENCODINGS, D <- ?RFCS, A <- ?ENCODINGS, C <- ?CONTAINERS, @@ -271,8 +280,9 @@ groups() -> init_per_group(Name, Config) -> case ?util:name(Name) of - [R,D,A,C,SD,CD] -> - G = #group{client_service = [$C|?util:unique_string()], + [T,R,D,A,C,SD,CD] -> + G = #group{transport = T, + client_service = [$C|?util:unique_string()], client_encoding = R, client_dict0 = dict0(D), client_strings = CD, @@ -288,8 +298,18 @@ init_per_group(Name, Config) -> end_per_group(_, _) -> ok. +%% Skip testcases that can reasonably fail under SCTP. init_per_testcase(Name, Config) -> - [{testcase, Name} | Config]. + case [skip || #group{transport = sctp} + <- [proplists:get_value(group, Config)], + send_maxlen == Name + orelse send_long == Name] + of + [skip] -> + {skip, sctp}; + [] -> + [{testcase, Name} | Config] + end. end_per_testcase(_, _) -> ok. @@ -367,16 +387,18 @@ start_services(Config) -> | ?SERVICE(CN, CD)]). add_transports(Config) -> - #group{client_service = CN, + #group{transport = T, + client_service = CN, server_service = SN} = group(Config), LRef = ?util:listen(SN, - tcp, + T, [{capabilities_cb, fun capx/2}, + {pool_size, 8}, {spawn_opt, [{min_heap_size, 8096}]}, {applications, apps(rfc3588)}]), Cs = [?util:connect(CN, - tcp, + T, LRef, [{id, Id}, {capabilities, [{'Origin-State-Id', origin(Id)}]}, diff --git a/lib/diameter/test/diameter_transport_SUITE.erl b/lib/diameter/test/diameter_transport_SUITE.erl index f098851bea..78bddbd1cf 100644 --- a/lib/diameter/test/diameter_transport_SUITE.erl +++ b/lib/diameter/test/diameter_transport_SUITE.erl @@ -64,7 +64,7 @@ = #diameter_caps{host_ip_address = Addrs}}). -%% The term we register after open a listening port with gen_tcp. +%% The term we register after open a listening port with gen_{tcp,sctp}. -define(TEST_LISTENER(Ref, PortNr), {?MODULE, listen, Ref, PortNr}). @@ -85,7 +85,7 @@ %% =========================================================================== suite() -> - [{timetrap, {minutes, 2}}]. + [{timetrap, {seconds, 15}}]. all() -> [start, @@ -401,12 +401,13 @@ gen_listen(tcp) -> %% gen_accept/2 gen_accept(sctp, Sock) -> - Assoc = ?RECV(?SCTP(Sock, {_, #sctp_assoc_change{state = comm_up, - outbound_streams = O, - inbound_streams = I, - assoc_id = A}}), - {O, I, A}), - putr(assoc, Assoc), + #sctp_assoc_change{state = comm_up, + outbound_streams = OS, + inbound_streams = IS, + assoc_id = Id} + = ?RECV(?SCTP(Sock, {_, #sctp_assoc_change{} = S}), S), + + putr(assoc, {OS, IS, Id}), {ok, Sock}; gen_accept(tcp, LSock) -> gen_tcp:accept(LSock). diff --git a/lib/diameter/test/diameter_util.erl b/lib/diameter/test/diameter_util.erl index c496876ee1..df7d268429 100644 --- a/lib/diameter/test/diameter_util.erl +++ b/lib/diameter/test/diameter_util.erl @@ -204,13 +204,14 @@ seed() -> %% unique_string/0 unique_string() -> - us(diameter_lib:now()). - -us({M,S,U}) -> - tl(lists:append(["-" ++ integer_to_list(N) || N <- [M,S,U]])); - -us(MonoT) -> - integer_to_list(MonoT). + try erlang:unique_integer() of + N -> + integer_to_list(N) + catch + error: undef -> %% OTP < 18 + {M,S,U} = timestamp(), + tl(lists:append(["-" ++ integer_to_list(N) || N <- [M,S,U]])) + end. %% --------------------------------------------------------------------------- %% have_sctp/0 diff --git a/lib/diameter/test/diameter_watchdog_SUITE.erl b/lib/diameter/test/diameter_watchdog_SUITE.erl index 5a3ff2c92f..f39e12686e 100644 --- a/lib/diameter/test/diameter_watchdog_SUITE.erl +++ b/lib/diameter/test/diameter_watchdog_SUITE.erl @@ -673,8 +673,7 @@ jitter(T,D) -> %% Generate a unique hostname for the faked peer. hostname() -> - {M,S,U} = diameter_util:timestamp(), - lists:flatten(io_lib:format("~p-~p-~p", [M,S,U])). + ?util:unique_string(). putr(Key, Val) -> put({?MODULE, Key}, Val). diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk index db7f72c44e..c278e74dca 100644 --- a/lib/diameter/vsn.mk +++ b/lib/diameter/vsn.mk @@ -16,5 +16,5 @@ # %CopyrightEnd% APPLICATION = diameter -DIAMETER_VSN = 1.9.1 +DIAMETER_VSN = 1.9.2 APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN) diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk index 24cfbf16d5..49a73331c6 100644 --- a/lib/edoc/vsn.mk +++ b/lib/edoc/vsn.mk @@ -1 +1 @@ -EDOC_VSN = 0.7.16 +EDOC_VSN = 0.7.17 diff --git a/lib/et/src/Makefile b/lib/et/src/Makefile index 377e593712..b6873371ed 100644 --- a/lib/et/src/Makefile +++ b/lib/et/src/Makefile @@ -65,7 +65,7 @@ APPUP_TARGET = $(EBIN)/$(APPUP_FILE) # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- -ERL_COMPILE_FLAGS += -pa $(ERL_TOP)/lib/et/ebin -I../include +ERL_COMPILE_FLAGS += -pa $(ERL_TOP)/lib/et/ebin -I../include -Werror # ---------------------------------------------------- # Special Build Targets diff --git a/lib/et/src/et_collector.erl b/lib/et/src/et_collector.erl index e05c67be60..1f60dee8ca 100644 --- a/lib/et/src/et_collector.erl +++ b/lib/et/src/et_collector.erl @@ -64,6 +64,8 @@ -export([init/1,terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2]). +-compile([{nowarn_deprecated_function,[{erlang,now,0}]}]). + -include("et_internal.hrl"). -include("../include/et.hrl"). diff --git a/lib/et/src/et_selector.erl b/lib/et/src/et_selector.erl index c8e9c907b2..5497096377 100644 --- a/lib/et/src/et_selector.erl +++ b/lib/et/src/et_selector.erl @@ -28,6 +28,8 @@ parse_event/2 ]). +-compile([{nowarn_deprecated_function,[{erlang,now,0}]}]). + -include("../include/et.hrl"). %%---------------------------------------------------------------------- diff --git a/lib/eunit/doc/overview.edoc b/lib/eunit/doc/overview.edoc index eb60f673ef..df716cdeea 100644 --- a/lib/eunit/doc/overview.edoc +++ b/lib/eunit/doc/overview.edoc @@ -572,21 +572,6 @@ Examples: <dt>`assertNotMatch(GuardedPattern, Expr)'</dt> <dd>The inverse case of assertMatch, for convenience. </dd> -<dt>`assertReceive(GuardedPattern, Timeout)'</dt> -<dd>Waits for up to the `Timeout' milliseconds for a message to arrive -in the mailbox of the current process that matches against the -`GuardedPattern' if testing is enabled. -If no message matching the `GuardedPattern' is received in the specified -`Timeout' interval, the assertion fails and an informative exception will -be generated; see the `assert' macro for further details. `GuardedPattern' -can be anything that you can write on the left hand side of the `->' -symbol in a case-clause, except that it cannot contain comma-separated -guard tests. - -Examples: -```?assertReceive(done, 1000)''' -```?assertReceive(Bin when byte_size(Bin) > 10, 1000)''' -</dd> <dt>`assertEqual(Expect, Expr)'</dt> <dd>Evaluates the expressions `Expect' and `Expr' and compares the results for equality, if testing is enabled. If the values are not diff --git a/lib/eunit/include/eunit.hrl b/lib/eunit/include/eunit.hrl index 8a829396ec..53d291430d 100644 --- a/lib/eunit/include/eunit.hrl +++ b/lib/eunit/include/eunit.hrl @@ -166,26 +166,6 @@ %% This is mostly a convenience which gives more detailed reports. %% Note: Guard is a guarded pattern, and can not be used for value. -ifdef(NOASSERT). --define(assertReceive(Guard, Timeout), ok). --else. --define(assertReceive(Guard, Timeout), - begin - ((fun () -> - receive (Guard) -> ok - after Timeout -> erlang:error({assertReceive_timedout, - [{module, ?MODULE}, - {line, ?LINE}, - {pattern, (??Guard)}, - {timeout, __V}]}) - end - end)()) - end). --endif. --define(_assertReceive(Guard, Timeout), ?_test(?assertReceive(Guard, Timeout))). - -%% This is mostly a convenience which gives more detailed reports. -%% Note: Guard is a guarded pattern, and can not be used for value. --ifdef(NOASSERT). -define(assertMatch(Guard, Expr), ok). -else. -define(assertMatch(Guard, Expr), diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk index dca8b3ece0..b551ee6eb6 100644 --- a/lib/eunit/vsn.mk +++ b/lib/eunit/vsn.mk @@ -1 +1 @@ -EUNIT_VSN = 2.2.9 +EUNIT_VSN = 2.3 diff --git a/lib/hipe/cerl/cerl_pmatch.erl b/lib/hipe/cerl/cerl_pmatch.erl index 3bc93e80dd..4f04b0a7ed 100644 --- a/lib/hipe/cerl/cerl_pmatch.erl +++ b/lib/hipe/cerl/cerl_pmatch.erl @@ -31,7 +31,7 @@ -module(cerl_pmatch). --define(NO_UNUSED, true). +%%-define(NO_UNUSED, true). -export([clauses/2]). -ifndef(NO_UNUSED). @@ -59,6 +59,8 @@ %% @see transform/2 -ifndef(NO_UNUSED). +-spec core_transform(cerl:c_module(), [_]) -> cerl:c_module(). + core_transform(M, Opts) -> cerl:to_records(transform(cerl:from_records(M), Opts)). -endif. % NO_UNUSED @@ -76,6 +78,8 @@ core_transform(M, Opts) -> %% @see core_transform/2 -ifndef(NO_UNUSED). +-spec transform(cerl:cerl(), [_]) -> cerl:cerl(). + transform(M, _Opts) -> expr(M, env__empty()). -endif. % NO_UNUSED @@ -109,7 +113,7 @@ transform(M, _Opts) -> %% @see expr/2 %% @see transform/2 --spec clauses([cerl:cerl()], rec_env:environment()) -> +-spec clauses([cerl:cerl(),...], rec_env:environment()) -> {cerl:cerl(), [cerl:cerl()]}. clauses(Cs, Env) -> @@ -406,6 +410,8 @@ make_let(Vs, A, B) -> %% @see rec_env -ifndef(NO_UNUSED). +-spec expr(cerl:cerl(), rec_env:environment()) -> cerl:cerl(). + expr(E, Env) -> case cerl:type(E) of literal -> diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index 5b1401b34a..ee77d65932 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -1113,8 +1113,8 @@ type(hipe_bifs, set_native_address, 3, Xs, Opaques) -> type(hipe_bifs, set_native_address_in_fe, 2, Xs, Opaques) -> strict(hipe_bifs, set_native_address_in_fe, 2, Xs, fun (_) -> t_atom('true') end, Opaques); -type(hipe_bifs, system_crc, 1, Xs, Opaques) -> - strict(hipe_bifs, system_crc, 1, Xs, fun (_) -> t_crc32() end, Opaques); +type(hipe_bifs, system_crc, 0, _, _Opaques) -> + t_crc32(); type(hipe_bifs, term_to_word, 1, Xs, Opaques) -> strict(hipe_bifs, term_to_word, 1, Xs, fun (_) -> t_integer() end, Opaques); @@ -2490,8 +2490,8 @@ arg_types(hipe_bifs, set_native_address, 3) -> [t_mfa(), t_integer(), t_boolean()]; arg_types(hipe_bifs, set_native_address_in_fe, 2) -> [t_integer(), t_integer()]; -arg_types(hipe_bifs, system_crc, 1) -> - [t_crc32()]; +arg_types(hipe_bifs, system_crc, 0) -> + []; arg_types(hipe_bifs, term_to_word, 1) -> [t_any()]; arg_types(hipe_bifs, update_code_size, 3) -> diff --git a/lib/hipe/vsn.mk b/lib/hipe/vsn.mk index 60b4e0559b..e507ae933f 100644 --- a/lib/hipe/vsn.mk +++ b/lib/hipe/vsn.mk @@ -1 +1 @@ -HIPE_VSN = 3.11.3 +HIPE_VSN = 3.12 diff --git a/lib/inets/doc/src/Makefile b/lib/inets/doc/src/Makefile index 1a8e1c7ca8..961bfa838d 100644 --- a/lib/inets/doc/src/Makefile +++ b/lib/inets/doc/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2012. All Rights Reserved. +# Copyright Ericsson AB 1997-2015. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -52,6 +52,7 @@ XML_REF3_FILES = \ httpc.xml\ httpd.xml \ httpd_conf.xml \ + httpd_custom_api.xml \ httpd_socket.xml \ httpd_util.xml \ mod_alias.xml \ diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml index e40660ab39..435f99ee23 100644 --- a/lib/inets/doc/src/httpd.xml +++ b/lib/inets/doc/src/httpd.xml @@ -204,7 +204,15 @@ <marker id="props_limit"></marker> <p><em>Limit properties</em> </p> - <taglist> + <taglist> + + <marker id="prop_customize"></marker> + <tag>{customize, atom()}</tag> + <item> + <p>A callback module to customize the inets HTTP servers behaviour + see <seealso marker="http_custom_api"> httpd_custom_api</seealso> </p> + </item> + <marker id="prop_disable_chunked_encoding"></marker> <tag>{disable_chunked_transfer_encoding_send, boolean()}</tag> <item> diff --git a/lib/inets/doc/src/httpd_custom_api.xml b/lib/inets/doc/src/httpd_custom_api.xml new file mode 100644 index 0000000000..faf1d277df --- /dev/null +++ b/lib/inets/doc/src/httpd_custom_api.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>2015</year><year>2015</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>httpd_custom_api</title> + <file>httpd_custom_api.xml</file> + </header> + <module>httpd_custom_api</module> + <modulesummary>Behaviour with optional callbacks to customize the inets HTTP server.</modulesummary> + <description> + <p> The module implementing this behaviour shall be supplied to to the servers + configuration with the option <seealso marker="httpd:prop_customize"> customize</seealso></p> + + </description> + <funcs> + <func> + <name>response_header({HeaderName, HeaderValue}) -> {true, Header} | false </name> + <fsummary>Filter and possible alter HTTP response headers.</fsummary> + <type> + <v>Header = {HeaderName :: string(), HeaderValue::string()}</v> + <d>The header name will be in lower case and should not be altered.</d> + </type> + <desc> + <p> Filter and possible alter HTTP response headers before they are sent to the client. + </p> + </desc> + </func> + + <func> + <name>request_header({HeaderName, HeaderValue}) -> {true, Header} | false </name> + <fsummary>Filter and possible alter HTTP request headers.</fsummary> + <type> + <v>Header = {HeaderName :: string(), HeaderValue::string()}</v> + <d>The header name will be in lower case and should not be altered.</d> + </type> + <desc> + <p> Filter and possible alter HTTP request headers before they are processed by the server. + </p> + </desc> + </func> + </funcs> +</erlref> + + diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index bae8e327a3..f563a8c4b0 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -32,7 +32,23 @@ <file>notes.xml</file> </header> - <section><title>Inets 5.10.8</title> + <section><title>Inets 5.10.9</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Add behaviour with optional callbacks to customize the + inets HTTP server.</p> + <p> + Own Id: OTP-12776</p> + </item> + </list> + </section> + +</section> + +<section><title>Inets 5.10.8</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/inets/doc/src/ref_man.xml b/lib/inets/doc/src/ref_man.xml index aaedf330b4..3afb020431 100644 --- a/lib/inets/doc/src/ref_man.xml +++ b/lib/inets/doc/src/ref_man.xml @@ -4,7 +4,7 @@ <application xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>1997</year><year>2013</year> + <year>1997</year><year>2015</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -39,6 +39,7 @@ <xi:include href="httpc.xml"/> <xi:include href="httpd.xml"/> <xi:include href="httpd_conf.xml"/> + <xi:include href="httpd_custom_api.xml"/> <xi:include href="httpd_socket.xml"/> <xi:include href="httpd_util.xml"/> <xi:include href="mod_alias.xml"/> diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index 8f2f11ce8e..f4f0c37570 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -87,7 +87,7 @@ %% block the httpc manager process in odd cases such as trying to call %% a server that does not exist. (See OTP-6735) The only API function %% sending messages to the handler process that can be called before -%% init has compleated is cancel and that is not a problem! (Send and +%% init has completed is cancel and that is not a problem! (Send and %% stream will not be called before the first request has been sent and %% the reply or part of it has arrived.) %%-------------------------------------------------------------------- @@ -392,7 +392,7 @@ handle_call(info, _, State) -> %% When the request in process has been canceled the handler process is %% stopped and the pipelined requests will be reissued or remaining %% requests will be sent on a new connection. This is is -%% based on the assumption that it is proably cheaper to reissue the +%% based on the assumption that it is probably cheaper to reissue the %% requests than to wait for a potentiall large response that we then %% only throw away. This of course is not always true maybe we could %% do something smarter here?! If the request canceled is not @@ -1345,7 +1345,7 @@ handle_empty_queue(Session, ProfileName, TimeOut, State) -> %% closed by the server, the client may want to close it. NewState = activate_queue_timeout(TimeOut, State), update_session(ProfileName, Session, #session.queue_length, 0), - %% Note mfa will be initilized when a new request + %% Note mfa will be initialized when a new request %% arrives. {noreply, NewState#state{request = undefined, diff --git a/lib/inets/src/http_server/Makefile b/lib/inets/src/http_server/Makefile index 51e3dd9212..00bad51ff9 100644 --- a/lib/inets/src/http_server/Makefile +++ b/lib/inets/src/http_server/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2013. All Rights Reserved. +# Copyright Ericsson AB 2005-2015. 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 @@ -46,6 +46,7 @@ MODULES = \ httpd_connection_sup\ httpd_cgi \ httpd_conf \ + httpd_custom \ httpd_example \ httpd_esi \ httpd_file\ diff --git a/lib/inets/src/http_server/httpd_custom.erl b/lib/inets/src/http_server/httpd_custom.erl new file mode 100644 index 0000000000..342469a579 --- /dev/null +++ b/lib/inets/src/http_server/httpd_custom.erl @@ -0,0 +1,69 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015-2015. 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(httpd_custom). + +-export([response_header/1, request_header/1]). +-export([customize_headers/3]). + +-include_lib("inets/src/inets_app/inets_internal.hrl"). + +response_header(Header) -> + {true, httpify(Header)}. +request_header(Header) -> + {true, Header}. + +customize_headers(?MODULE, Function, Arg) -> + ?MODULE:Function(Arg); +customize_headers(Module, Function, Arg) -> + try Module:Function(Arg) of + {true, Value} -> + ?MODULE:Function(Value); + false -> + false + catch + _:_ -> + ?MODULE:Function(Arg) + end. + +httpify({Key0, Value}) -> + %% make sure first letter is capital (defacto standard) + Words1 = string:tokens(Key0, "-"), + Words2 = upify(Words1, []), + Key = new_key(Words2), + Key ++ ": " ++ Value ++ ?CRLF . + +new_key([]) -> + ""; +new_key([W]) -> + W; +new_key([W1,W2]) -> + W1 ++ "-" ++ W2; +new_key([W|R]) -> + W ++ "-" ++ new_key(R). + +upify([], Acc) -> + lists:reverse(Acc); +upify([Key|Rest], Acc) -> + upify(Rest, [upify2(Key)|Acc]). + +upify2([C|Rest]) when (C >= $a) andalso (C =< $z) -> + [C-($a-$A)|Rest]; +upify2(Str) -> + Str. diff --git a/lib/inets/src/http_server/httpd_request.erl b/lib/inets/src/http_server/httpd_request.erl index 3ff07616f9..782120c284 100644 --- a/lib/inets/src/http_server/httpd_request.erl +++ b/lib/inets/src/http_server/httpd_request.erl @@ -42,28 +42,28 @@ %%%========================================================================= %%% Internal application API %%%========================================================================= -parse([Bin, MaxSizes]) -> - ?hdrt("parse", [{bin, Bin}, {max_sizes, MaxSizes}]), - parse_method(Bin, [], 0, proplists:get_value(max_method, MaxSizes), MaxSizes, []); +parse([Bin, Options]) -> + ?hdrt("parse", [{bin, Bin}, {max_sizes, Options}]), + parse_method(Bin, [], 0, proplists:get_value(max_method, Options), Options, []); parse(Unknown) -> ?hdrt("parse", [{unknown, Unknown}]), exit({bad_args, Unknown}). %% Functions that may be returned during the decoding process %% if the input data is incompleate. -parse_method([Bin, Method, Current, Max, MaxSizes, Result]) -> - parse_method(Bin, Method, Current, Max, MaxSizes, Result). +parse_method([Bin, Method, Current, Max, Options, Result]) -> + parse_method(Bin, Method, Current, Max, Options, Result). -parse_uri([Bin, URI, Current, Max, MaxSizes, Result]) -> - parse_uri(Bin, URI, Current, Max, MaxSizes, Result). +parse_uri([Bin, URI, Current, Max, Options, Result]) -> + parse_uri(Bin, URI, Current, Max, Options, Result). -parse_version([Bin, Rest, Version, Current, Max, MaxSizes, Result]) -> - parse_version(<<Rest/binary, Bin/binary>>, Version, Current, Max, MaxSizes, +parse_version([Bin, Rest, Version, Current, Max, Options, Result]) -> + parse_version(<<Rest/binary, Bin/binary>>, Version, Current, Max, Options, Result). -parse_headers([Bin, Rest, Header, Headers, Current, Max, MaxSizes, Result]) -> +parse_headers([Bin, Rest, Header, Headers, Current, Max, Options, Result]) -> parse_headers(<<Rest/binary, Bin/binary>>, - Header, Headers, Current, Max, MaxSizes, Result). + Header, Headers, Current, Max, Options, Result). whole_body([Bin, Body, Length]) -> whole_body(<<Body/binary, Bin/binary>>, Length). @@ -134,13 +134,13 @@ update_mod_data(ModData, Method, RequestURI, HTTPVersion, Headers)-> %%%======================================================================== %%% Internal functions %%%======================================================================== -parse_method(<<>>, Method, Current, Max, MaxSizes, Result) -> - {?MODULE, parse_method, [Method, Current, Max, MaxSizes, Result]}; -parse_method(<<?SP, Rest/binary>>, Method, _Current, _Max, MaxSizes, Result) -> - parse_uri(Rest, [], 0, proplists:get_value(max_uri, MaxSizes), MaxSizes, +parse_method(<<>>, Method, Current, Max, Options, Result) -> + {?MODULE, parse_method, [Method, Current, Max, Options, Result]}; +parse_method(<<?SP, Rest/binary>>, Method, _Current, _Max, Options, Result) -> + parse_uri(Rest, [], 0, proplists:get_value(max_uri, Options), Options, [string:strip(lists:reverse(Method)) | Result]); -parse_method(<<Octet, Rest/binary>>, Method, Current, Max, MaxSizes, Result) when Current =< Max -> - parse_method(Rest, [Octet | Method], Current + 1, Max, MaxSizes, Result); +parse_method(<<Octet, Rest/binary>>, Method, Current, Max, Options, Result) when Current =< Max -> + parse_method(Rest, [Octet | Method], Current + 1, Max, Options, Result); parse_method(_, _, _, Max, _, _) -> %% We do not know the version of the client as it comes after the %% method send the lowest version in the response so that the client @@ -153,30 +153,30 @@ parse_uri(_, _, Current, MaxURI, _, _) %% uri send the lowest version in the response so that the client %% will be able to handle it. {error, {size_error, MaxURI, 414, "URI unreasonably long"},lowest_version()}; -parse_uri(<<>>, URI, Current, Max, MaxSizes, Result) -> - {?MODULE, parse_uri, [URI, Current, Max, MaxSizes, Result]}; -parse_uri(<<?SP, Rest/binary>>, URI, _, _, MaxSizes, Result) -> - parse_version(Rest, [], 0, proplists:get_value(max_version, MaxSizes), MaxSizes, +parse_uri(<<>>, URI, Current, Max, Options, Result) -> + {?MODULE, parse_uri, [URI, Current, Max, Options, Result]}; +parse_uri(<<?SP, Rest/binary>>, URI, _, _, Options, Result) -> + parse_version(Rest, [], 0, proplists:get_value(max_version, Options), Options, [string:strip(lists:reverse(URI)) | Result]); %% Can happen if it is a simple HTTP/0.9 request e.i "GET /\r\n\r\n" -parse_uri(<<?CR, _Rest/binary>> = Data, URI, _, _, MaxSizes, Result) -> - parse_version(Data, [], 0, proplists:get_value(max_version, MaxSizes), MaxSizes, +parse_uri(<<?CR, _Rest/binary>> = Data, URI, _, _, Options, Result) -> + parse_version(Data, [], 0, proplists:get_value(max_version, Options), Options, [string:strip(lists:reverse(URI)) | Result]); -parse_uri(<<Octet, Rest/binary>>, URI, Current, Max, MaxSizes, Result) -> - parse_uri(Rest, [Octet | URI], Current + 1, Max, MaxSizes, Result). +parse_uri(<<Octet, Rest/binary>>, URI, Current, Max, Options, Result) -> + parse_uri(Rest, [Octet | URI], Current + 1, Max, Options, Result). -parse_version(<<>>, Version, Current, Max, MaxSizes, Result) -> - {?MODULE, parse_version, [<<>>, Version, Current, Max, MaxSizes, Result]}; -parse_version(<<?LF, Rest/binary>>, Version, Current, Max, MaxSizes, Result) -> +parse_version(<<>>, Version, Current, Max, Options, Result) -> + {?MODULE, parse_version, [<<>>, Version, Current, Max, Options, Result]}; +parse_version(<<?LF, Rest/binary>>, Version, Current, Max, Options, Result) -> %% If ?CR is is missing RFC2616 section-19.3 - parse_version(<<?CR, ?LF, Rest/binary>>, Version, Current, Max, MaxSizes, Result); -parse_version(<<?CR, ?LF, Rest/binary>>, Version, _, _, MaxSizes, Result) -> - parse_headers(Rest, [], [], 0, proplists:get_value(max_header, MaxSizes), MaxSizes, + parse_version(<<?CR, ?LF, Rest/binary>>, Version, Current, Max, Options, Result); +parse_version(<<?CR, ?LF, Rest/binary>>, Version, _, _, Options, Result) -> + parse_headers(Rest, [], [], 0, proplists:get_value(max_header, Options), Options, [string:strip(lists:reverse(Version)) | Result]); -parse_version(<<?CR>> = Data, Version, Current, Max, MaxSizes, Result) -> - {?MODULE, parse_version, [Data, Version, Current, Max, MaxSizes, Result]}; -parse_version(<<Octet, Rest/binary>>, Version, Current, Max, MaxSizes, Result) when Current =< Max -> - parse_version(Rest, [Octet | Version], Current + 1, Max, MaxSizes, Result); +parse_version(<<?CR>> = Data, Version, Current, Max, Options, Result) -> + {?MODULE, parse_version, [Data, Version, Current, Max, Options, Result]}; +parse_version(<<Octet, Rest/binary>>, Version, Current, Max, Options, Result) when Current =< Max -> + parse_version(Rest, [Octet | Version], Current + 1, Max, Options, Result); parse_version(_, _, _, Max,_,_) -> {error, {size_error, Max, 413, "Version string unreasonably long"}, lowest_version()}. @@ -185,34 +185,42 @@ parse_headers(_, _, _, Current, Max, _, Result) HttpVersion = lists:nth(3, lists:reverse(Result)), {error, {size_error, Max, 413, "Headers unreasonably long"}, HttpVersion}; -parse_headers(<<>>, Header, Headers, Current, Max, MaxSizes, Result) -> +parse_headers(<<>>, Header, Headers, Current, Max, Options, Result) -> {?MODULE, parse_headers, [<<>>, Header, Headers, Current, Max, - MaxSizes, Result]}; -parse_headers(<<?CR,?LF,?LF,Body/binary>>, [], [], Current, Max, MaxSizes, Result) -> + Options, Result]}; +parse_headers(<<?CR,?LF,?LF,Body/binary>>, [], [], Current, Max, Options, Result) -> %% If ?CR is is missing RFC2616 section-19.3 parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], Current, Max, - MaxSizes, Result); + Options, Result); -parse_headers(<<?LF,?LF,Body/binary>>, [], [], Current, Max, MaxSizes, Result) -> +parse_headers(<<?LF,?LF,Body/binary>>, [], [], Current, Max, Options, Result) -> %% If ?CR is is missing RFC2616 section-19.3 parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], Current, Max, - MaxSizes, Result); + Options, Result); parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], _, _, _, Result) -> NewResult = list_to_tuple(lists:reverse([Body, {#http_request_h{}, []} | Result])), {ok, NewResult}; parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, _, _, - MaxSizes, Result) -> + Options, Result) -> + Customize = proplists:get_value(customize, Options), case http_request:key_value(lists:reverse(Header)) of undefined -> %% Skip headers with missing : - {ok, list_to_tuple(lists:reverse([Body, {http_request:headers(Headers, #http_request_h{}), Headers} | Result]))}; + FinalHeaders = lists:filtermap(fun(H) -> + httpd_custom:customize_headers(Customize, request_header, H) + end, + Headers), + {ok, list_to_tuple(lists:reverse([Body, {http_request:headers(FinalHeaders, #http_request_h{}), FinalHeaders} | Result]))}; NewHeader -> - case check_header(NewHeader, MaxSizes) of + case check_header(NewHeader, Options) of ok -> - {ok, list_to_tuple(lists:reverse([Body, {http_request:headers([NewHeader | Headers], + FinalHeaders = lists:filtermap(fun(H) -> + httpd_custom:customize_headers(Customize, request_header, H) + end, [NewHeader | Headers]), + {ok, list_to_tuple(lists:reverse([Body, {http_request:headers(FinalHeaders, #http_request_h{}), - [NewHeader | Headers]} | Result]))}; + FinalHeaders} | Result]))}; {error, Reason} -> HttpVersion = lists:nth(3, lists:reverse(Result)), @@ -221,12 +229,12 @@ parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, _, _, end; parse_headers(<<?CR,?LF,?CR>> = Data, Header, Headers, Current, Max, - MaxSizes, Result) -> + Options, Result) -> {?MODULE, parse_headers, [Data, Header, Headers, Current, Max, - MaxSizes, Result]}; -parse_headers(<<?LF>>, [], [], Current, Max, MaxSizes, Result) -> + Options, Result]}; +parse_headers(<<?LF>>, [], [], Current, Max, Options, Result) -> %% If ?CR is is missing RFC2616 section-19.3 - parse_headers(<<?CR,?LF>>, [], [], Current, Max, MaxSizes, Result); + parse_headers(<<?CR,?LF>>, [], [], Current, Max, Options, Result); %% There where no headers, which is unlikely to happen. parse_headers(<<?CR,?LF>>, [], [], _, _, _, Result) -> @@ -235,30 +243,30 @@ parse_headers(<<?CR,?LF>>, [], [], _, _, _, Result) -> {ok, NewResult}; parse_headers(<<?LF>>, Header, Headers, Current, Max, - MaxSizes, Result) -> + Options, Result) -> %% If ?CR is is missing RFC2616 section-19.3 - parse_headers(<<?CR,?LF>>, Header, Headers, Current, Max, MaxSizes, Result); + parse_headers(<<?CR,?LF>>, Header, Headers, Current, Max, Options, Result); parse_headers(<<?CR,?LF>> = Data, Header, Headers, Current, Max, - MaxSizes, Result) -> + Options, Result) -> {?MODULE, parse_headers, [Data, Header, Headers, Current, Max, - MaxSizes, Result]}; + Options, Result]}; parse_headers(<<?LF, Octet, Rest/binary>>, Header, Headers, Current, Max, - MaxSizes, Result) -> + Options, Result) -> %% If ?CR is is missing RFC2616 section-19.3 parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, Current, Max, - MaxSizes, Result); + Options, Result); parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, _, Max, - MaxSizes, Result) -> + Options, Result) -> case http_request:key_value(lists:reverse(Header)) of undefined -> %% Skip headers with missing : parse_headers(Rest, [Octet], Headers, - 0, Max, MaxSizes, Result); + 0, Max, Options, Result); NewHeader -> - case check_header(NewHeader, MaxSizes) of + case check_header(NewHeader, Options) of ok -> parse_headers(Rest, [Octet], [NewHeader | Headers], - 0, Max, MaxSizes, Result); + 0, Max, Options, Result); {error, Reason} -> HttpVersion = lists:nth(3, lists:reverse(Result)), {error, Reason, HttpVersion} @@ -266,19 +274,19 @@ parse_headers(<<?CR,?LF, Octet, Rest/binary>>, Header, Headers, _, Max, end; parse_headers(<<?CR>> = Data, Header, Headers, Current, Max, - MaxSizes, Result) -> + Options, Result) -> {?MODULE, parse_headers, [Data, Header, Headers, Current, Max, - MaxSizes, Result]}; + Options, Result]}; parse_headers(<<?LF>>, Header, Headers, Current, Max, - MaxSizes, Result) -> + Options, Result) -> %% If ?CR is is missing RFC2616 section-19.3 parse_headers(<<?CR, ?LF>>, Header, Headers, Current, Max, - MaxSizes, Result); + Options, Result); parse_headers(<<Octet, Rest/binary>>, Header, Headers, Current, - Max, MaxSizes, Result) -> + Max, Options, Result) -> parse_headers(Rest, [Octet | Header], Headers, Current + 1, Max, - MaxSizes, Result). + Options, Result). whole_body(Body, Length) -> case size(Body) of diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index f7a9fe5d49..9947e17b47 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -121,13 +121,15 @@ continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) -> MaxURISize = max_uri_size(ConfigDB), NrOfRequest = max_keep_alive_request(ConfigDB), MaxContentLen = max_content_length(ConfigDB), + Customize = customize(ConfigDB), {_, Status} = httpd_manager:new_connection(Manager), MFA = {httpd_request, parse, [[{max_uri, MaxURISize}, {max_header, MaxHeaderSize}, {max_version, ?HTTP_MAX_VERSION_STRING}, {max_method, ?HTTP_MAX_METHOD_STRING}, - {max_content_length, MaxContentLen} + {max_content_length, MaxContentLen}, + {customize, Customize} ]]}, State = #state{mod = Mod, @@ -550,11 +552,13 @@ handle_next_request(#state{mod = #mod{connection = true} = ModData, MaxHeaderSize = max_header_size(ModData#mod.config_db), MaxURISize = max_uri_size(ModData#mod.config_db), MaxContentLen = max_content_length(ModData#mod.config_db), + Customize = customize(ModData#mod.config_db), MFA = {httpd_request, parse, [[{max_uri, MaxURISize}, {max_header, MaxHeaderSize}, {max_version, ?HTTP_MAX_VERSION_STRING}, {max_method, ?HTTP_MAX_METHOD_STRING}, - {max_content_length, MaxContentLen} + {max_content_length, MaxContentLen}, + {customize, Customize} ]]}, TmpState = State#state{mod = NewModData, mfa = MFA, @@ -640,3 +644,6 @@ max_keep_alive_request(ConfigDB) -> max_content_length(ConfigDB) -> httpd_util:lookup(ConfigDB, max_content_length, ?HTTP_MAX_CONTENT_LENGTH). + +customize(ConfigDB) -> + httpd_util:lookup(ConfigDB, customize, httpd_custom). diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl index 2fa91d47a0..71dc05e46d 100644 --- a/lib/inets/src/http_server/httpd_response.erl +++ b/lib/inets/src/http_server/httpd_response.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2013. All Rights Reserved. +%% Copyright Ericsson AB 1997-2015. 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 @@ -176,7 +176,7 @@ send_header(#mod{socket_type = Type, StatusLine = [NewVer, " ", io_lib:write(NewStatusCode), " ", httpd_util:reason_phrase(NewStatusCode), ?CRLF], ConnectionHeader = get_connection(Conn, NewVer), - Head = list_to_binary([StatusLine, Headers, ConnectionHeader , ?CRLF]), + Head = [StatusLine, Headers, ConnectionHeader , ?CRLF], httpd_socket:deliver(Type, Sock, Head). map_status_code("HTTP/1.0", Code) @@ -286,45 +286,21 @@ create_header(ConfigDb, KeyValueTupleHeaders) -> Date = httpd_util:rfc1123_date(), ContentType = "text/html", Server = server(ConfigDb), - NewHeaders = add_default_headers([{"date", Date}, - {"content-type", ContentType} - | if Server=="" -> []; - true -> [{"server", Server}] - end - ], - KeyValueTupleHeaders), - lists:map(fun fix_header/1, NewHeaders). - - + Headers0 = add_default_headers([{"date", Date}, + {"content-type", ContentType} + | if Server=="" -> []; + true -> [{"server", Server}] + end + ], + KeyValueTupleHeaders), + CustomizeCB = httpd_util:lookup(ConfigDb, customize, httpd_custom), + lists:filtermap(fun(H) -> + httpd_custom:customize_headers(CustomizeCB, response_header, H) + end, + [Header || Header <- Headers0]). server(ConfigDb) -> httpd_util:lookup(ConfigDb, server, ?SERVER_SOFTWARE). -fix_header({Key0, Value}) -> - %% make sure first letter is capital - Words1 = string:tokens(Key0, "-"), - Words2 = upify(Words1, []), - Key = new_key(Words2), - Key ++ ": " ++ Value ++ ?CRLF . - -new_key([]) -> - ""; -new_key([W]) -> - W; -new_key([W1,W2]) -> - W1 ++ "-" ++ W2; -new_key([W|R]) -> - W ++ "-" ++ new_key(R). - -upify([], Acc) -> - lists:reverse(Acc); -upify([Key|Rest], Acc) -> - upify(Rest, [upify2(Key)|Acc]). - -upify2([C|Rest]) when (C >= $a) andalso (C =< $z) -> - [C-($a-$A)|Rest]; -upify2(Str) -> - Str. - add_default_headers([], Headers) -> Headers; diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src index b7c3e341e8..6ba9795d9e 100644 --- a/lib/inets/src/inets_app/inets.app.src +++ b/lib/inets/src/inets_app/inets.app.src @@ -63,6 +63,7 @@ httpd_cgi, httpd_connection_sup, httpd_conf, + httpd_custom, httpd_esi, httpd_example, httpd_file, diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 0dfc65e8f7..ab7ffadf75 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -1289,7 +1289,9 @@ dummy_server_init(Caller, ip_comm, Inet, _) -> {max_header, ?HTTP_MAX_HEADER_SIZE}, {max_version,?HTTP_MAX_VERSION_STRING}, {max_method, ?HTTP_MAX_METHOD_STRING}, - {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}]]}, + {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}, + {customize, httpd_custom} + ]]}, [], ListenSocket); dummy_server_init(Caller, ssl, Inet, SSLOptions) -> @@ -1305,7 +1307,8 @@ dummy_ssl_server_init(Caller, BaseOpts, Inet) -> {max_method, ?HTTP_MAX_METHOD_STRING}, {max_version,?HTTP_MAX_VERSION_STRING}, {max_method, ?HTTP_MAX_METHOD_STRING}, - {max_content_length, ?HTTP_MAX_CONTENT_LENGTH} + {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}, + {customize, httpd_custom} ]]}, [], ListenSocket). @@ -1384,18 +1387,20 @@ handle_request(Module, Function, Args, Socket) -> stop; <<>> -> {httpd_request, parse, [[{max_uri,?HTTP_MAX_URI_SIZE}, - {max_header, ?HTTP_MAX_HEADER_SIZE}, - {max_version,?HTTP_MAX_VERSION_STRING}, - {max_method, ?HTTP_MAX_METHOD_STRING}, - {max_content_length, ?HTTP_MAX_CONTENT_LENGTH} - ]]}; + {max_header, ?HTTP_MAX_HEADER_SIZE}, + {max_version,?HTTP_MAX_VERSION_STRING}, + {max_method, ?HTTP_MAX_METHOD_STRING}, + {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}, + {customize, httpd_custom} + ]]}; Data -> handle_request(httpd_request, parse, [Data, [{max_uri, ?HTTP_MAX_URI_SIZE}, - {max_header, ?HTTP_MAX_HEADER_SIZE}, + {max_header, ?HTTP_MAX_HEADER_SIZE}, {max_version,?HTTP_MAX_VERSION_STRING}, {max_method, ?HTTP_MAX_METHOD_STRING}, - {max_content_length, ?HTTP_MAX_CONTENT_LENGTH} + {max_content_length, ?HTTP_MAX_CONTENT_LENGTH}, + {customize, httpd_custom} ]], Socket) end; NewMFA -> diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index 7670c2cc60..bc6b0d5c79 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -53,6 +53,8 @@ all() -> {group, https_basic}, {group, http_limit}, {group, https_limit}, + {group, http_custom}, + {group, https_custom}, {group, http_basic_auth}, {group, https_basic_auth}, {group, http_auth_api}, @@ -76,6 +78,8 @@ groups() -> {https_basic, [], basic_groups()}, {http_limit, [], [{group, limit}]}, {https_limit, [], [{group, limit}]}, + {http_custom, [], [{group, custom}]}, + {https_custom, [], [{group, custom}]}, {http_basic_auth, [], [{group, basic_auth}]}, {https_basic_auth, [], [{group, basic_auth}]}, {http_auth_api, [], [{group, auth_api}]}, @@ -92,6 +96,7 @@ groups() -> {https_reload, [], [{group, reload}]}, {http_mime_types, [], [alias_1_1, alias_1_0, alias_0_9]}, {limit, [], [max_clients_1_1, max_clients_1_0, max_clients_0_9]}, + {custom, [], [customize]}, {reload, [], [non_disturbing_reconfiger_dies, disturbing_reconfiger_dies, non_disturbing_1_1, @@ -178,6 +183,7 @@ end_per_suite(_Config) -> %%-------------------------------------------------------------------- init_per_group(Group, Config0) when Group == https_basic; Group == https_limit; + Group == https_custom; Group == https_basic_auth; Group == https_auth_api; Group == https_auth_api_dets; @@ -188,6 +194,7 @@ init_per_group(Group, Config0) when Group == https_basic; init_ssl(Group, Config0); init_per_group(Group, Config0) when Group == http_basic; Group == http_limit; + Group == http_custom; Group == http_basic_auth; Group == http_auth_api; Group == http_auth_api_dets; @@ -977,6 +984,30 @@ missing_CR(Config) -> {version, Version}]). %%------------------------------------------------------------------------- +customize() -> + [{doc, "Test filtering of headers with custom callback"}]. + +customize(Config) when is_list(Config) -> + Version = "HTTP/1.1", + Host = ?config(host, Config), + Type = ?config(type, Config), + ok = httpd_test_lib:verify_request(?config(type, Config), Host, + ?config(port, Config), + transport_opts(Type, Config), + ?config(node, Config), + http_request("GET /index.html ", Version, Host), + [{statuscode, 200}, + {header, "Content-Type", "text/html"}, + {header, "Date"}, + {no_header, "Server"}, + {version, Version}]). + +response_header({"server", _}) -> + false; +response_header(Header) -> + {true, Header}. + +%%------------------------------------------------------------------------- max_header() -> ["Denial Of Service (DOS) attack, prevented by max_header"]. max_header(Config) when is_list(Config) -> @@ -1320,17 +1351,19 @@ setup_server_dirs(ServerRoot, DocRoot, DataDir) -> start_apps(Group) when Group == https_basic; Group == https_limit; + Group == https_custom; Group == https_basic_auth; Group == https_auth_api; Group == https_auth_api_dets; Group == https_auth_api_mnesia; - Group == http_htaccess; - Group == http_security; - Group == http_reload + Group == https_htaccess; + Group == https_security; + Group == https_reload -> inets_test_lib:start_apps([inets, asn1, crypto, public_key, ssl]); start_apps(Group) when Group == http_basic; Group == http_limit; + Group == http_custom; Group == http_basic_auth; Group == http_auth_api; Group == http_auth_api_dets; @@ -1390,6 +1423,10 @@ server_config(http_limit, Config) -> [{max_clients, 1}, %% Make sure option checking code is run {max_content_length, 100000002}] ++ server_config(http, Config); +server_config(http_custom, Config) -> + [{custom, ?MODULE}] ++ server_config(http, Config); +server_config(https_custom, Config) -> + [{custom, ?MODULE}] ++ server_config(https, Config); server_config(https_limit, Config) -> [{max_clients, 1}] ++ server_config(https, Config); server_config(http_basic_auth, Config) -> diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index ecb84e447c..f52347e39e 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2014. All Rights Reserved. +# Copyright Ericsson AB 2001-2015. 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,6 +18,6 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.10.8 +INETS_VSN = 6.0 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml index df2f0b01ee..f49d63b5a6 100644 --- a/lib/kernel/doc/src/error_logger.xml +++ b/lib/kernel/doc/src/error_logger.xml @@ -58,12 +58,11 @@ specific events. (<c>add_report_handler/1,2</c>). Also, there is a useful event handler in STDLIB for multi-file logging of events, see <c>log_mf_h(3)</c>.</p> - <p>Warning events were introduced in Erlang/OTP R9C. To retain - backwards compatibility, these are by default tagged as errors, - thus showing up as error reports in the logs. By using - the command line flag <c><![CDATA[+W <w | i>]]></c>, they can instead - be tagged as warnings or info. Tagging them as warnings may - require rewriting existing user defined event handlers.</p> + <p>Warning events were introduced in Erlang/OTP R9C and are enabled + by default as of 18.0. To retain backwards compatibility with existing + user defined event handlers, these may be tagged as errors or info + using the command line flag <c><![CDATA[+W <e | i | w>]]></c>, thus + showing up as error or info reports in the logs.</p> </description> <datatypes> <datatype> @@ -132,7 +131,7 @@ ok</pre> <desc> <p>Returns the current mapping for warning events. Events sent using <c>warning_msg/1,2</c> or <c>warning_report/1,2</c> - are tagged as errors (default), warnings or info, depending + are tagged as errors, warnings (default) or info, depending on the value of the command line flag <c>+W</c>.</p> <pre> os$ <input>erl</input> @@ -140,25 +139,25 @@ Erlang (BEAM) emulator version 5.4.8 [hipe] [threads:0] [kernel-poll] Eshell V5.4.8 (abort with ^G) 1> <input>error_logger:warning_map().</input> -error -2> <input>error_logger:warning_msg("Warnings tagged as: ~p~n", [error]).</input> +warning +2> <input>error_logger:warning_msg("Warnings tagged as: ~p~n", [warning]).</input> -=ERROR REPORT==== 11-Aug-2005::15:31:23 === -Warnings tagged as: error +=WARNING REPORT==== 11-Aug-2005::15:31:55 === +Warnings tagged as: warning ok 3> User switch command --> q -os$ <input>erl +W w</input> +os$ <input>erl +W e</input> Erlang (BEAM) emulator version 5.4.8 [hipe] [threads:0] [kernel-poll] Eshell V5.4.8 (abort with ^G) 1> <input>error_logger:warning_map().</input> -warning -2> <input>error_logger:warning_msg("Warnings tagged as: ~p~n", [warning]).</input> +error +2> <input>error_logger:warning_msg("Warnings tagged as: ~p~n", [error]).</input> -=WARNING REPORT==== 11-Aug-2005::15:31:55 === -Warnings tagged as: warning +=ERROR REPORT==== 11-Aug-2005::15:31:23 === +Warnings tagged as: error ok</pre> </desc> </func> diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index 820ecd1e30..71ef5cd48f 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -347,11 +347,22 @@ do_recv(Sock, Bs) -> </func> <func> <name name="shutdown" arity="2"/> - <fsummary>Immediately close a socket</fsummary> + <fsummary>Asynchronously close a socket</fsummary> <desc> - <p>Immediately close a socket in one or two directions.</p> + <p>Close a socket in one or two directions.</p> <p><c><anno>How</anno> == write</c> means closing the socket for writing, reading from it is still possible.</p> + <p>If <c><anno>How</anno> == read</c>, or there is no outgoing + data buffered in the <c><anno>Socket</anno></c> port, + then the socket is shutdown immediately and any error encountered + is returned in <c><anno>Reason</anno></c>.</p> + <p>If there is data buffered in the socket port, then the attempt + to shutdown the socket is postponed until that data is written to the + kernel socket send buffer. Any errors encountered will result + in the socket being closed and <c>{error, closed}</c> being returned + on the next + <seealso marker="gen_tcp#recv/2">recv/2</seealso> or + <seealso marker="gen_tcp#send/2">send/2</seealso>.</p> <p>To be able to handle that the peer has done a shutdown on the write side, the <c>{exit_on_close, false}</c> option is useful.</p> diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index ec2c350931..d668738109 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -1527,26 +1527,28 @@ tcp_controlling_process(S, NewOwner) when is_port(S), is_pid(NewOwner) -> _ -> case prim_inet:getopt(S, active) of {ok, A0} -> - case A0 of - false -> ok; - _ -> ok = prim_inet:setopt(S, active, false) - end, - case tcp_sync_input(S, NewOwner, false) of - true -> %% socket already closed, + SetOptRes = + case A0 of + false -> ok; + _ -> prim_inet:setopt(S, active, false) + end, + case {tcp_sync_input(S, NewOwner, false), SetOptRes} of + {true, _} -> %% socket already closed ok; - false -> + {false, ok} -> try erlang:port_connect(S, NewOwner) of true -> unlink(S), %% unlink from port case A0 of false -> ok; - _ -> ok = prim_inet:setopt(S, active, A0) - end, - ok + _ -> prim_inet:setopt(S, active, A0) + end catch error:Reason -> {error, Reason} - end + end; + {false, Error} -> + Error end; Error -> Error diff --git a/lib/kernel/src/inet_parse.erl b/lib/kernel/src/inet_parse.erl index a88c94a453..a694642b19 100644 --- a/lib/kernel/src/inet_parse.erl +++ b/lib/kernel/src/inet_parse.erl @@ -675,28 +675,22 @@ ipv6_addr_done(Ar, Br, N) -> ipv6_addr_done(Ar) -> list_to_tuple(lists:reverse(Ar)). -%% Collect Hex digits -hex(Cs) -> hex(Cs, []). -%% -hex([C|Cs], R) when C >= $0, C =< $9 -> - hex(Cs, [C|R]); -hex([C|Cs], R) when C >= $a, C =< $f -> - hex(Cs, [C|R]); -hex([C|Cs], R) when C >= $A, C =< $F -> - hex(Cs, [C|R]); -hex(Cs, [_|_]=R) when is_list(Cs) -> +%% Collect 1-4 Hex digits +hex(Cs) -> hex(Cs, [], 4). +%% +hex([C|Cs], R, N) when C >= $0, C =< $9, N > 0 -> + hex(Cs, [C|R], N-1); +hex([C|Cs], R, N) when C >= $a, C =< $f, N > 0 -> + hex(Cs, [C|R], N-1); +hex([C|Cs], R, N) when C >= $A, C =< $F, N > 0 -> + hex(Cs, [C|R], N-1); +hex(Cs, [_|_]=R, _) when is_list(Cs) -> {lists:reverse(R),Cs}; -hex(_, _) -> +hex(_, _, _) -> erlang:error(badarg). %% Hex string to integer -hex_to_int(Cs0) -> - case strip0(Cs0) of - Cs when length(Cs) =< 4 -> - erlang:list_to_integer("0"++Cs, 16); - _ -> - erlang:error(badarg) - end. +hex_to_int(Cs) -> erlang:list_to_integer(Cs, 16). %% Dup onto head of existing list dup(0, _, L) -> diff --git a/lib/kernel/src/inet_sctp.erl b/lib/kernel/src/inet_sctp.erl index 93528d305d..f0f13c8d4a 100644 --- a/lib/kernel/src/inet_sctp.erl +++ b/lib/kernel/src/inet_sctp.erl @@ -133,15 +133,18 @@ connect_get_assoc(S, Addr, Port, Active, Timer) -> Timeout = inet:timeout(Timer), receive {sctp,S,Addr,Port,{_,#sctp_assoc_change{state=St}=Ev}} -> - case Active of - once -> - ok = prim_inet:setopt(S, active, once); - _ -> ok - end, - if St =:= comm_up -> + SetOptRes = + case Active of + once -> prim_inet:setopt(S, active, once); + _ -> ok + end, + case {St, SetOptRes} of + {comm_up, ok} -> {ok,Ev}; - true -> - {error,Ev} + {_, ok} -> + {error,Ev}; + {_, Error} -> + Error end after Timeout -> {error,timeout} diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index 1bae762bed..5d3836bad7 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -1,7 +1,7 @@ %% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2014. All Rights Reserved. +%% Copyright Ericsson AB 1999-2015. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -17,7 +17,7 @@ %% %CopyrightEnd% {"%VSN%", %% Up from - max one major revision back - [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-17 + [{<<"3\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-17 %% Down to - max one major revision back - [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-17 + [{<<"3\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-17 }. diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 549c65d034..c82aaf0582 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -1654,9 +1654,7 @@ get_mode(Config) when is_list(Config) -> init(Tester) -> {ok, Tester}. -handle_event({error, _GL, {emulator, _, _}}, Tester) -> - {ok, Tester}; -handle_event({error, _GL, Msg}, Tester) -> +handle_event({warning_msg, _GL, Msg}, Tester) -> Tester ! Msg, {ok, Tester}; handle_event(_Event, State) -> diff --git a/lib/kernel/test/error_logger_warn_SUITE.erl b/lib/kernel/test/error_logger_warn_SUITE.erl index 2bf467610e..fb576d77a3 100644 --- a/lib/kernel/test/error_logger_warn_SUITE.erl +++ b/lib/kernel/test/error_logger_warn_SUITE.erl @@ -21,8 +21,8 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2, - basic/1,warnings_info/1,warnings_warnings/1, - rb_basic/1,rb_warnings_info/1,rb_warnings_warnings/1, + basic/1,warnings_info/1,warnings_errors/1, + rb_basic/1,rb_warnings_info/1,rb_warnings_errors/1, rb_trunc/1,rb_utc/1,file_utc/1]). %% Internal exports. @@ -48,8 +48,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [basic, warnings_info, warnings_warnings, rb_basic, - rb_warnings_info, rb_warnings_warnings, rb_trunc, + [basic, warnings_info, warnings_errors, rb_basic, + rb_warnings_info, rb_warnings_errors, rb_trunc, rb_utc, file_utc]. groups() -> @@ -88,11 +88,11 @@ warnings_info(Config) when is_list(Config) -> put(elw_config,Config), warnings_info(). -warnings_warnings(doc) -> - ["Tests mapping warnings to warnings functionality"]; -warnings_warnings(Config) when is_list(Config) -> +warnings_errors(doc) -> + ["Tests mapping warnings to errors functionality"]; +warnings_errors(Config) when is_list(Config) -> put(elw_config,Config), - warnings_warnings(). + warnings_errors(). rb_basic(doc) -> ["Tests basic rb functionality"]; @@ -106,11 +106,11 @@ rb_warnings_info(Config) when is_list(Config) -> put(elw_config,Config), rb_warnings_info(). -rb_warnings_warnings(doc) -> - ["Tests warnings as warnings rb functionality"]; -rb_warnings_warnings(Config) when is_list(Config) -> +rb_warnings_errors(doc) -> + ["Tests warnings as errors rb functionality"]; +rb_warnings_errors(Config) when is_list(Config) -> put(elw_config,Config), - rb_warnings_warnings(). + rb_warnings_errors(). rb_trunc(doc) -> ["Tests rb functionality on truncated data"]; @@ -159,6 +159,9 @@ install_relay(Node) -> rpc:call(Node,error_logger,add_report_handler,[?MODULE,[self()]]). +warning_map(Node) -> + rpc:call(Node,error_logger,warning_map,[]). + format(Node,A,B) -> rpc:call(Node,error_logger,format,[A,B]). error_msg(Node,A,B) -> @@ -185,19 +188,20 @@ basic() -> ?line ok = install_relay(Node), ?line Self = self(), ?line GL = group_leader(), + ?line warning = warning_map(Node), ?line format(Node,"~p~n",[Self]), ?line ?EXPECT({handle_event,{error,GL,{_,"~p~n",[Self]}}}), ?line error_msg(Node,"~p~n",[Self]), ?line ?EXPECT({handle_event,{error,GL,{_,"~p~n",[Self]}}}), ?line warning_msg(Node,"~p~n",[Self]), - ?line ?EXPECT({handle_event,{error,GL,{_,"~p~n",[Self]}}}), + ?line ?EXPECT({handle_event,{warning_msg,GL,{_,"~p~n",[Self]}}}), ?line info_msg(Node,"~p~n",[Self]), ?line ?EXPECT({handle_event,{info_msg,GL,{_,"~p~n",[Self]}}}), ?line Report = [{self,Self},{gl,GL},make_ref()], ?line error_report(Node,Report), ?line ?EXPECT({handle_event,{error_report,GL,{_,std_error,Report}}}), ?line warning_report(Node,Report), - ?line ?EXPECT({handle_event,{error_report,GL,{_,std_error,Report}}}), + ?line ?EXPECT({handle_event,{warning_report,GL,{_,std_warning,Report}}}), ?line info_report(Node,Report), ?line ?EXPECT({handle_event,{info_report,GL,{_,std_info,Report}}}), @@ -209,6 +213,7 @@ warnings_info() -> ?line ok = install_relay(Node), ?line Self = self(), ?line GL = group_leader(), + ?line info = warning_map(Node), ?line Report = [{self,Self},{gl,GL},make_ref()], ?line warning_msg(Node,"~p~n",[Self]), ?line ?EXPECT({handle_event,{info_msg,GL,{_,"~p~n",[Self]}}}), @@ -217,16 +222,17 @@ warnings_info() -> ?line stop_node(Node), ok. -warnings_warnings() -> - ?line Node = start_node(nn(),"+Ww"), +warnings_errors() -> + ?line Node = start_node(nn(),"+We"), ?line ok = install_relay(Node), ?line Self = self(), ?line GL = group_leader(), + ?line error = warning_map(Node), ?line Report = [{self,Self},{gl,GL},make_ref()], ?line warning_msg(Node,"~p~n",[Self]), - ?line ?EXPECT({handle_event,{warning_msg,GL,{_,"~p~n",[Self]}}}), + ?line ?EXPECT({handle_event,{error,GL,{_,"~p~n",[Self]}}}), ?line warning_report(Node,Report), - ?line ?EXPECT({handle_event,{warning_report,GL,{_,std_warning,Report}}}), + ?line ?EXPECT({handle_event,{error_report,GL,{_,std_error,Report}}}), ?line stop_node(Node), ok. @@ -356,6 +362,7 @@ rb_basic() -> "error_logger_mf_maxfiles 5"), ?line Self = self(), ?line GL = group_leader(), + ?line warning = warning_map(Node), ?line Report = [{self,Self},{gl,GL},make_ref()], ?line fake_gl(Node,warning_msg,"~p~n",[Self]), ?line fake_gl(Node,warning_report,Report), @@ -363,10 +370,14 @@ rb_basic() -> ?line application:start(sasl), ?line rb:start([{report_dir, rd()}]), ?line rb:list(), - ?line true = (one_rb_lines([error]) > 1), - ?line true = (one_rb_lines([error_report]) > 1), - ?line 1 = one_rb_findstr([error],pid_to_list(Self)), - ?line 1 = one_rb_findstr([error_report],pid_to_list(Self)), + ?line true = (one_rb_lines([error]) =:= 0), + ?line true = (one_rb_lines([error_report]) =:= 0), + ?line 0 = one_rb_findstr([error],pid_to_list(Self)), + ?line 0 = one_rb_findstr([error_report],pid_to_list(Self)), + ?line 1 = one_rb_findstr([warning_msg],pid_to_list(Self)), + ?line 1 = one_rb_findstr([warning_report],pid_to_list(Self)), + ?line 0 = one_rb_findstr([info_msg],pid_to_list(Self)), + ?line 0 = one_rb_findstr([info_report],pid_to_list(Self)), ?line 2 = one_rb_findstr([],pid_to_list(Self)), ?line true = (one_rb_findstr([progress],"===") > 4), ?line rb:stop(), @@ -381,6 +392,7 @@ rb_warnings_info() -> "error_logger_mf_maxfiles 5"), ?line Self = self(), ?line GL = group_leader(), + ?line info = warning_map(Node), ?line Report = [{self,Self},{gl,GL},make_ref()], ?line fake_gl(Node,warning_msg,"~p~n",[Self]), ?line fake_gl(Node,warning_report,Report), @@ -403,13 +415,14 @@ rb_warnings_info() -> ?line stop_node(Node), ok. -rb_warnings_warnings() -> +rb_warnings_errors() -> ?line clean_rd(), - ?line Node = start_node(nn(),"+W w -boot start_sasl -sasl error_logger_mf_dir "++ + ?line Node = start_node(nn(),"+W e -boot start_sasl -sasl error_logger_mf_dir "++ quote(rd())++" error_logger_mf_maxbytes 5000 " "error_logger_mf_maxfiles 5"), ?line Self = self(), ?line GL = group_leader(), + ?line error = warning_map(Node), ?line Report = [{self,Self},{gl,GL},make_ref()], ?line fake_gl(Node,warning_msg,"~p~n",[Self]), ?line fake_gl(Node,warning_report,Report), @@ -417,12 +430,12 @@ rb_warnings_warnings() -> ?line application:start(sasl), ?line rb:start([{report_dir, rd()}]), ?line rb:list(), - ?line true = (one_rb_lines([error]) =:= 0), - ?line true = (one_rb_lines([error_report]) =:= 0), - ?line 0 = one_rb_findstr([error],pid_to_list(Self)), - ?line 0 = one_rb_findstr([error_report],pid_to_list(Self)), - ?line 1 = one_rb_findstr([warning_msg],pid_to_list(Self)), - ?line 1 = one_rb_findstr([warning_report],pid_to_list(Self)), + ?line true = (one_rb_lines([error]) > 1), + ?line true = (one_rb_lines([error_report]) > 1), + ?line 1 = one_rb_findstr([error],pid_to_list(Self)), + ?line 1 = one_rb_findstr([error_report],pid_to_list(Self)), + ?line 0 = one_rb_findstr([warning_msg],pid_to_list(Self)), + ?line 0 = one_rb_findstr([warning_report],pid_to_list(Self)), ?line 0 = one_rb_findstr([info_msg],pid_to_list(Self)), ?line 0 = one_rb_findstr([info_report],pid_to_list(Self)), ?line 2 = one_rb_findstr([],pid_to_list(Self)), @@ -434,7 +447,7 @@ rb_warnings_warnings() -> rb_trunc() -> ?line clean_rd(), - ?line Node = start_node(nn(),"+W w -boot start_sasl -sasl error_logger_mf_dir "++ + ?line Node = start_node(nn(),"-boot start_sasl -sasl error_logger_mf_dir "++ quote(rd())++" error_logger_mf_maxbytes 5000 " "error_logger_mf_maxfiles 5"), ?line Self = self(), @@ -467,7 +480,7 @@ rb_trunc() -> rb_utc() -> ?line clean_rd(), - ?line Node = start_node(nn(),"+W w -boot start_sasl -sasl error_logger_mf_dir "++ + ?line Node = start_node(nn(),"-boot start_sasl -sasl error_logger_mf_dir "++ quote(rd())++" error_logger_mf_maxbytes 5000 " "error_logger_mf_maxfiles 5 -sasl utc_log true"), ?line Self = self(), @@ -500,7 +513,7 @@ rb_utc() -> file_utc() -> ?line file:delete(lf()), - ?line SS="+W w -stdlib utc_log true -kernel error_logger "++ oquote("{file,"++iquote(lf())++"}"), + ?line SS="-stdlib utc_log true -kernel error_logger "++ oquote("{file,"++iquote(lf())++"}"), %erlang:display(SS), ?line Node = start_node(nn(),SS), %erlang:display(rpc:call(Node,application,get_env,[kernel,error_logger])), diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl index c27d265550..4a527e2f51 100644 --- a/lib/kernel/test/gen_tcp_api_SUITE.erl +++ b/lib/kernel/test/gen_tcp_api_SUITE.erl @@ -32,6 +32,7 @@ t_connect_bad/1, t_recv_timeout/1, t_recv_eof/1, t_shutdown_write/1, t_shutdown_both/1, t_shutdown_error/1, + t_shutdown_async/1, t_fdopen/1, t_fdconnect/1, t_implicit_inet6/1]). -export([getsockfd/0,closesockfd/1]). @@ -41,7 +42,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [{group, t_accept}, {group, t_connect}, {group, t_recv}, t_shutdown_write, t_shutdown_both, t_shutdown_error, - t_fdopen, t_fdconnect, t_implicit_inet6]. + t_shutdown_async, t_fdopen, t_fdconnect, t_implicit_inet6]. groups() -> [{t_accept, [], [t_accept_timeout]}, @@ -155,7 +156,34 @@ t_shutdown_error(Config) when is_list(Config) -> ?line ok = gen_tcp:close(L), ?line {error, closed} = gen_tcp:shutdown(L, read_write), ok. - + +t_shutdown_async(Config) when is_list(Config) -> + ?line {OS, _} = os:type(), + ?line {ok, L} = gen_tcp:listen(0, [{sndbuf, 4096}]), + ?line {ok, Port} = inet:port(L), + ?line {ok, Client} = gen_tcp:connect(localhost, Port, + [{recbuf, 4096}, + {active, false}]), + ?line {ok, S} = gen_tcp:accept(L), + ?line PayloadSize = 1024 * 1024, + ?line Payload = lists:duplicate(PayloadSize, $.), + ?line ok = gen_tcp:send(S, Payload), + ?line case erlang:port_info(S, queue_size) of + {queue_size, N} when N > 0 -> ok; + {queue_size, 0} when OS =:= win32 -> ok; + {queue_size, 0} = T -> ?t:fail({unexpected, T}) + end, + + ?line ok = gen_tcp:shutdown(S, write), + ?line {ok, Buf} = gen_tcp:recv(Client, PayloadSize), + ?line {error, closed} = gen_tcp:recv(Client, 0), + ?line case length(Buf) of + PayloadSize -> ok; + Sz -> ?t:fail({payload_size, + {expected, PayloadSize}, + {received, Sz}}) + end. + %%% gen_tcp:fdopen/2 diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl index 44a32fc1ec..c77de9316f 100644 --- a/lib/kernel/test/inet_SUITE.erl +++ b/lib/kernel/test/inet_SUITE.erl @@ -569,8 +569,11 @@ parse_address(Config) when is_list(Config) -> "::-1", "::g", "f:f11::10100:2", + "f:f11::01100:2", "::17000", + "::01700", "10000::", + "01000::", "::8:7:6:5:4:3:2:1", "8:7:6:5:4:3:2:1::", "8:7:6:5:4::3:2:1", diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index e1d447a465..c912da0091 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 3.2 +KERNEL_VSN = 4.0 diff --git a/lib/megaco/src/app/megaco.app.src b/lib/megaco/src/app/megaco.app.src index 3720b1109e..573b1857f6 100644 --- a/lib/megaco/src/app/megaco.app.src +++ b/lib/megaco/src/app/megaco.app.src @@ -114,7 +114,7 @@ {env, []}, {mod, {megaco_sup, []}}, {runtime_dependencies, ["stdlib-2.5","runtime_tools-1.8.14","kernel-3.0", - "et-1.5","erts-6.0","debugger-4.0", + "et-1.5","erts-7.0","debugger-4.0", "asn1-3.0"]} ]}. diff --git a/lib/megaco/src/app/megaco.appup.src b/lib/megaco/src/app/megaco.appup.src index 92504e8e87..1c55a92b55 100644 --- a/lib/megaco/src/app/megaco.appup.src +++ b/lib/megaco/src/app/megaco.appup.src @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2014. All Rights Reserved. +%% Copyright Ericsson AB 2001-2015. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -183,11 +183,15 @@ %% | %% v %% 3.17.3 +%% | +%% v +%% 3.18 %% %% {"%VSN%", [ + {"3.17.3", [{restart_application,megaco}]}, {"3.17.2", []}, {"3.17.1", [{restart_application,megaco}]}, {"3.17.0.3", [{restart_application,megaco}]}, @@ -202,6 +206,7 @@ } ], [ + {"3.17.3", [{restart_application,megaco}]}, {"3.17.2", []}, {"3.17.1", [{restart_application,megaco}]}, {"3.17.0.3", [{restart_application,megaco}]}, diff --git a/lib/megaco/src/engine/megaco_trans_sender.erl b/lib/megaco/src/engine/megaco_trans_sender.erl index 710fef405a..e07f404289 100644 --- a/lib/megaco/src/engine/megaco_trans_sender.erl +++ b/lib/megaco/src/engine/megaco_trans_sender.erl @@ -672,8 +672,7 @@ to(To, Start) -> %% Time in milli seconds t() -> - {A,B,C} = erlang:now(), - A*1000000000+B*1000+(C div 1000). + erlang:monotonic_time(milli_seconds). warning_msg(F, A) -> ?megaco_warning("Transaction sender: " ++ F, A). diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk index b23339e408..79dd495c4b 100644 --- a/lib/mnesia/vsn.mk +++ b/lib/mnesia/vsn.mk @@ -1 +1 @@ -MNESIA_VSN = 4.12.5 +MNESIA_VSN = 4.13 diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk index 10ed3bdfe5..7e7e32099b 100644 --- a/lib/observer/vsn.mk +++ b/lib/observer/vsn.mk @@ -1 +1 @@ -OBSERVER_VSN = 2.0.4 +OBSERVER_VSN = 2.1 diff --git a/lib/os_mon/src/cpu_sup.erl b/lib/os_mon/src/cpu_sup.erl index 3d837431d8..d8cfd845bc 100644 --- a/lib/os_mon/src/cpu_sup.erl +++ b/lib/os_mon/src/cpu_sup.erl @@ -121,7 +121,7 @@ util(Args) when is_list (Args) -> util(_) -> erlang:error(badarg). --spec util() -> float(). +-spec util() -> float() | {'error', any()}. util() -> case util([]) of diff --git a/lib/os_mon/vsn.mk b/lib/os_mon/vsn.mk index 833e855e0e..7f2667e40a 100644 --- a/lib/os_mon/vsn.mk +++ b/lib/os_mon/vsn.mk @@ -1 +1 @@ -OS_MON_VSN = 2.3.1 +OS_MON_VSN = 2.4 diff --git a/lib/parsetools/vsn.mk b/lib/parsetools/vsn.mk index dd9cc2991c..b99b3bb713 100644 --- a/lib/parsetools/vsn.mk +++ b/lib/parsetools/vsn.mk @@ -1 +1 @@ -PARSETOOLS_VSN = 2.0.12 +PARSETOOLS_VSN = 2.1 diff --git a/lib/sasl/doc/src/sasl_app.xml b/lib/sasl/doc/src/sasl_app.xml index 9c3c80bd13..572e550061 100644 --- a/lib/sasl/doc/src/sasl_app.xml +++ b/lib/sasl/doc/src/sasl_app.xml @@ -92,6 +92,13 @@ <item>Installs <c>sasl_report_file_h</c> in the error logger. This makes all reports go to the file <c>FileName</c>. <c>FileName</c> is a string.</item> + <tag><c>{file,FileName,Modes}</c></tag> + <item>Same as <c>{file,FileName}</c> except that the <c>Modes</c> + allows to specify the modes used for opening the <c>FileName</c> + given to the <seealso marker="kernel:file#open/2">file:open/2</seealso> + call. When not specified, the <c>Modes</c> defaults to <c>[write]</c>. + Use <c>[append]</c> for having the <c>FileName</c> open in append mode. + <c>FileName</c> is a string.</item> <tag><c>false</c></tag> <item> <p>No SASL error logger handler is installed.</p> diff --git a/lib/sasl/src/sasl.erl b/lib/sasl/src/sasl.erl index fdea6da13e..4a220f0511 100644 --- a/lib/sasl/src/sasl.erl +++ b/lib/sasl/src/sasl.erl @@ -55,7 +55,9 @@ get_sasl_error_logger() -> case application:get_env(sasl, sasl_error_logger) of {ok, false} -> undefined; {ok, tty} -> tty; - {ok, {file, File}} when is_list(File) -> {file, File}; + {ok, {file, File}} when is_list(File) -> {file, File, [write]}; + {ok, {file, File, Modes}} when is_list(File), is_list(Modes) -> + {file, File, Modes}; {ok, Bad} -> exit({bad_config, {sasl, {sasl_error_logger, Bad}}}); _ -> undefined end. @@ -125,9 +127,9 @@ delete_sasl_error_logger(Type) -> error_logger:delete_report_handler(mod(Type)). mod(tty) -> sasl_report_tty_h; -mod({file, _File}) -> sasl_report_file_h. +mod({file, _File, _Modes}) -> sasl_report_file_h. -args({file, File}, Type) -> {File, type(Type)}; +args({file, File, Modes}, Type) -> {File, Modes, type(Type)}; args(_, Type) -> type(Type). type(error) -> error; diff --git a/lib/sasl/src/sasl_report_file_h.erl b/lib/sasl/src/sasl_report_file_h.erl index f42b4b5ff2..a5bd0ac055 100644 --- a/lib/sasl/src/sasl_report_file_h.erl +++ b/lib/sasl/src/sasl_report_file_h.erl @@ -28,9 +28,9 @@ handle_event/2, handle_call/2, handle_info/2, terminate/2]). -init({File, Type}) -> +init({File, Modes, Type}) when is_list(Modes) -> process_flag(trap_exit, true), - case file:open(File, [write]) of + case file:open(File, Modes) of {ok,Fd} -> {ok, {Fd, File, Type}}; What -> diff --git a/lib/sasl/test/sasl_SUITE.erl b/lib/sasl/test/sasl_SUITE.erl index d7b99d506e..d9ab9e551c 100644 --- a/lib/sasl/test/sasl_SUITE.erl +++ b/lib/sasl/test/sasl_SUITE.erl @@ -26,10 +26,11 @@ %% Test cases must be exported. -export([app_test/1, appup_test/1, - log_mf_h_env/1]). + log_mf_h_env/1, + log_file/1]). all() -> - [log_mf_h_env, app_test, appup_test]. + [log_mf_h_env, log_file, app_test, appup_test]. groups() -> []. @@ -151,10 +152,9 @@ check_appup([],_,_) -> log_mf_h_env(Config) -> PrivDir = ?config(priv_dir,Config), LogDir = filename:join(PrivDir,sasl_SUITE_log_dir), - ok = file:make_dir(LogDir), + ok = filelib:ensure_dir(LogDir), application:stop(sasl), - SaslEnv = application:get_all_env(sasl), - lists:foreach(fun({E,_V}) -> application:unset_env(sasl,E) end, SaslEnv), + clear_env(sasl), ok = application:set_env(sasl,error_logger_mf_dir,LogDir), match_error(missing_config,application:start(sasl)), @@ -178,6 +178,23 @@ log_mf_h_env(Config) -> ok = application:set_env(sasl,error_logger_mf_dir,LogDir), ok = application:start(sasl). +log_file(Config) -> + PrivDir = ?config(priv_dir,Config), + LogDir = filename:join(PrivDir,sasl_SUITE_log_dir), + ok = filelib:ensure_dir(LogDir), + File = filename:join(LogDir, "file.log"), + application:stop(sasl), + clear_env(sasl), + + ok = application:set_env(sasl,sasl_error_logger,{file, File}, [{persistent, true}]), + ok = application:start(sasl), + application:stop(sasl), + ok = application:set_env(sasl,sasl_error_logger,{file, File, [append]}, [{persistent, true}]), + ok = application:start(sasl), + application:stop(sasl), + ok = application:set_env(sasl,sasl_error_logger, tty, [{persistent, false}]), + ok = application:start(sasl). + %%----------------------------------------------------------------- %% Internal @@ -185,3 +202,7 @@ match_error(Expected,{error,{bad_return,{_,{'EXIT',{Expected,{sasl,_}}}}}}) -> ok; match_error(Expected,Actual) -> ?t:fail({unexpected_return,Expected,Actual}). + +clear_env(App) -> + [application:unset_env(App,Opt) || {Opt,_} <- application:get_all_env(App)], + ok. diff --git a/lib/sasl/vsn.mk b/lib/sasl/vsn.mk index 4259a2d76c..8d1a043410 100644 --- a/lib/sasl/vsn.mk +++ b/lib/sasl/vsn.mk @@ -1 +1 @@ -SASL_VSN = 2.4.1 +SASL_VSN = 2.4.2 diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src index 081163b368..a21ff863be 100644 --- a/lib/snmp/src/app/snmp.appup.src +++ b/lib/snmp/src/app/snmp.appup.src @@ -28,6 +28,8 @@ %% {update, snmpa_local_db, soft, soft_purge, soft_purge, []} %% {add_module, snmpm_net_if_mt} [ + {"5.1.2", [ % Only runtime dependencies change + ]}, {"5.1.1", [{restart_application, snmp}]}, {"5.1", [ % Only compiler changes ]}, @@ -47,6 +49,8 @@ %% {remove, {snmpm_net_if_mt, soft_purge, soft_purge}} [ + {"5.1.2", [ % Only runtime dependencies change + ]}, {"5.1.1", [{restart_application, snmp}]}, {"5.1", [ % Only compiler changes ]}, diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index 579a3ae4a8..c77ee1e77a 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -29,6 +29,33 @@ <file>notes.xml</file> </header> +<section><title>Ssh 3.2.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Gracefully terminate if sockets is unexpectedly closed.</p> + <p> + Own Id: OTP-12782</p> + </item> + <item> + <p> + Made Codenomicon Defensics test suite pass: <list> + <item>limit number of algorithms in kexinit + message</item> <item>check 'e' and 'f' parameters in + kexdh</item> <item>implement 'keyboard-interactive' user + authentication on server side</item> <item> return plain + text message to bad version exchange message</item> + </list></p> + <p> + Own Id: OTP-12784</p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 3.2.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml index df13442fc6..cf58806aa8 100644 --- a/lib/ssh/doc/src/ssh.xml +++ b/lib/ssh/doc/src/ssh.xml @@ -35,13 +35,15 @@ <section> <title>SSH</title> - + <marker id="supported"/> <list type="bulleted"> <item>For application dependencies see <seealso marker="SSH_app"> ssh(6)</seealso> </item> <item>Supported SSH version is 2.0.</item> + <item>Supported public key algorithms: ssh-rsa and ssh-dss.</item> <item>Supported MAC algorithms: hmac-sha2-256 and hmac-sha1.</item> <item>Supported encryption algorithms: aes128-ctr, aes128-cb and 3des-cbc.</item> <item>Supported key exchange algorithms: diffie-hellman-group1-sha1.</item> + <item>Supported compression algorithms: none, zlib, [email protected],</item> <item>Supports unicode filenames if the emulator and the underlaying OS support it. See section DESCRIPTION in the <seealso marker="kernel:file">file</seealso> manual page in <c>kernel</c> @@ -79,6 +81,18 @@ <seealso marker="ssh_channel">ssh_channel(3)</seealso></p></item> <tag><c>channel_init_args() =</c></tag> <item><p><c>list()</c></p></item> + + <tag><c>algs_list() =</c></tag> + <item><p><c>list( alg_entry() )</c></p></item> + + <tag><c>alg_entry() =</c></tag> + <item><p><c>{kex, simple_algs()} | {public_key, simple_algs()} | {cipher, double_algs()} | {mac, double_algs()} | {compression, double_algs()}</c></p></item> + + <tag><c>simple_algs() =</c></tag> + <item><p><c>list( atom() )</c></p></item> + + <tag><c>double_algs() =</c></tag> + <item><p><c>[{client2serverlist,simple_algs()},{server2client,simple_algs()}] | simple_algs()</c></p></item> </taglist> </section> @@ -160,19 +174,58 @@ and <c>password</c>. However, those optins are not always desirable to use from a security point of view.</p> </item> + <tag><c><![CDATA[{public_key_alg, 'ssh-rsa' | 'ssh-dss'}]]></c></tag> <item> + <note> + <p>This option is kept for compatibility. It is ignored if the <c>preferred_algorithms</c> + option is used. The equivalence of <c>{public_key_alg,'ssh-dss'}</c> is + <c>{preferred_algorithms, [{public_key,['ssh-dss','ssh-rsa']}]}</c>.</p> + </note> <p>Sets the preferred public key algorithm to use for user authentication. If the preferred algorithm fails, the other algorithm is tried. The default is to try <c><![CDATA['ssh-rsa']]></c> first.</p> </item> + <tag><c><![CDATA[{pref_public_key_algs, list()}]]></c></tag> <item> + <note> + <p>This option is kept for compatibility. It is ignored if the <c>preferred_algorithms</c> + option is used. The equivalence of <c>{pref_public_key_algs,['ssh-dss']}</c> is + <c>{preferred_algorithms, [{public_key,['ssh-dss']}]}</c>.</p> + </note> <p>List of public key algorithms to try to use. <c>'ssh-rsa'</c> and <c>'ssh-dss'</c> are available. Overrides <c><![CDATA[{public_key_alg, 'ssh-rsa' | 'ssh-dss'}]]></c></p> </item> + + <tag><c><![CDATA[{preferred_algorithms, algs_list()}]]></c></tag> + <item> + <p>List of algorithms to use in the algorithm negotiation. The default <c>algs_list()</c> can + be obtained from <seealso marker="#default_algorithms/0">default_algorithms/0</seealso>. + </p> + <p>Here is an example of this option:</p> + <code> +{preferred_algorithms, + [{public_key,['ssh-rsa','ssh-dss']}, + {cipher,[{client2server,['aes128-ctr']}, + {server2client,['aes128-cbc','3des-cbc']}]}, + {mac,['hmac-sha2-256','hmac-sha1']}, + {compression,[none,zlib]} +} +</code> + <p>The example specifies different algorithms in the two directions (client2server and server2client), for cipher but specifies the same +algorithms for mac and compression in both directions. The kex (key exchange) and public key algorithms are set to their default values, +kex is implicit but public_key is set explicitly.</p> + + <warning> + <p>Changing the values can make a connection less secure. Do not change unless you + know exactly what you are doing. If you do not understand the values then you + are not supposed to change them.</p> + </warning> + </item> + <tag><c><![CDATA[{connect_timeout, timeout()}]]></c></tag> <item> <p>Sets a time-out on the transport layer @@ -341,6 +394,33 @@ user. From a security perspective this option makes the server very vulnerable.</p> </item> + + <tag><c><![CDATA[{preferred_algorithms, algs_list()}]]></c></tag> + <item> + <p>List of algorithms to use in the algorithm negotiation. The default <c>algs_list()</c> can + be obtained from <seealso marker="#default_algorithms/0">default_algorithms/0</seealso>. + </p> + <p>Here is an example of this option:</p> + <code> +{preferred_algorithms, + [{public_key,['ssh-rsa','ssh-dss']}, + {cipher,[{client2server,['aes128-ctr']}, + {server2client,['aes128-cbc','3des-cbc']}]}, + {mac,['hmac-sha2-256','hmac-sha1']}, + {compression,[none,zlib]} +} +</code> + <p>The example specifies different algorithms in the two directions (client2server and server2client), for cipher but specifies the same +algorithms for mac and compression in both directions. The kex (key exchange) and public key algorithms are set to their default values, +kex is implicit but public_key is set explicitly.</p> + + <warning> + <p>Changing the values can make a connection less secure. Do not change unless you + know exactly what you are doing. If you do not understand the values then you + are not supposed to change them.</p> + </warning> + </item> + <tag><c><![CDATA[{pwdfun, fun(User::string(), password::string()) -> boolean()}]]></c></tag> <item> <p>Provides a function for password validation. This function is called @@ -445,6 +525,26 @@ </desc> </func> + <func> + <name>default_algorithms() -> algs_list()</name> + <fsummary>Get a list declaring the supported algorithms</fsummary> + <desc> + <p>Returns a key-value list, where the keys are the different types of algorithms and the values are the + algorithms themselves. An example:</p> + <code> +20> ssh:default_algorithms(). +[{kex,['diffie-hellman-group1-sha1']}, + {public_key,['ssh-rsa','ssh-dss']}, + {cipher,[{client2server,['aes128-ctr','aes128-cbc','3des-cbc']}, + {server2client,['aes128-ctr','aes128-cbc','3des-cbc']}]}, + {mac,[{client2server,['hmac-sha2-256','hmac-sha1']}, + {server2client,['hmac-sha2-256','hmac-sha1']}]}, + {compression,[{client2server,[none,zlib]}, + {server2client,[none,zlib]}]}] +21> +</code> + </desc> + </func> <func> <name>shell(Host) -> </name> diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index 71e7d77475..4a07473f74 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -28,6 +28,7 @@ -export([start/0, start/1, stop/0, connect/3, connect/4, close/1, connection_info/2, channel_info/3, daemon/1, daemon/2, daemon/3, + default_algorithms/0, stop_listener/1, stop_listener/2, stop_daemon/1, stop_daemon/2, shell/1, shell/2, shell/3]). @@ -208,6 +209,11 @@ shell(Host, Port, Options) -> end. %%-------------------------------------------------------------------- +%%-------------------------------------------------------------------- +default_algorithms() -> + ssh_transport:default_algorithms(). + +%%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- fix_idle_time(SshOptions) -> @@ -259,7 +265,7 @@ do_start_daemon(Host, Port, Options, SocketOptions) -> end. handle_options(Opts) -> - try handle_option(proplists:unfold(Opts), [], []) of + try handle_option(algs_compatibility(proplists:unfold(Opts)), [], []) of {Inet, Ssh} -> {handle_ip(Inet), Ssh} catch @@ -267,6 +273,35 @@ handle_options(Opts) -> Error end. + +algs_compatibility(Os) -> + %% Take care of old options 'public_key_alg' and 'pref_public_key_algs' + comp_pk(proplists:get_value(preferred_algorithms,Os), + proplists:get_value(pref_public_key_algs,Os), + proplists:get_value(public_key_alg, Os), + [{K,V} || {K,V} <- Os, + K =/= public_key_alg, + K =/= pref_public_key_algs] + ). + +comp_pk(undefined, undefined, undefined, Os) -> Os; +comp_pk( PrefAlgs, _, _, Os) when PrefAlgs =/= undefined -> Os; + +comp_pk(undefined, undefined, ssh_dsa, Os) -> comp_pk(undefined, undefined, 'ssh-dss', Os); +comp_pk(undefined, undefined, ssh_rsa, Os) -> comp_pk(undefined, undefined, 'ssh-rsa', Os); +comp_pk(undefined, undefined, PK, Os) -> + PKs = [PK | ssh_transport:supported_algorithms(public_key)--[PK]], + [{preferred_algorithms, [{public_key,PKs}] } | Os]; + +comp_pk(undefined, PrefPKs, _, Os) when PrefPKs =/= undefined -> + PKs = [case PK of + ssh_dsa -> 'ssh-dss'; + ssh_rsa -> 'ssh-rsa'; + _ -> PK + end || PK <- PrefPKs], + [{preferred_algorithms, [{public_key,PKs}]} | Os]. + + handle_option([], SocketOptions, SshOptions) -> {SocketOptions, SshOptions}; handle_option([{system_dir, _} = Opt | Rest], SocketOptions, SshOptions) -> @@ -279,8 +314,6 @@ handle_option([{silently_accept_hosts, _} = Opt | Rest], SocketOptions, SshOptio handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{user_interaction, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); -handle_option([{public_key_alg, _} = Opt | Rest], SocketOptions, SshOptions) -> - handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{connect_timeout, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{user, _} = Opt | Rest], SocketOptions, SshOptions) -> @@ -297,10 +330,6 @@ handle_option([{pwdfun, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{key_cb, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); -handle_option([{role, _} = Opt | Rest], SocketOptions, SshOptions) -> - handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); -handle_option([{compression, _} = Opt | Rest], SocketOptions, SshOptions) -> - handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); %%Backwards compatibility handle_option([{allow_user_interaction, Value} | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option({user_interaction, Value}) | SshOptions]); @@ -331,7 +360,9 @@ handle_option([{exec, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{auth_methods, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); -handle_option([{pref_public_key_algs, _} = Opt | Rest], SocketOptions, SshOptions) -> +handle_option([{auth_method_kb_interactive_data, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{preferred_algorithms,_} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{quiet_mode, _} = Opt|Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); @@ -367,19 +398,8 @@ handle_ssh_option({silently_accept_hosts, Value} = Opt) when is_boolean(Value) - Opt; handle_ssh_option({user_interaction, Value} = Opt) when is_boolean(Value) -> Opt; -handle_ssh_option({public_key_alg, ssh_dsa}) -> - {public_key_alg, 'ssh-dss'}; -handle_ssh_option({public_key_alg, ssh_rsa}) -> - {public_key_alg, 'ssh-rsa'}; -handle_ssh_option({public_key_alg, Value} = Opt) when Value == 'ssh-rsa'; Value == 'ssh-dss' -> - Opt; -handle_ssh_option({pref_public_key_algs, Value} = Opt) when is_list(Value), length(Value) >= 1 -> - case handle_pref_algs(Value, []) of - {true, NewOpts} -> - NewOpts; - _ -> - throw({error, {eoptions, Opt}}) - end; +handle_ssh_option({preferred_algorithms,[_|_]} = Opt) -> + handle_pref_algs(Opt); handle_ssh_option({connect_timeout, Value} = Opt) when is_integer(Value); Value == infinity -> Opt; handle_ssh_option({max_sessions, Value} = Opt) when is_integer(Value), Value>0 -> @@ -411,6 +431,13 @@ handle_ssh_option({exec, Function} = Opt) when is_function(Function) -> Opt; handle_ssh_option({auth_methods, Value} = Opt) when is_list(Value) -> Opt; +handle_ssh_option({auth_method_kb_interactive_data, {Name,Instruction,Prompt,Echo}} = Opt) when is_list(Name), + is_list(Instruction), + is_list(Prompt), + is_boolean(Echo) -> + Opt; +handle_ssh_option({auth_method_kb_interactive_data, F} = Opt) when is_function(F,3) -> + Opt; handle_ssh_option({infofun, Value} = Opt) when is_function(Value) -> Opt; handle_ssh_option({connectfun, Value} = Opt) when is_function(Value) -> @@ -465,23 +492,83 @@ handle_inet_option({reuseaddr, _} = Opt) -> %% Option verified by inet handle_inet_option(Opt) -> Opt. + + %% Check preferred algs -handle_pref_algs([], Acc) -> - {true, lists:reverse(Acc)}; -handle_pref_algs([H|T], Acc) -> - case H of - ssh_dsa -> - handle_pref_algs(T, ['ssh-dss'| Acc]); - ssh_rsa -> - handle_pref_algs(T, ['ssh-rsa'| Acc]); - 'ssh-dss' -> - handle_pref_algs(T, ['ssh-dss'| Acc]); - 'ssh-rsa' -> - handle_pref_algs(T, ['ssh-rsa'| Acc]); - _ -> - false + +handle_pref_algs({preferred_algorithms,Algs}) -> + try alg_duplicates(Algs, [], []) of + [] -> + {preferred_algorithms, + [try ssh_transport:supported_algorithms(Key) + of + DefAlgs -> handle_pref_alg(Key,Vals,DefAlgs) + catch + _:_ -> throw({error, {{eoptions, {preferred_algorithms,Key}}, + "Bad preferred_algorithms key"}}) + end || {Key,Vals} <- Algs] + }; + + Dups -> + throw({error, {{eoptions, {preferred_algorithms,Dups}}, "Duplicates found"}}) + catch + _:_ -> + throw({error, {{eoptions, preferred_algorithms}, "Malformed"}}) end. +alg_duplicates([{K,V}|KVs], Ks, Dups0) -> + Dups = + case lists:member(K,Ks) of + true -> + [K|Dups0]; + false -> + Dups0 + end, + case V--lists:usort(V) of + [] -> + alg_duplicates(KVs, [K|Ks], Dups); + Ds -> + alg_duplicates(KVs, [K|Ks], Dups++Ds) + end; +alg_duplicates([], _Ks, Dups) -> + Dups. + +handle_pref_alg(Key, + Vs=[{client2server,C2Ss=[_|_]},{server2client,S2Cs=[_|_]}], + [{client2server,Sup_C2Ss},{server2client,Sup_S2Cs}] + ) -> + chk_alg_vs(Key, C2Ss, Sup_C2Ss), + chk_alg_vs(Key, S2Cs, Sup_S2Cs), + {Key, Vs}; + +handle_pref_alg(Key, + Vs=[{server2client,[_|_]},{client2server,[_|_]}], + Sup=[{client2server,_},{server2client,_}] + ) -> + handle_pref_alg(Key, lists:reverse(Vs), Sup); + +handle_pref_alg(Key, + Vs=[V|_], + Sup=[{client2server,_},{server2client,_}] + ) when is_atom(V) -> + handle_pref_alg(Key, [{client2server,Vs},{server2client,Vs}], Sup); + +handle_pref_alg(Key, + Vs=[V|_], + Sup=[S|_] + ) when is_atom(V), is_atom(S) -> + chk_alg_vs(Key, Vs, Sup), + {Key, Vs}; + +handle_pref_alg(Key, Vs, _) -> + throw({error, {{eoptions, {preferred_algorithms,[{Key,Vs}]}}, "Badly formed list"}}). + +chk_alg_vs(OptKey, Values, SupportedValues) -> + case (Values -- SupportedValues) of + [] -> Values; + Bad -> throw({error, {{eoptions, {OptKey,Bad}}, "Unsupported value(s) found"}}) + end. + handle_ip(Inet) -> %% Default to ipv4 case lists:member(inet, Inet) of true -> diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index 45c4d52d7e..df9a97c8f8 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -30,7 +30,8 @@ -export([publickey_msg/1, password_msg/1, keyboard_interactive_msg/1, service_request_msg/1, init_userauth_request_msg/1, userauth_request_msg/1, handle_userauth_request/3, - handle_userauth_info_request/3, handle_userauth_info_response/2 + handle_userauth_info_request/3, handle_userauth_info_response/2, + default_public_key_algorithms/0 ]). %%-------------------------------------------------------------------- @@ -115,33 +116,16 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) -> service = "ssh-connection", method = "none", data = <<>>}, - case proplists:get_value(pref_public_key_algs, Opts, false) of - false -> - FirstAlg = proplists:get_value(public_key_alg, Opts, ?PREFERRED_PK_ALG), - SecondAlg = other_alg(FirstAlg), - Prefs = method_preference(FirstAlg, SecondAlg), - ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User, - userauth_preference = Prefs, - userauth_methods = none, - service = "ssh-connection"}); - Algs -> - FirstAlg = lists:nth(1, Algs), - case length(Algs) =:= 2 of - true -> - SecondAlg = other_alg(FirstAlg), - Prefs = method_preference(FirstAlg, SecondAlg), - ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User, - userauth_preference = Prefs, - userauth_methods = none, - service = "ssh-connection"}); - _ -> - Prefs = method_preference(FirstAlg), - ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User, - userauth_preference = Prefs, - userauth_methods = none, - service = "ssh-connection"}) - end - end; + + + Algs = proplists:get_value(public_key, + proplists:get_value(preferred_algorithms, Opts, []), + default_public_key_algorithms()), + Prefs = method_preference(Algs), + ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User, + userauth_preference = Prefs, + userauth_methods = none, + service = "ssh-connection"}); {error, no_user} -> ErrStr = "Could not determine the users name", throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_ILLEGAL_USER_NAME, @@ -259,6 +243,54 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, handle_userauth_request(#ssh_msg_userauth_request{user = User, service = "ssh-connection", + method = "keyboard-interactive", + data = _}, + _, #ssh{opts = Opts} = Ssh) -> + %% RFC4256 + %% The data field contains: + %% - language tag (deprecated). If =/=[] SHOULD use it however. We skip + %% it for simplicity. + %% - submethods. "... the user can give a hint of which actual methods + %% he wants to use. ...". It's a "MAY use" so we skip + %% it. It also needs an understanding between the client + %% and the server. + %% + %% "The server MUST reply with an SSH_MSG_USERAUTH_SUCCESS, + %% SSH_MSG_USERAUTH_FAILURE, or SSH_MSG_USERAUTH_INFO_REQUEST message." + Default = {"SSH server", + "Enter password for \""++User++"\"", + "pwd: ", + false}, + + {Name, Instruction, Prompt, Echo} = + case proplists:get_value(auth_method_kb_interactive_data, Opts) of + undefined -> + Default; + {_,_,_,_}=V -> + V; + F when is_function(F) -> + {_,PeerName} = Ssh#ssh.peer, + F(PeerName, User, "ssh-connection") + end, + EchoEnc = case Echo of + true -> <<?TRUE>>; + false -> <<?FALSE>> + end, + Msg = #ssh_msg_userauth_info_request{name = unicode:characters_to_list(Name), + instruction = unicode:characters_to_list(Instruction), + language_tag = "", + num_prompts = 1, + data = <<?STRING(unicode:characters_to_binary(Prompt)), + EchoEnc/binary + >> + }, + {not_authorized, {User, undefined}, + ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User, + opts = [{max_kb_tries,3},{kb_userauth_info_msg,Msg}|Opts] + })}; + +handle_userauth_request(#ssh_msg_userauth_request{user = User, + service = "ssh-connection", method = Other}, _, #ssh{userauth_supported_methods = Methods} = Ssh) -> {not_authorized, {User, {authmethod, Other}}, @@ -280,6 +312,38 @@ handle_userauth_info_request( #ssh_msg_userauth_info_response{num_responses = NumPrompts, data = Responses}, Ssh)}. +handle_userauth_info_response(#ssh_msg_userauth_info_response{num_responses = 1, + data = <<?UINT32(Sz), Password:Sz/binary>>}, + #ssh{opts = Opts0, + user = User} = Ssh) -> + NumTriesLeft = proplists:get_value(max_kb_tries, Opts0, 0) - 1, + Opts = lists:keydelete(max_kb_tries,1,Opts0), + case check_password(User, unicode:characters_to_list(Password), Opts) of + true -> + {authorized, User, + ssh_transport:ssh_packet(#ssh_msg_userauth_success{}, Ssh)}; + false when NumTriesLeft > 0 -> + UserAuthInfoMsg = + (proplists:get_value(kb_userauth_info_msg,Opts)) + #ssh_msg_userauth_info_request{name = "", + instruction = + lists:concat( + ["Bad user or password, try again. ", + integer_to_list(NumTriesLeft), + " tries left."])}, + {not_authorized, {User, undefined}, + ssh_transport:ssh_packet(UserAuthInfoMsg, + Ssh#ssh{opts = [{max_kb_tries,NumTriesLeft}|Opts]})}; + + false -> + {not_authorized, {User, {error,"Bad user or password"}}, + ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ + authentications = "", + partial_success = false}, + Ssh#ssh{opts = lists:keydelete(kb_userauth_info_msg,1,Opts)} + )} + end; + handle_userauth_info_response(#ssh_msg_userauth_info_response{}, _Auth) -> throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE, @@ -287,20 +351,20 @@ handle_userauth_info_response(#ssh_msg_userauth_info_response{}, "keyboard-interactive", language = "en"}). + +default_public_key_algorithms() -> ?PREFERRED_PK_ALGS. + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -method_preference(Alg1, Alg2) -> - [{"publickey", ?MODULE, publickey_msg, [Alg1]}, - {"publickey", ?MODULE, publickey_msg,[Alg2]}, - {"password", ?MODULE, password_msg, []}, - {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []} - ]. -method_preference(Alg1) -> - [{"publickey", ?MODULE, publickey_msg, [Alg1]}, - {"password", ?MODULE, password_msg, []}, - {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []} - ]. +method_preference(Algs) -> + lists:foldr(fun(A, Acc) -> + [{"publickey", ?MODULE, publickey_msg, [A]} | Acc] + end, + [{"password", ?MODULE, password_msg, []}, + {"keyboard-interactive", ?MODULE, keyboard_interactive_msg, []} + ], + Algs). user_name(Opts) -> Env = case os:type() of @@ -418,10 +482,6 @@ keyboard_interact_fun(KbdInteractFun, Name, Instr, PromptInfos, NumPrompts) -> language = "en"}}) end. -other_alg('ssh-rsa') -> - 'ssh-dss'; -other_alg('ssh-dss') -> - 'ssh-rsa'. decode_public_key_v2(<<?UINT32(Len0), _:Len0/binary, ?UINT32(Len1), BinE:Len1/binary, ?UINT32(Len2), BinN:Len2/binary>> diff --git a/lib/ssh/src/ssh_auth.hrl b/lib/ssh/src/ssh_auth.hrl index 6cd8e6bf14..764c9f4246 100644 --- a/lib/ssh/src/ssh_auth.hrl +++ b/lib/ssh/src/ssh_auth.hrl @@ -23,7 +23,7 @@ -define(SUPPORTED_AUTH_METHODS, "publickey,keyboard-interactive,password"). --define(PREFERRED_PK_ALG, 'ssh-rsa'). +-define(PREFERRED_PK_ALGS, ['ssh-rsa','ssh-dss']). -define(SSH_MSG_USERAUTH_REQUEST, 50). -define(SSH_MSG_USERAUTH_FAILURE, 51). diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index 65208ae158..3bdca4ba94 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -33,7 +33,7 @@ -include("ssh_transport.hrl"). -include("ssh_auth.hrl"). -include("ssh_connect.hrl"). - +-compile(export_all). -export([start_link/3]). %% Internal application API @@ -333,22 +333,25 @@ info(ConnectionHandler, ChannelProcess) -> hello(socket_control, #state{socket = Socket, ssh_params = Ssh} = State) -> VsnMsg = ssh_transport:hello_version_msg(string_version(Ssh)), send_msg(VsnMsg, State), - {ok, [{recbuf, Size}]} = inet:getopts(Socket, [recbuf]), - inet:setopts(Socket, [{packet, line}, {active, once}, {recbuf, ?MAX_PROTO_VERSION}]), - {next_state, hello, State#state{recbuf = Size}}; + case getopt(recbuf, Socket) of + {ok, Size} -> + inet:setopts(Socket, [{packet, line}, {active, once}, {recbuf, ?MAX_PROTO_VERSION}]), + {next_state, hello, State#state{recbuf = Size}}; + {error, Reason} -> + {stop, {shutdown, Reason}, State} + end; hello({info_line, _Line},#state{role = client, socket = Socket} = State) -> %% The server may send info lines before the version_exchange inet:setopts(Socket, [{active, once}]), {next_state, hello, State}; -hello({info_line, _Line},#state{role = server} = State) -> - DisconnectMsg = - #ssh_msg_disconnect{code = - ?SSH_DISCONNECT_PROTOCOL_ERROR, - description = "Did not receive expected protocol version exchange", - language = "en"}, - handle_disconnect(DisconnectMsg, State); +hello({info_line, _Line},#state{role = server, + socket = Socket, + transport_cb = Transport } = State) -> + %% as openssh + Transport:send(Socket, "Protocol mismatch."), + {stop, {shutdown,"Protocol mismatch in version exchange."}, State}; hello({version_exchange, Version}, #state{ssh_params = Ssh0, socket = Socket, @@ -501,10 +504,21 @@ userauth(#ssh_msg_userauth_info_request{} = Msg, {next_state, userauth, next_packet(State#state{ssh_params = Ssh})}; userauth(#ssh_msg_userauth_info_response{} = Msg, - #state{ssh_params = #ssh{role = server} = Ssh0} = State) -> - {ok, {Reply, Ssh}} = ssh_auth:handle_userauth_info_response(Msg, Ssh0), - send_msg(Reply, State), - {next_state, userauth, next_packet(State#state{ssh_params = Ssh})}; + #state{ssh_params = #ssh{role = server, + peer = {_, Address}} = Ssh0, + opts = Opts, starter = Pid} = State) -> + case ssh_auth:handle_userauth_info_response(Msg, Ssh0) of + {authorized, User, {Reply, Ssh}} -> + send_msg(Reply, State), + Pid ! ssh_connected, + connected_fun(User, Address, "keyboard-interactive", Opts), + {next_state, connected, + next_packet(State#state{auth_user = User, ssh_params = Ssh})}; + {not_authorized, {User, Reason}, {Reply, Ssh}} -> + retry_fun(User, Address, Reason, Opts), + send_msg(Reply, State), + {next_state, userauth, next_packet(State#state{ssh_params = Ssh})} + end; userauth(#ssh_msg_userauth_success{}, #state{ssh_params = #ssh{role = client} = Ssh, starter = Pid} = State) -> @@ -1156,54 +1170,38 @@ init_ssh(server = Role, Vsn, Version, Options, Socket) -> supported_host_keys(client, _, Options) -> try - case extract_algs(proplists:get_value(pref_public_key_algs, Options, false), []) of - false -> - ["ssh-rsa", "ssh-dss"]; - Algs -> - Algs + case proplists:get_value(public_key, + proplists:get_value(preferred_algorithms,Options,[]) + ) of + undefined -> + ssh_auth:default_public_key_algorithms(); + L -> + L -- (L--ssh_auth:default_public_key_algorithms()) end + of + [] -> + {stop, {shutdown, "No public key algs"}}; + Algs -> + [atom_to_list(A) || A<-Algs] catch exit:Reason -> {stop, {shutdown, Reason}} end; supported_host_keys(server, KeyCb, Options) -> - lists:foldl(fun(Type, Acc) -> - case available_host_key(KeyCb, Type, Options) of - {error, _} -> - Acc; - Alg -> - [Alg | Acc] - end - end, [], - %% Prefered alg last so no need to reverse - ["ssh-dss", "ssh-rsa"]). -extract_algs(false, _) -> - false; -extract_algs([],[]) -> - false; -extract_algs([], NewList) -> - lists:reverse(NewList); -extract_algs([H|T], NewList) -> - case H of - 'ssh-dss' -> - extract_algs(T, ["ssh-dss"|NewList]); - 'ssh-rsa' -> - extract_algs(T, ["ssh-rsa"|NewList]) - end. -available_host_key(KeyCb, "ssh-dss"= Alg, Opts) -> - case KeyCb:host_key('ssh-dss', Opts) of - {ok, _} -> - Alg; - Other -> - Other - end; -available_host_key(KeyCb, "ssh-rsa" = Alg, Opts) -> - case KeyCb:host_key('ssh-rsa', Opts) of - {ok, _} -> - Alg; - Other -> - Other - end. + Algs= + [atom_to_list(A) || A <- proplists:get_value(public_key, + proplists:get_value(preferred_algorithms,Options,[]), + ssh_auth:default_public_key_algorithms() + ), + available_host_key(KeyCb, A, Options) + ], + Algs. + + +%% Alg :: atom() +available_host_key(KeyCb, Alg, Opts) -> + element(1, catch KeyCb:host_key(Alg, Opts)) == ok. + send_msg(Msg, #state{socket = Socket, transport_cb = Transport}) -> Transport:send(Socket, Msg). @@ -1779,3 +1777,12 @@ start_timeout(_,_, infinity) -> ok; start_timeout(Channel, From, Time) -> erlang:send_after(Time, self(), {timeout, {Channel, From}}). + +getopt(Opt, Socket) -> + case inet:getopts(Socket, [Opt]) of + {ok, [{Opt, Value}]} -> + {ok, Value}; + Other -> + {error, {unexpected_getopts_return, Other}} + end. + diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index d6414bab6c..ea9bca2390 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -31,6 +31,8 @@ -export([versions/2, hello_version_msg/1]). -export([next_seqnum/1, decrypt_first_block/2, decrypt_blocks/3, + supported_algorithms/0, supported_algorithms/1, + default_algorithms/0, default_algorithms/1, is_valid_mac/3, handle_hello_version/1, key_exchange_init_msg/1, @@ -42,6 +44,68 @@ unpack/3, decompress/2, ssh_packet/2, pack/2, msg_data/1, sign/3, verify/4]). +%%%---------------------------------------------------------------------------- +%%% +%%% There is a difference between supported and default algorithms. The +%%% SUPPORTED algorithms can be handled (maybe untested...). The DEFAULT ones +%%% are announced in ssh_msg_kexinit and in ssh:default_algorithms/0 to the +%%% user. +%%% +%%% A supported algorithm can be requested in the option 'preferred_algorithms', +%%% but may give unexpected results because of being promoted to default. +%%% +%%% This makes it possible to add experimental algorithms (in supported_algorithms) +%%% and test them without letting the default users know about them. +%%% + +default_algorithms() -> [{K,default_algorithms(K)} || K <- algo_classes()]. + +algo_classes() -> [kex, public_key, cipher, mac, compression]. + +default_algorithms(compression) -> + %% Do not announce '[email protected]' because there seem to be problems + supported_algorithms(compression, same(['[email protected]'])); +default_algorithms(Alg) -> + supported_algorithms(Alg). + + +supported_algorithms() -> [{K,supported_algorithms(K)} || K <- algo_classes()]. + +supported_algorithms(kex) -> + ['diffie-hellman-group1-sha1']; +supported_algorithms(public_key) -> + ssh_auth:default_public_key_algorithms(); +supported_algorithms(cipher) -> + Supports = crypto:supports(), + CipherAlgos = [{aes_ctr, 'aes128-ctr'}, {aes_cbc128, 'aes128-cbc'}, {des3_cbc, '3des-cbc'}], + Algs = [SshAlgo || + {CryptoAlgo, SshAlgo} <- CipherAlgos, + lists:member(CryptoAlgo, proplists:get_value(ciphers, Supports, []))], + same(Algs); +supported_algorithms(mac) -> + Supports = crypto:supports(), + HashAlgos = [{sha256, 'hmac-sha2-256'}, {sha, 'hmac-sha1'}], + Algs = [SshAlgo || + {CryptoAlgo, SshAlgo} <- HashAlgos, + lists:member(CryptoAlgo, proplists:get_value(hashs, Supports, []))], + same(Algs); +supported_algorithms(compression) -> + same(['none','zlib','[email protected]']). + + +supported_algorithms(Key, [{client2server,BL1},{server2client,BL2}]) -> + [{client2server,As1},{server2client,As2}] = supported_algorithms(Key), + [{client2server,As1--BL1},{server2client,As2--BL2}]; +supported_algorithms(Key, BlackList) -> + supported_algorithms(Key) -- BlackList. + + + + +same(Algs) -> [{client2server,Algs}, {server2client,Algs}]. + + +%%%---------------------------------------------------------------------------- versions(client, Options)-> Vsn = proplists:get_value(vsn, Options, ?DEFAULT_CLIENT_VERSION), {Vsn, format_version(Vsn, software_version(Options))}; @@ -128,62 +192,45 @@ key_exchange_init_msg(Ssh0) -> kex_init(#ssh{role = Role, opts = Opts, available_host_keys = HostKeyAlgs}) -> Random = ssh_bits:random(16), - Compression = case proplists:get_value(compression, Opts, none) of - openssh_zlib -> ["[email protected]", "none"]; - zlib -> ["zlib", "none"]; - none -> ["none", "zlib"] - end, - kexinit_messsage(Role, Random, Compression, HostKeyAlgs). + PrefAlgs = + case proplists:get_value(preferred_algorithms,Opts) of + undefined -> + default_algorithms(); + Algs0 -> + Algs0 + end, + kexinit_message(Role, Random, PrefAlgs, HostKeyAlgs). key_init(client, Ssh, Value) -> Ssh#ssh{c_keyinit = Value}; key_init(server, Ssh, Value) -> Ssh#ssh{s_keyinit = Value}. -available_ssh_algos() -> - Supports = crypto:supports(), - CipherAlgos = [{aes_ctr, "aes128-ctr"}, {aes_cbc128, "aes128-cbc"}, {des3_cbc, "3des-cbc"}], - Ciphers = [SshAlgo || - {CryptoAlgo, SshAlgo} <- CipherAlgos, - lists:member(CryptoAlgo, proplists:get_value(ciphers, Supports, []))], - HashAlgos = [{sha256, "hmac-sha2-256"}, {sha, "hmac-sha1"}], - Hashs = [SshAlgo || - {CryptoAlgo, SshAlgo} <- HashAlgos, - lists:member(CryptoAlgo, proplists:get_value(hashs, Supports, []))], - {Ciphers, Hashs}. - -kexinit_messsage(client, Random, Compression, HostKeyAlgs) -> - {CipherAlgs, HashAlgs} = available_ssh_algos(), - #ssh_msg_kexinit{ - cookie = Random, - kex_algorithms = ["diffie-hellman-group1-sha1"], - server_host_key_algorithms = HostKeyAlgs, - encryption_algorithms_client_to_server = CipherAlgs, - encryption_algorithms_server_to_client = CipherAlgs, - mac_algorithms_client_to_server = HashAlgs, - mac_algorithms_server_to_client = HashAlgs, - compression_algorithms_client_to_server = Compression, - compression_algorithms_server_to_client = Compression, - languages_client_to_server = [], - languages_server_to_client = [] - }; -kexinit_messsage(server, Random, Compression, HostKeyAlgs) -> - {CipherAlgs, HashAlgs} = available_ssh_algos(), +kexinit_message(_Role, Random, Algs, HostKeyAlgs) -> #ssh_msg_kexinit{ cookie = Random, - kex_algorithms = ["diffie-hellman-group1-sha1"], + kex_algorithms = to_strings( get_algs(kex,Algs) ), server_host_key_algorithms = HostKeyAlgs, - encryption_algorithms_client_to_server = CipherAlgs, - encryption_algorithms_server_to_client = CipherAlgs, - mac_algorithms_client_to_server = HashAlgs, - mac_algorithms_server_to_client = HashAlgs, - compression_algorithms_client_to_server = Compression, - compression_algorithms_server_to_client = Compression, + encryption_algorithms_client_to_server = c2s(cipher,Algs), + encryption_algorithms_server_to_client = s2c(cipher,Algs), + mac_algorithms_client_to_server = c2s(mac,Algs), + mac_algorithms_server_to_client = s2c(mac,Algs), + compression_algorithms_client_to_server = c2s(compression,Algs), + compression_algorithms_server_to_client = s2c(compression,Algs), languages_client_to_server = [], languages_server_to_client = [] }. +c2s(Key, Algs) -> x2y(client2server, Key, Algs). +s2c(Key, Algs) -> x2y(server2client, Key, Algs). + +x2y(DirectionKey, Key, Algs) -> to_strings(proplists:get_value(DirectionKey, get_algs(Key,Algs))). + +get_algs(Key, Algs) -> proplists:get_value(Key, Algs, default_algorithms(Key)). + +to_strings(L) -> lists:map(fun erlang:atom_to_list/1, L). + new_keys_message(Ssh0) -> {SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0), @@ -448,6 +495,7 @@ select_algorithm(Role, Client, Server) -> decompress = Decompression, c_lng = C_Lng, s_lng = S_Lng}, +%%ct:pal("~p~n Client=~p~n Server=~p~n Alg=~p~n",[Role,Client,Server,Alg]), {ok, Alg}. select_encrypt_decrypt(client, Client, Server) -> @@ -537,10 +585,15 @@ alg_final(SSH0) -> {ok,SSH6} = decompress_final(SSH5), SSH6. -select_all(CL, SL) -> +select_all(CL, SL) when length(CL) + length(SL) < 50 -> A = CL -- SL, %% algortihms only used by client %% algorithms used by client and server (client pref) - lists:map(fun(ALG) -> list_to_atom(ALG) end, (CL -- A)). + lists:map(fun(ALG) -> list_to_atom(ALG) end, (CL -- A)); +select_all(_CL, _SL) -> + throw(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR, + description = "Too many algorithms", + language = "en"}). + select([], []) -> none; diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index aaf0fa9905..cff695681e 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -56,6 +56,7 @@ all() -> ssh_daemon_minimal_remote_max_packet_size_option, ssh_msg_debug_fun_option_client, ssh_msg_debug_fun_option_server, + preferred_algorithms, id_string_no_opt_client, id_string_own_string_client, id_string_random_client, @@ -92,6 +93,7 @@ basic_tests() -> %%-------------------------------------------------------------------- init_per_suite(Config) -> + catch crypto:stop(), case catch crypto:start() of ok -> Config; @@ -289,7 +291,7 @@ exec_compressed(Config) when is_list(Config) -> UserDir = ?config(priv_dir, Config), {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},{user_dir, UserDir}, - {compression, zlib}, + {preferred_algorithms,[{compression, [zlib]}]}, {failfun, fun ssh_test_lib:failfun/2}]), ConnectionRef = @@ -1064,6 +1066,57 @@ ssh_daemon_minimal_remote_max_packet_size_option(Config) -> ssh:stop_daemon(Server). %%-------------------------------------------------------------------- +%% This test try every algorithm by connecting to an Erlang server +preferred_algorithms(Config) -> + SystemDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth + file:make_dir(UserDir), + + {Server, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, UserDir}, + {user_passwords, [{"vego", "morot"}]}, + {failfun, fun ssh_test_lib:failfun/2}]), + Available = ssh:default_algorithms(), + Tests = [[{Tag,[Alg]}] || {Tag, SubAlgs} <- Available, + is_atom(hd(SubAlgs)), + Alg <- SubAlgs] + ++ [[{Tag,[{T1,[A1]},{T2,[A2]}]}] || {Tag, [{T1,As1},{T2,As2}]} <- Available, + A1 <- As1, + A2 <- As2], + ct:log("TESTS: ~p",[Tests]), + [connect_exec_channel(Host,Port,PrefAlgs) || PrefAlgs <- Tests], + ssh:stop_daemon(Server). + + +connect_exec_channel(_Host, Port, Algs) -> + ct:log("Try ~p",[Algs]), + ConnectionRef = ssh_test_lib:connect(Port, [{silently_accept_hosts, true}, + {user_interaction, false}, + {user, "vego"}, + {password, "morot"}, + {preferred_algorithms,Algs} + ]), + chan_exec(ConnectionRef, "2*21.", <<"42\n">>), + ssh:close(ConnectionRef). + +chan_exec(ConnectionRef, Cmnd, Expected) -> + {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity), + success = ssh_connection:exec(ConnectionRef, ChannelId0,Cmnd, infinity), + Data0 = {ssh_cm, ConnectionRef, {data, ChannelId0, 0, Expected}}, + case ssh_test_lib:receive_exec_result(Data0) of + expected -> + ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId0); + {unexpected_msg,{ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}} + = ExitStatus0} -> + ct:pal("0: Collected data ~p", [ExitStatus0]), + ssh_test_lib:receive_exec_result(Data0, + ConnectionRef, ChannelId0); + Other0 -> + ct:fail(Other0) + end. + +%%-------------------------------------------------------------------- id_string_no_opt_client(Config) -> {Server, _Host, Port} = fake_daemon(Config), {error,_} = ssh:connect("localhost", Port, [], 1000), @@ -1233,12 +1286,15 @@ openssh_zlib_basic_test(Config) -> {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, {user_dir, UserDir}, + {preferred_algorithms,[{compression, ['[email protected]']}]}, {failfun, fun ssh_test_lib:failfun/2}]), ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, {user_dir, UserDir}, {user_interaction, false}, - {compression, openssh_zlib}]), + {preferred_algorithms,[{compression, ['[email protected]', + none]}]} + ]), ok = ssh:close(ConnectionRef), ssh:stop_daemon(Pid). diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl index db51f65509..f0c337cf2f 100644 --- a/lib/ssh/test/ssh_connection_SUITE.erl +++ b/lib/ssh/test/ssh_connection_SUITE.erl @@ -65,6 +65,7 @@ ptty() -> %%-------------------------------------------------------------------- init_per_suite(Config) -> + catch crypto:stop(), case catch crypto:start() of ok -> Config; diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl index cb74a27638..850b1cbf6b 100644 --- a/lib/ssh/test/ssh_sftp_SUITE.erl +++ b/lib/ssh/test/ssh_sftp_SUITE.erl @@ -49,6 +49,7 @@ all() -> init_per_suite(Config) -> + catch crypto:stop(), case (catch crypto:start()) of ok -> ssh:start(), diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl index 0ce8eec906..925b02a437 100644 --- a/lib/ssh/test/ssh_sftpd_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_SUITE.erl @@ -68,6 +68,7 @@ groups() -> %%-------------------------------------------------------------------- init_per_suite(Config) -> + catch crypto:stop(), case (catch crypto:start()) of ok -> DataDir = ?config(data_dir, Config), diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl index cc34cc0793..eac7575486 100644 --- a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl @@ -52,6 +52,7 @@ groups() -> init_per_suite(Config) -> catch ssh:stop(), + catch crypto:stop(), case catch crypto:start() of ok -> DataDir = ?config(data_dir, Config), diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl index a61fd2dd41..277e3a1b08 100644 --- a/lib/ssh/test/ssh_to_openssh_SUITE.erl +++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl @@ -60,6 +60,7 @@ groups() -> ]. init_per_suite(Config) -> + catch crypto:stop(), case catch crypto:start() of ok -> case gen_tcp:connect("localhost", 22, []) of @@ -166,9 +167,11 @@ erlang_client_openssh_server_exec_compressed() -> [{doc, "Test that compression option works"}]. erlang_client_openssh_server_exec_compressed(Config) when is_list(Config) -> + CompressAlgs = [zlib, '[email protected]',none], ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true}, {user_interaction, false}, - {compression, zlib}]), + {preferred_algorithms, + [{compression,CompressAlgs}]}]), {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), success = ssh_connection:exec(ConnectionRef, ChannelId, "echo testing", infinity), @@ -326,8 +329,11 @@ erlang_server_openssh_client_exec_compressed(Config) when is_list(Config) -> PrivDir = ?config(priv_dir, Config), KnownHosts = filename:join(PrivDir, "known_hosts"), +%% CompressAlgs = [zlib, '[email protected]'], % Does not work + CompressAlgs = [zlib], {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, - {compression, zlib}, + {preferred_algorithms, + [{compression, CompressAlgs}]}, {failfun, fun ssh_test_lib:failfun/2}]), ct:sleep(500), diff --git a/lib/ssh/test/ssh_unicode_SUITE.erl b/lib/ssh/test/ssh_unicode_SUITE.erl index cc916673b3..07d51335c6 100644 --- a/lib/ssh/test/ssh_unicode_SUITE.erl +++ b/lib/ssh/test/ssh_unicode_SUITE.erl @@ -55,6 +55,7 @@ all() -> init_per_suite(Config) -> + catch crypto:stop(), case {file:native_name_encoding(), (catch crypto:start())} of {utf8, ok} -> ssh:start(), diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 352563700b..fe0606b1a3 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -25,7 +25,23 @@ <file>notes.xml</file> </header> <p>This document describes the changes made to the SSL application.</p> - <section><title>SSL 6.0</title> + <section><title>SSL 6.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Terminate gracefully when receving bad input to premaster + secret calculation</p> + <p> + Own Id: OTP-12783</p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 6.0</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index 1476336039..d100e41930 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -1,14 +1,16 @@ %% -*- erlang -*- {"%VSN%", [ - {<<"6\\..*">>, [{restart_application, ssl}]}, - {<<"5\\..*">>, [{restart_application, ssl}]}, + {<<"6.0">>, [{load_module, ssl_handshake, soft_purge, soft_purge, []}]}, + {<<"5\\.3\\.[1-7]($|\\..*)">>, [{restart_application, ssl}]}, + {<<"5\\.[0-2]($|\\..*)">>, [{restart_application, ssl}]}, {<<"4\\..*">>, [{restart_application, ssl}]}, {<<"3\\..*">>, [{restart_application, ssl}]} ], [ - {<<"6\\..*">>, [{restart_application, ssl}]}, - {<<"5\\..*">>, [{restart_application, ssl}]}, + {<<"6.0">>, [{load_module, ssl_handshake, soft_purge, soft_purge, []}]}, + {<<"5\\.3\\.[1-7]($|\\..*)">>, [{restart_application, ssl}]}, + {<<"5\\.[0-2]($|\\..*)">>, [{restart_application, ssl}]}, {<<"4\\..*">>, [{restart_application, ssl}]}, {<<"3\\..*">>, [{restart_application, ssl}]} ] diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index b538fefe53..12a17cb6ac 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -476,19 +476,27 @@ update_handshake_history({Handshake0, _Prev}, Data) -> %% end. premaster_secret(OtherPublicDhKey, MyPrivateKey, #'DHParameter'{} = Params) -> - public_key:compute_key(OtherPublicDhKey, MyPrivateKey, Params); - + try + public_key:compute_key(OtherPublicDhKey, MyPrivateKey, Params) + catch + error:computation_failed -> + throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)) + end; premaster_secret(PublicDhKey, PrivateDhKey, #server_dh_params{dh_p = Prime, dh_g = Base}) -> - crypto:compute_key(dh, PublicDhKey, PrivateDhKey, [Prime, Base]); + try + crypto:compute_key(dh, PublicDhKey, PrivateDhKey, [Prime, Base]) + catch + error:computation_failed -> + throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)) + end; premaster_secret(#client_srp_public{srp_a = ClientPublicKey}, ServerKey, #srp_user{prime = Prime, verifier = Verifier}) -> case crypto:compute_key(srp, ClientPublicKey, ServerKey, {host, [Verifier, Prime, '6a']}) of error -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)); PremasterSecret -> PremasterSecret end; - premaster_secret(#server_srp_params{srp_n = Prime, srp_g = Generator, srp_s = Salt, srp_b = Public}, ClientKeys, {Username, Password}) -> case ssl_srp_primes:check_srp_params(Generator, Prime) of @@ -496,21 +504,19 @@ premaster_secret(#server_srp_params{srp_n = Prime, srp_g = Generator, srp_s = Sa DerivedKey = crypto:hash(sha, [Salt, crypto:hash(sha, [Username, <<$:>>, Password])]), case crypto:compute_key(srp, Public, ClientKeys, {user, [DerivedKey, Prime, Generator, '6a']}) of error -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)); PremasterSecret -> PremasterSecret end; _ -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER) + throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)) end; - premaster_secret(#client_rsa_psk_identity{ identity = PSKIdentity, exchange_keys = #encrypted_premaster_secret{premaster_secret = EncPMS} }, #'RSAPrivateKey'{} = Key, PSKLookup) -> PremasterSecret = premaster_secret(EncPMS, Key), psk_secret(PSKIdentity, PSKLookup, PremasterSecret); - premaster_secret(#server_dhe_psk_params{ hint = IdentityHint, dh_params = #server_dh_params{dh_y = PublicDhKey} = Params}, @@ -518,7 +524,6 @@ premaster_secret(#server_dhe_psk_params{ LookupFun) -> PremasterSecret = premaster_secret(PublicDhKey, PrivateDhKey, Params), psk_secret(IdentityHint, LookupFun, PremasterSecret); - premaster_secret({rsa_psk, PSKIdentity}, PSKLookup, RSAPremasterSecret) -> psk_secret(PSKIdentity, PSKLookup, RSAPremasterSecret). @@ -527,13 +532,10 @@ premaster_secret(#client_dhe_psk_identity{ dh_public = PublicDhKey}, PrivateKey, #'DHParameter'{} = Params, PSKLookup) -> PremasterSecret = premaster_secret(PublicDhKey, PrivateKey, Params), psk_secret(PSKIdentity, PSKLookup, PremasterSecret). - premaster_secret(#client_psk_identity{identity = PSKIdentity}, PSKLookup) -> psk_secret(PSKIdentity, PSKLookup); - premaster_secret({psk, PSKIdentity}, PSKLookup) -> psk_secret(PSKIdentity, PSKLookup); - premaster_secret(#'ECPoint'{} = ECPoint, #'ECPrivateKey'{} = ECDHKeys) -> public_key:compute_key(ECPoint, ECDHKeys); premaster_secret(EncSecret, #'RSAPrivateKey'{} = RSAPrivateKey) -> @@ -2036,7 +2038,7 @@ psk_secret(PSKIdentity, PSKLookup) -> #alert{} = Alert -> Alert; _ -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER) + throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)) end. psk_secret(PSKIdentity, PSKLookup, PremasterSecret) -> @@ -2048,7 +2050,7 @@ psk_secret(PSKIdentity, PSKLookup, PremasterSecret) -> #alert{} = Alert -> Alert; _ -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER) + throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)) end. handle_psk_identity(_PSKIdentity, LookupFun) diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl index a22af6b960..d23b42ace5 100644 --- a/lib/ssl/src/ssl_tls_dist_proxy.erl +++ b/lib/ssl/src/ssl_tls_dist_proxy.erl @@ -227,7 +227,10 @@ loop_conn_setup(World, Erts) -> {tcp_closed, Erts} -> ssl:close(World); {ssl_closed, World} -> - gen_tcp:close(Erts) + gen_tcp:close(Erts); + {ssl_error, World, _} -> + + ssl:close(World) end. loop_conn(World, Erts) -> @@ -241,7 +244,9 @@ loop_conn(World, Erts) -> {tcp_closed, Erts} -> ssl:close(World); {ssl_closed, World} -> - gen_tcp:close(Erts) + gen_tcp:close(Erts); + {ssl_error, World, _} -> + ssl:close(World) end. get_ssl_options(Type) -> diff --git a/lib/stdlib/doc/src/orddict.xml b/lib/stdlib/doc/src/orddict.xml index ec1e43f29c..c853b402d4 100644 --- a/lib/stdlib/doc/src/orddict.xml +++ b/lib/stdlib/doc/src/orddict.xml @@ -48,8 +48,11 @@ <datatypes> <datatype> - <name name="orddict"/> - <desc><p>As returned by new/0.</p></desc> + <name name="orddict" n_vars="2"/> + <desc><p>Dictionary as returned by <c>new/0</c>.</p></desc> + </datatype> + <datatype> + <name name="orddict" n_vars="0"/> </datatype> </datatypes> diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml index ffac1c0bd7..6ff477a42d 100644 --- a/lib/stdlib/doc/src/supervisor.xml +++ b/lib/stdlib/doc/src/supervisor.xml @@ -386,9 +386,15 @@ added to the supervisor and the function returns the same value.</p> <p>If the child process start function returns <c>ignore</c>, - the child specification is added to the supervisor, the pid - is set to <c>undefined</c>, and the function returns - <c>{ok,undefined}</c>.</p> + the child specification is added to the supervisor (unless the + supervisor is a <c>simple_one_for_one</c> supervisor, see below), + the pid is set to <c>undefined</c> and the function returns + <c>{ok,undefined}</c>. + </p> + <p>In the case of a <c>simple_one_for_one</c> supervisor, when a child + process start function returns <c>ignore</c> the functions returns + <c>{ok,undefined}</c> and no child is added to the supervisor. + </p> <p>If the child process start function returns an error tuple or an erroneous value, or if it fails, the child specification is discarded, and the function returns <c>{error,Error}</c> where diff --git a/lib/stdlib/src/erl_anno.erl b/lib/stdlib/src/erl_anno.erl index 9fb767fc93..fa83375c34 100644 --- a/lib/stdlib/src/erl_anno.erl +++ b/lib/stdlib/src/erl_anno.erl @@ -150,9 +150,7 @@ is_filename(T) -> is_list(T) orelse is_binary(T). is_string(T) -> - try lists:all(fun(C) when is_integer(C), C >= 0 -> true end, T) - catch _:_ -> false - end. + is_list(T). -spec column(Anno) -> column() | 'undefined' when Anno :: anno(). diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 714c260bda..b13848c501 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -609,22 +609,30 @@ pack_warnings(Ws) -> %% add_warning(ErrorDescriptor, State) -> State' %% add_warning(Line, Error, State) -> State' -add_error(E, St) -> St#lint{errors=[{St#lint.file,E}|St#lint.errors]}. +add_error(E, St) -> add_lint_error(E, St#lint.file, St). add_error(Anno, E, St) -> - {File,Location} = loc(Anno), - add_error({Location,erl_lint,E}, St#lint{file = File}). + {File,Location} = loc(Anno, St), + add_lint_error({Location,erl_lint,E}, File, St). -add_warning(W, St) -> St#lint{warnings=[{St#lint.file,W}|St#lint.warnings]}. +add_lint_error(E, File, St) -> + St#lint{errors=[{File,E}|St#lint.errors]}. + +add_warning(W, St) -> add_lint_warning(W, St#lint.file, St). add_warning(FileLine, W, St) -> - {File,Location} = loc(FileLine), - add_warning({Location,erl_lint,W}, St#lint{file = File}). + {File,Location} = loc(FileLine, St), + add_lint_warning({Location,erl_lint,W}, File, St). + +add_lint_warning(W, File, St) -> + St#lint{warnings=[{File,W}|St#lint.warnings]}. -loc(Anno) -> - File = erl_anno:file(Anno), +loc(Anno, St) -> Location = erl_anno:location(Anno), - {File,Location}. + case erl_anno:file(Anno) of + undefined -> {St#lint.file,Location}; + File -> {File,Location} + end. %% forms([Form], State) -> State' @@ -667,11 +675,21 @@ eval_file_attribute(Forms, St) -> eval_file_attr([{attribute,_L,file,{File,_Line}}=Form | Forms], _File) -> [Form | eval_file_attr(Forms, File)]; eval_file_attr([Form0 | Forms], File) -> - Form = set_file(Form0, File), + Form = set_form_file(Form0, File), [Form | eval_file_attr(Forms, File)]; eval_file_attr([], _File) -> []. +%% Sets the file only on the form. This is used on post-traversal. +%% For the remaining of the AST we rely on #lint.file. + +set_form_file({attribute,L,K,V}, File) -> + {attribute,erl_anno:set_file(File, L),K,V}; +set_form_file({function,L,N,A,C}, File) -> + {function,erl_anno:set_file(File, L),N,A,C}; +set_form_file(Form, _File) -> + Form. + set_file(T, File) -> F = fun(Anno) -> erl_anno:set_file(File, Anno) end, erl_parse:map_anno(F, T). @@ -796,10 +814,10 @@ 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,A,compile,nowarn_bif_clash} <- Forms, - {_,L} <- [loc(A)] ], + {_,L} <- [loc(A, St0)] ], Errors1 = [ {St0#lint.file,{L,erl_lint,disallowed_nowarn_bif_clash}} || {attribute,A,compile,{nowarn_bif_clash, {_,_}}} <- Forms, - {_,L} <- [loc(A)] ], + {_,L} <- [loc(A, St0)] ], Disabled = (not is_warn_enabled(bif_clash, St0)), Errors = if Disabled andalso Errors0 =:= [] -> @@ -924,7 +942,7 @@ behaviour_conflicting(AllBfs, St) -> behaviour_add_conflicts(R, St). behaviour_add_conflicts([{Cb,[{FirstLoc,FirstB}|Cs]}|T], St0) -> - FirstL = element(2, loc(FirstLoc)), + FirstL = element(2, loc(FirstLoc, St0)), St = behaviour_add_conflict(Cs, Cb, FirstL, FirstB, St0), behaviour_add_conflicts(T, St); behaviour_add_conflicts([], St) -> St. @@ -1142,7 +1160,7 @@ check_unused_records(Forms, St0) -> end, St0#lint.records, UsedRecords), Unused = [{Name,FileLine} || {Name,{FileLine,_Fields}} <- dict:to_list(URecs), - element(1, loc(FileLine)) =:= FirstFile], + element(1, loc(FileLine, St0)) =:= FirstFile], foldl(fun ({N,L}, St) -> add_warning(L, {unused_record, N}, St) end, St0, Unused); @@ -1335,14 +1353,15 @@ check_on_load(St) -> St. -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) -> +call_function(Line, F, A, #lint{usage=Usage0,called=Cd,func=Func,file=File}=St) -> #usage{calls = Cs} = Usage0, NA = {F,A}, Usage = case Cs of undefined -> Usage0; _ -> Usage0#usage{calls=dict:append(Func, NA, Cs)} end, - St#lint{called=[{NA,Line}|Cd], usage=Usage}. + Anno = erl_anno:set_file(File, Line), + St#lint{called=[{NA,Anno}|Cd], usage=Usage}. %% function(Line, Name, Arity, Clauses, State) -> State. @@ -2121,7 +2140,7 @@ expr({'receive',Line,Cs,To,ToEs}, Vt, St0) -> {Cvt,St3} = icrt_clauses(Cs, Vt, St2), %% Csvts = [vtnew(Tevt, Vt)|Cvt], %This is just NEW variables! Csvts = [Tevt|Cvt], - Rvt = icrt_export(Csvts, Vt, {'receive',Line}), + Rvt = icrt_export(Csvts, Vt, {'receive',Line}, St3), {vtmerge([Tvt,Tevt,Rvt]),St3}; expr({'fun',Line,Body}, Vt, St) -> %%No one can think funs export! @@ -2824,10 +2843,9 @@ check_record_types([{type, _, field_type, [{atom, AL, FName}, Type]}|Left], check_record_types([], _Name, _DefFields, SeenVars, St, _SeenFields) -> {SeenVars, St}. -used_type(TypePair, L, St) -> - Usage = St#lint.usage, +used_type(TypePair, L, #lint{usage = Usage, file = File} = St) -> OldUsed = Usage#usage.used_types, - UsedTypes = dict:store(TypePair, L, OldUsed), + UsedTypes = dict:store(TypePair, erl_anno:set_file(File, L), OldUsed), St#lint{usage=Usage#usage{used_types=UsedTypes}}. is_default_type({Name, NumberOfTypeVariables}) -> @@ -2982,7 +3000,7 @@ check_unused_types(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) -> UsedTypes = gb_sets:from_list(L), FoldFun = fun(Type, #typeinfo{line = FileLine}, AccSt) -> - case loc(FileLine) of + case loc(FileLine, AccSt) of {FirstFile, _} -> case gb_sets:is_member(Type, UsedTypes) of true -> AccSt; @@ -3020,7 +3038,7 @@ check_local_opaque_types(St) -> icrt_clauses(Cs, In, Vt, St0) -> {Csvt,St1} = icrt_clauses(Cs, Vt, St0), - UpdVt = icrt_export(Csvt, Vt, In), + UpdVt = icrt_export(Csvt, Vt, In, St1), {UpdVt,St1}. %% icrt_clauses(Clauses, ImportVarTable, State) -> @@ -3037,8 +3055,8 @@ icrt_clause({clause,_Line,H,G,B}, Vt0, St0) -> {Bvt,St3} = exprs(B, vtupdate(Vt2, Vt0), St2), {vtupdate(Bvt, Vt2),St3}. -icrt_export(Vts, Vt, {Tag,Attrs}) -> - {_File,Loc} = loc(Attrs), +icrt_export(Vts, Vt, {Tag,Attrs}, St) -> + {_File,Loc} = loc(Attrs, St), icrt_export(lists:merge(Vts), Vt, {Tag,Loc}, length(Vts), []). icrt_export([{V,{{export,_},_,_}}|Vs0], [{V,{{export,_}=S0,_,Ls}}|Vt], @@ -3395,7 +3413,7 @@ vtupdate(Uvt, Vt0) -> %% Return all new variables in UpdVarTable as unsafe. vtunsafe({Tag,FileLine}, Uvt, Vt) -> - {_File,Line} = loc(FileLine), + Line = erl_anno:location(FileLine), [{V,{{unsafe,{Tag,Line}},U,Ls}} || {V,{_,U,Ls}} <- vtnew(Uvt, Vt)]. %% vtmerge(VarTable, VarTable) -> VarTable. diff --git a/lib/stdlib/src/orddict.erl b/lib/stdlib/src/orddict.erl index af5d917840..cbdf25d757 100644 --- a/lib/stdlib/src/orddict.erl +++ b/lib/stdlib/src/orddict.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2015. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -25,11 +25,13 @@ -export([store/3,append/3,append_list/3,update/3,update/4,update_counter/3]). -export([fold/3,map/2,filter/2,merge/3]). --export_type([orddict/0]). +-export_type([orddict/0, orddict/2]). %%--------------------------------------------------------------------------- --type orddict() :: [{Key :: term(), Value :: term()}]. +-type orddict() :: orddict(_, _). + +-type orddict(Key, Value) :: [{Key, Value}]. %%--------------------------------------------------------------------------- @@ -38,8 +40,7 @@ new() -> []. -spec is_key(Key, Orddict) -> boolean() when - Key :: term(), - Orddict :: orddict(). + Orddict :: orddict(Key, Value :: term()). is_key(Key, [{K,_}|_]) when Key < K -> false; is_key(Key, [{K,_}|Dict]) when Key > K -> is_key(Key, Dict); @@ -47,14 +48,14 @@ is_key(_Key, [{_K,_Val}|_]) -> true; %Key == K is_key(_, []) -> false. -spec to_list(Orddict) -> List when - Orddict :: orddict(), - List :: [{Key :: term(), Value :: term()}]. + Orddict :: orddict(Key, Value), + List :: [{Key, Value}]. to_list(Dict) -> Dict. -spec from_list(List) -> Orddict when - List :: [{Key :: term(), Value :: term()}], - Orddict :: orddict(). + List :: [{Key, Value}], + Orddict :: orddict(Key, Value). from_list([]) -> []; from_list([{_,_}]=Pair) -> Pair; @@ -73,17 +74,13 @@ is_empty([]) -> true; is_empty([_|_]) -> false. -spec fetch(Key, Orddict) -> Value when - Key :: term(), - Value :: term(), - Orddict :: orddict(). + Orddict :: orddict(Key, Value). fetch(Key, [{K,_}|D]) when Key > K -> fetch(Key, D); fetch(Key, [{K,Value}|_]) when Key == K -> Value. -spec find(Key, Orddict) -> {'ok', Value} | 'error' when - Key :: term(), - Orddict :: orddict(), - Value :: term(). + Orddict :: orddict(Key, Value). find(Key, [{K,_}|_]) when Key < K -> error; find(Key, [{K,_}|D]) when Key > K -> find(Key, D); @@ -91,17 +88,16 @@ find(_Key, [{_K,Value}|_]) -> {ok,Value}; %Key == K find(_, []) -> error. -spec fetch_keys(Orddict) -> Keys when - Orddict :: orddict(), - Keys :: [term()]. + Orddict :: orddict(Key, Value :: term()), + Keys :: [Key]. fetch_keys([{Key,_}|Dict]) -> [Key|fetch_keys(Dict)]; fetch_keys([]) -> []. -spec erase(Key, Orddict1) -> Orddict2 when - Key :: term(), - Orddict1 :: orddict(), - Orddict2 :: orddict(). + Orddict1 :: orddict(Key, Value), + Orddict2 :: orddict(Key, Value). erase(Key, [{K,_}=E|Dict]) when Key < K -> [E|Dict]; erase(Key, [{K,_}=E|Dict]) when Key > K -> @@ -110,10 +106,8 @@ erase(_Key, [{_K,_Val}|Dict]) -> Dict; %Key == K erase(_, []) -> []. -spec store(Key, Value, Orddict1) -> Orddict2 when - Key :: term(), - Value :: term(), - Orddict1 :: orddict(), - Orddict2 :: orddict(). + Orddict1 :: orddict(Key, Value), + Orddict2 :: orddict(Key, Value). store(Key, New, [{K,_}|_]=Dict) when Key < K -> [{Key,New}|Dict]; @@ -124,10 +118,8 @@ store(Key, New, [{_K,_Old}|Dict]) -> %Key == K store(Key, New, []) -> [{Key,New}]. -spec append(Key, Value, Orddict1) -> Orddict2 when - Key :: term(), - Value :: term(), - Orddict1 :: orddict(), - Orddict2 :: orddict(). + Orddict1 :: orddict(Key, Value), + Orddict2 :: orddict(Key, Value). append(Key, New, [{K,_}|_]=Dict) when Key < K -> [{Key,[New]}|Dict]; @@ -138,10 +130,9 @@ append(Key, New, [{_K,Old}|Dict]) -> %Key == K append(Key, New, []) -> [{Key,[New]}]. -spec append_list(Key, ValList, Orddict1) -> Orddict2 when - Key :: term(), - ValList :: [Value :: term()], - Orddict1 :: orddict(), - Orddict2 :: orddict(). + ValList :: [Value], + Orddict1 :: orddict(Key, Value), + Orddict2 :: orddict(Key, Value). append_list(Key, NewList, [{K,_}|_]=Dict) when Key < K -> [{Key,NewList}|Dict]; @@ -153,10 +144,9 @@ append_list(Key, NewList, []) -> [{Key,NewList}]. -spec update(Key, Fun, Orddict1) -> Orddict2 when - Key :: term(), - Fun :: fun((Value1 :: term()) -> Value2 :: term()), - Orddict1 :: orddict(), - Orddict2 :: orddict(). + Fun :: fun((Value1 :: Value) -> Value2 :: Value), + Orddict1 :: orddict(Key, Value), + Orddict2 :: orddict(Key, Value). update(Key, Fun, [{K,_}=E|Dict]) when Key > K -> [E|update(Key, Fun, Dict)]; @@ -164,11 +154,10 @@ update(Key, Fun, [{K,Val}|Dict]) when Key == K -> [{Key,Fun(Val)}|Dict]. -spec update(Key, Fun, Initial, Orddict1) -> Orddict2 when - Key :: term(), - Initial :: term(), - Fun :: fun((Value1 :: term()) -> Value2 :: term()), - Orddict1 :: orddict(), - Orddict2 :: orddict(). + Initial :: Value, + Fun :: fun((Value1 :: Value) -> Value2 :: Value), + Orddict1 :: orddict(Key, Value), + Orddict2 :: orddict(Key, Value). update(Key, _, Init, [{K,_}|_]=Dict) when Key < K -> [{Key,Init}|Dict]; @@ -179,10 +168,9 @@ update(Key, Fun, _Init, [{_K,Val}|Dict]) -> %Key == K update(Key, _, Init, []) -> [{Key,Init}]. -spec update_counter(Key, Increment, Orddict1) -> Orddict2 when - Key :: term(), - Increment :: number(), - Orddict1 :: orddict(), - Orddict2 :: orddict(). + Orddict1 :: orddict(Key, Value), + Orddict2 :: orddict(Key, Value), + Increment :: number(). update_counter(Key, Incr, [{K,_}|_]=Dict) when Key < K -> [{Key,Incr}|Dict]; @@ -193,28 +181,30 @@ update_counter(Key, Incr, [{_K,Val}|Dict]) -> %Key == K update_counter(Key, Incr, []) -> [{Key,Incr}]. -spec fold(Fun, Acc0, Orddict) -> Acc1 when - Fun :: fun((Key :: term(), Value :: term(), AccIn :: term()) -> AccOut :: term()), - Acc0 :: term(), - Acc1 :: term(), - Orddict :: orddict(). + Fun :: fun((Key, Value, AccIn) -> AccOut), + Orddict :: orddict(Key, Value), + Acc0 :: Acc, + Acc1 :: Acc, + AccIn :: Acc, + AccOut :: Acc. fold(F, Acc, [{Key,Val}|D]) -> fold(F, F(Key, Val, Acc), D); fold(F, Acc, []) when is_function(F, 3) -> Acc. -spec map(Fun, Orddict1) -> Orddict2 when - Fun :: fun((Key :: term(), Value1 :: term()) -> Value2 :: term()), - Orddict1 :: orddict(), - Orddict2 :: orddict(). + Fun :: fun((Key, Value1) -> Value2), + Orddict1 :: orddict(Key, Value1), + Orddict2 :: orddict(Key, Value2). map(F, [{Key,Val}|D]) -> [{Key,F(Key, Val)}|map(F, D)]; map(F, []) when is_function(F, 2) -> []. -spec filter(Pred, Orddict1) -> Orddict2 when - Pred :: fun((Key :: term(), Value :: term()) -> boolean()), - Orddict1 :: orddict(), - Orddict2 :: orddict(). + Pred :: fun((Key, Value) -> boolean()), + Orddict1 :: orddict(Key, Value), + Orddict2 :: orddict(Key, Value). filter(F, [{Key,Val}=E|D]) -> case F(Key, Val) of @@ -224,10 +214,10 @@ filter(F, [{Key,Val}=E|D]) -> filter(F, []) when is_function(F, 2) -> []. -spec merge(Fun, Orddict1, Orddict2) -> Orddict3 when - Fun :: fun((Key :: term(), Value1 :: term(), Value2 :: term()) -> Value :: term()), - Orddict1 :: orddict(), - Orddict2 :: orddict(), - Orddict3 :: orddict(). + Fun :: fun((Key, Value1, Value2) -> Value), + Orddict1 :: orddict(Key, Value1), + Orddict2 :: orddict(Key, Value2), + Orddict3 :: orddict(Key, Value). merge(F, [{K1,_}=E1|D1], [{K2,_}=E2|D2]) when K1 < K2 -> [E1|merge(F, D1, [E2|D2])]; diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src index ee87a8ddb2..b3569c2848 100644 --- a/lib/stdlib/src/stdlib.appup.src +++ b/lib/stdlib/src/stdlib.appup.src @@ -1,7 +1,7 @@ %% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2014. All Rights Reserved. +%% Copyright Ericsson AB 1999-2015. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -17,9 +17,7 @@ %% %CopyrightEnd% {"%VSN%", %% Up from - max one major revision back - [{<<"2\\.[1-3](\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1-17.3 - {<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}], %% 17.0 + [{<<"2\\.[0-4](\\.[0-9]+)*">>,[restart_new_emulator]}], %% 17.0-17.5 %% Down to - max one major revision back - [{<<"2\\.[1-3](\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1-17.3 - {<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}] %% 17.0 + [{<<"2\\.[0-4](\\.[0-9]+)*">>,[restart_new_emulator]}] %% 17.0-17.5 }. diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl index 67655b1145..1d7396adee 100644 --- a/lib/stdlib/src/supervisor.erl +++ b/lib/stdlib/src/supervisor.erl @@ -381,7 +381,7 @@ handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) -> #child{mfargs = {M, F, A}} = Child, Args = A ++ EArgs, case do_start_child_i(M, F, Args) of - {ok, undefined} when Child#child.restart_type =:= temporary -> + {ok, undefined} -> {reply, {ok, undefined}, State}; {ok, Pid} -> NState = save_dynamic_child(Child#child.restart_type, Pid, Args, State), diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl index 3c67bd67c6..f986c0081d 100644 --- a/lib/stdlib/src/zip.erl +++ b/lib/stdlib/src/zip.erl @@ -24,7 +24,7 @@ list_dir/1, list_dir/2, table/1, table/2, t/1, tt/1]). -%% unzipping peicemeal +%% unzipping piecemeal -export([openzip_open/1, openzip_open/2, openzip_get/1, openzip_get/2, openzip_t/1, openzip_tt/1, diff --git a/lib/stdlib/test/binary_module_SUITE.erl b/lib/stdlib/test/binary_module_SUITE.erl index 5248870744..8d26c77c9b 100644 --- a/lib/stdlib/test/binary_module_SUITE.erl +++ b/lib/stdlib/test/binary_module_SUITE.erl @@ -993,43 +993,51 @@ random_parts(X,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}), + put(success_counter,0), + random:seed({1271,769940,559934}), + Nr = {1,40}, + Hr = {30,1000}, + I1 = 1500, + I2 = 5, + do_random_match_comp(I1,Nr,Hr), io:format("Number of successes: ~p~n",[get(success_counter)]), - ?line do_random_match_comp2(5000,{1,40},{30,1000}), + do_random_match_comp2(I1,Nr,Hr), io:format("Number of successes: ~p~n",[get(success_counter)]), - ?line do_random_match_comp3(5000,{1,40},{30,1000}), + do_random_match_comp3(I1,Nr,Hr), io:format("Number of successes: ~p~n",[get(success_counter)]), - ?line do_random_match_comp4(5000,{1,40},{30,1000}), + do_random_match_comp4(I1,Nr,Hr), io:format("Number of successes: ~p~n",[get(success_counter)]), - ?line do_random_matches_comp(5000,{1,40},{30,1000}), + do_random_matches_comp(I1,Nr,Hr), io:format("Number of successes: ~p~n",[get(success_counter)]), - ?line do_random_matches_comp2(5000,{1,40},{30,1000}), + do_random_matches_comp2(I1,Nr,Hr), 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), + do_random_matches_comp3(I2,Nr,Hr), + erts_debug:set_internal_state(available_internal_state,true), + io:format("oldlimit: ~p~n",[ erts_debug:set_internal_state(binary_loop_limit,100)]), + do_random_match_comp(I1,Nr,Hr), + do_random_matches_comp3(I2,Nr,Hr), + io:format("limit was: ~p~n",[ erts_debug:set_internal_state(binary_loop_limit,default)]), + 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}), + put(success_counter,0), + random:seed({1271,769940,559934}), + Nr = {1,40}, + Hr = {30,1000}, + I1 = 1500, + do_random_split_comp(I1,Nr,Hr), io:format("Number of successes: ~p~n",[get(success_counter)]), - ?line do_random_replace_comp(5000,{1,40},{30,1000}), + do_random_replace_comp(I1,Nr,Hr), io:format("Number of successes: ~p~n",[get(success_counter)]), - ?line do_random_split_comp2(5000,{1,40},{30,1000}), + do_random_split_comp2(I1,Nr,Hr), io:format("Number of successes: ~p~n",[get(success_counter)]), - ?line do_random_replace_comp2(5000,{1,40},{30,1000}), + do_random_replace_comp2(I1,Nr,Hr), 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) -> diff --git a/lib/stdlib/test/erl_anno_SUITE.erl b/lib/stdlib/test/erl_anno_SUITE.erl index 7632fbd324..d024f6907d 100644 --- a/lib/stdlib/test/erl_anno_SUITE.erl +++ b/lib/stdlib/test/erl_anno_SUITE.erl @@ -89,7 +89,6 @@ is_anno(_Config) -> false = erl_anno:is_anno([{generated,true}]), false = erl_anno:is_anno([{location,1},{file,nofile}]), false = erl_anno:is_anno([{location,1},{text,notext}]), - false = erl_anno:is_anno([{location,1},{text,[a,b,c]}]), true = erl_anno:is_anno(erl_anno:new(1)), A0 = erl_anno:new({1, 17}), diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index a88843bb6e..fff6b11a38 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -3061,13 +3061,13 @@ time_lookup(Config) when is_list(Config) -> "~p ets lookups/s",[Values]))}. time_lookup_do(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, - [10000,ets,lookup,[Tab,{a,key}]]), - ?line true = ets:delete(Tab), - round(10000 / Time). % lookups/s + Tab = ets_new(foo,Opts), + fill_tab(Tab,foo), + ets:insert(Tab,{{a,key},foo}), + {Time,_} = ?t:timecall(test_server,do_times, + [100000,ets,lookup,[Tab,{a,key}]]), + true = ets:delete(Tab), + round(100000 / Time). % lookups/s badlookup(doc) -> ["Check proper return values from bad lookups in existing/non existing " diff --git a/lib/stdlib/test/io_proto_SUITE.erl b/lib/stdlib/test/io_proto_SUITE.erl index 858a78b1d2..78432789cd 100644 --- a/lib/stdlib/test/io_proto_SUITE.erl +++ b/lib/stdlib/test/io_proto_SUITE.erl @@ -482,7 +482,7 @@ unicode_options_gen(Config) when is_list(Config) -> PrivDir = ?config(priv_dir, Config), AllModes = [utf8,utf16,{utf16,big},{utf16,little}, utf32,{utf32,big},{utf32,little}], - FSize = 17*1024, + FSize = 9*1024, NumItersRead = 2, NumItersWrite = 2, Dir = filename:join(PrivDir, "GENDATA1"), diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl index 348c308f5d..56829fac5c 100644 --- a/lib/stdlib/test/qlc_SUITE.erl +++ b/lib/stdlib/test/qlc_SUITE.erl @@ -6120,7 +6120,7 @@ otp_6964(Config) when is_list(Config) -> lists:flatten(qlc:format_error(ErrReply)), qlc_SUITE:install_error_logger(), 20000 = length(F(warning_msg)), - {error, joining} = qlc_SUITE:read_error_logger(), + {warning, joining} = qlc_SUITE:read_error_logger(), 20000 = length(F(info_msg)), {info, joining} = qlc_SUITE:read_error_logger(), 20000 = length(F(error_msg)), @@ -6155,8 +6155,8 @@ otp_6964(Config) when is_list(Config) -> {error, caching} = qlc_SUITE:read_error_logger(), {error, caching} = qlc_SUITE:read_error_logger(), 1 = length(F(warning_msg)), - {error, caching} = qlc_SUITE:read_error_logger(), - {error, caching} = qlc_SUITE:read_error_logger(), + {warning, caching} = qlc_SUITE:read_error_logger(), + {warning, caching} = qlc_SUITE:read_error_logger(), 1 = length(F(info_msg)), {info, caching} = qlc_SUITE:read_error_logger(), {info, caching} = qlc_SUITE:read_error_logger(), @@ -6188,7 +6188,7 @@ otp_6964(Config) when is_list(Config) -> L = F(info_msg), {info, sorting} = qlc_SUITE:read_error_logger(), L = F(warning_msg), - {error, sorting} = qlc_SUITE:read_error_logger(), + {warning, sorting} = qlc_SUITE:read_error_logger(), qlc_SUITE:uninstall_error_logger(), ets:delete(E1), ets:delete(E2)">>], @@ -6215,7 +6215,7 @@ otp_6964(Config) when is_list(Config) -> R = lists:sort(F(error_msg)), {error, caching} = qlc_SUITE:read_error_logger(), R = lists:sort(F(warning_msg)), - {error, caching} = qlc_SUITE:read_error_logger(), + {warning, caching} = qlc_SUITE:read_error_logger(), qlc_SUITE:uninstall_error_logger(), ErrReply = F(not_allowed), {error,qlc,{tmpdir_usage,caching}} = ErrReply, @@ -8178,6 +8178,8 @@ read_error_logger() -> {error, Why}; {info, Why} -> {info, Why}; + {warning, Why} -> + {warning, Why}; {error, Pid, Tuple} -> {error, Pid, Tuple} after 1000 -> @@ -8192,8 +8194,7 @@ read_error_logger() -> init(Tester) -> {ok, Tester}. -handle_event({error, _GL, {_Pid, _Msg, [Why, _]}}, Tester) - when is_atom(Why) -> +handle_event({error, _GL, {_Pid, _Msg, [Why, _]}}, Tester) when is_atom(Why) -> Tester ! {error, Why}, {ok, Tester}; handle_event({error, _GL, {_Pid, _Msg, [P, T]}}, Tester) when is_pid(P) -> @@ -8202,6 +8203,9 @@ handle_event({error, _GL, {_Pid, _Msg, [P, T]}}, Tester) when is_pid(P) -> handle_event({info_msg, _GL, {_Pid, _Msg, [Why, _]}}, Tester) -> Tester ! {info, Why}, {ok, Tester}; +handle_event({warning_msg, _GL, {_Pid, _Msg, [Why, _]}}, Tester) when is_atom(Why) -> + Tester ! {warning, Why}, + {ok, Tester}; handle_event(_Event, State) -> {ok, State}. diff --git a/lib/stdlib/test/rand_SUITE.erl b/lib/stdlib/test/rand_SUITE.erl index 9a1f37aa75..39ce1bd89a 100644 --- a/lib/stdlib/test/rand_SUITE.erl +++ b/lib/stdlib/test/rand_SUITE.erl @@ -33,7 +33,7 @@ -include_lib("test_server/include/test_server.hrl"). % Default timetrap timeout (set in init_per_testcase). --define(default_timeout, ?t:minutes(1)). +-define(default_timeout, ?t:minutes(3)). -define(LOOP, 1000000). init_per_testcase(_Case, Config) -> diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl index 9dcf19707c..015b09f35e 100644 --- a/lib/stdlib/test/supervisor_SUITE.erl +++ b/lib/stdlib/test/supervisor_SUITE.erl @@ -37,6 +37,7 @@ sup_start_ignore_child/1, sup_start_ignore_temporary_child/1, sup_start_ignore_temporary_child_start_child/1, sup_start_ignore_temporary_child_start_child_simple/1, + sup_start_ignore_permanent_child_start_child_simple/1, sup_start_error_return/1, sup_start_fail/1, sup_start_map/1, sup_start_map_faulty_specs/1, sup_stop_infinity/1, sup_stop_timeout/1, sup_stop_brutal_kill/1, @@ -99,6 +100,7 @@ groups() -> sup_start_ignore_child, sup_start_ignore_temporary_child, sup_start_ignore_temporary_child_start_child, sup_start_ignore_temporary_child_start_child_simple, + sup_start_ignore_permanent_child_start_child_simple, sup_start_error_return, sup_start_fail]}, {sup_start_map, [], [sup_start_map, sup_start_map_faulty_specs]}, @@ -250,6 +252,27 @@ sup_start_ignore_temporary_child_start_child_simple(Config) [1,1,0,1] = get_child_counts(sup_test). %%------------------------------------------------------------------------- +%% Tests what happens if child's init-callback returns ignore for a +%% permanent child when child is started with start_child/2, and the +%% supervisor is simple_one_for_one. +%% Child spec shall NOT be saved!!! +sup_start_ignore_permanent_child_start_child_simple(Config) + when is_list(Config) -> + process_flag(trap_exit, true), + Child1 = {child1, {supervisor_1, start_child, [ignore]}, + permanent, 1000, worker, []}, + {ok, Pid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child1]}}), + + {ok, undefined} = supervisor:start_child(sup_test, []), + {ok, CPid2} = supervisor:start_child(sup_test, []), + + [{undefined, CPid2, worker, []}] = supervisor:which_children(sup_test), + [1,1,0,1] = get_child_counts(sup_test), + + %% Regression test: check that the supervisor terminates without error. + exit(Pid, shutdown), + check_exit_reason(Pid, shutdown). +%%------------------------------------------------------------------------- %% Tests what happens if init-callback returns a invalid value. sup_start_error_return(Config) when is_list(Config) -> process_flag(trap_exit, true), diff --git a/lib/stdlib/test/unicode_SUITE.erl b/lib/stdlib/test/unicode_SUITE.erl index 613be99ccd..9f5d485df6 100644 --- a/lib/stdlib/test/unicode_SUITE.erl +++ b/lib/stdlib/test/unicode_SUITE.erl @@ -87,8 +87,9 @@ ex_binaries_errors_utf8(Config) when is_list(Config) -> %% Now, try with longer binary (trapping) BrokenPart = list_to_binary(lists:seq(128,255)), BrokenSz = byte_size(BrokenPart), + Seq255 = lists:seq(1,255), [ begin - OKList = lists:flatten(lists:duplicate(N,lists:seq(1,255))), + OKList = lists:flatten(lists:duplicate(N,Seq255)), OKBin = unicode:characters_to_binary(OKList), OKLen = length(OKList), %% Copy to avoid that the binary get's writable diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk index f57f31c8de..a1f2a946b1 100644 --- a/lib/stdlib/vsn.mk +++ b/lib/stdlib/vsn.mk @@ -1 +1 @@ -STDLIB_VSN = 2.4 +STDLIB_VSN = 2.5 diff --git a/lib/syntax_tools/src/Makefile b/lib/syntax_tools/src/Makefile index 2c565cee7f..2e91adf8af 100644 --- a/lib/syntax_tools/src/Makefile +++ b/lib/syntax_tools/src/Makefile @@ -75,7 +75,8 @@ $(EBIN)/%.$(EMULATOR):%.erl # special rules and dependencies to apply the transform to itself $(EBIN)/merl_transform.beam: $(EBIN)/merl.beam ./merl_transform.beam \ - ../include/merl.hrl + ../include/merl.hrl \ + $(EBIN)/erl_syntax.beam $(EBIN)/erl_syntax_lib.beam ./merl_transform.beam: ./merl_transform.erl $(EBIN)/merl.beam \ ../include/merl.hrl $(V_ERLC) -DMERL_NO_TRANSFORM $(ERL_COMPILE_FLAGS) -o ./ $< diff --git a/lib/syntax_tools/src/syntax_tools.app.src b/lib/syntax_tools/src/syntax_tools.app.src index e207901def..dd4ac46055 100644 --- a/lib/syntax_tools/src/syntax_tools.app.src +++ b/lib/syntax_tools/src/syntax_tools.app.src @@ -17,4 +17,5 @@ {registered,[]}, {applications, [stdlib]}, {env, []}, - {runtime_dependencies, ["stdlib-2.5","kernel-3.0","erts-6.0"]}]}. + {runtime_dependencies, + ["compiler-6.0","erts-6.0","kernel-3.0","stdlib-2.5"]}]}. diff --git a/lib/syntax_tools/vsn.mk b/lib/syntax_tools/vsn.mk index 1c42ef0ddb..403e90196e 100644 --- a/lib/syntax_tools/vsn.mk +++ b/lib/syntax_tools/vsn.mk @@ -1 +1 @@ -SYNTAX_TOOLS_VSN = 1.6.18 +SYNTAX_TOOLS_VSN = 1.7 diff --git a/lib/tools/test/lcnt_SUITE.erl b/lib/tools/test/lcnt_SUITE.erl index 010dffe138..de68486b1b 100644 --- a/lib/tools/test/lcnt_SUITE.erl +++ b/lib/tools/test/lcnt_SUITE.erl @@ -97,12 +97,12 @@ t_conflicts_file([File|Files]) -> {ok, _} = lcnt:start(), ok = lcnt:load(File), ok = lcnt:conflicts(), - THs = [-1, 0, 100, 1000], + THs = [-1, 5], Print = [name , id , type , entry , tries , colls , ratio , time , duration], Opts = [ [{sort, Sort}, {reverse, Rev}, {max_locks, ML}, {combine, Combine}, {thresholds, [TH]}, {print, [Print]}] || - Sort <- [name , id , type , tries , colls , ratio , time , entry], - ML <- [none, 1 , 32, 4096], + Sort <- [name , type , tries , colls , ratio , time], + ML <- [none, 32], Combine <- [true, false], TH <- [{tries, Tries} || Tries <- THs] ++ [{colls, Colls} || Colls <- THs] ++ [{time, Time} || Time <- THs], Rev <- [true, false] @@ -131,12 +131,12 @@ t_locations_file([File|Files]) -> {ok, _} = lcnt:start(), ok = lcnt:load(File), ok = lcnt:locations(), - THs = [-1, 0, 100, 1000], + THs = [-1, 0, 100], Print = [name , id , type , entry , tries , colls , ratio , time , duration], Opts = [ [{full_id, Id}, {sort, Sort}, {max_locks, ML}, {combine, Combine}, {thresholds, [TH]}, {print, Print}] || Sort <- [name , id , type , tries , colls , ratio , time , entry], - ML <- [none, 1 , 64], + ML <- [none, 64], Combine <- [true, false], TH <- [{tries, Tries} || Tries <- THs] ++ [{colls, Colls} || Colls <- THs] ++ [{time, Time} || Time <- THs], Id <- [true, false] diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk index 3b3202d38b..68c3f6e29c 100644 --- a/lib/tools/vsn.mk +++ b/lib/tools/vsn.mk @@ -1 +1 @@ -TOOLS_VSN = 2.7.2 +TOOLS_VSN = 2.8 diff --git a/lib/typer/vsn.mk b/lib/typer/vsn.mk index ce658e257b..74c0ccfc59 100644 --- a/lib/typer/vsn.mk +++ b/lib/typer/vsn.mk @@ -1 +1 @@ -TYPER_VSN = 0.9.8 +TYPER_VSN = 0.9.9 diff --git a/lib/wx/vsn.mk b/lib/wx/vsn.mk index 942d4c0d6f..09fb9f384c 100644 --- a/lib/wx/vsn.mk +++ b/lib/wx/vsn.mk @@ -1 +1 @@ -WX_VSN = 1.3.3 +WX_VSN = 1.4 diff --git a/otp_versions.table b/otp_versions.table index fbed2ce427..50a77237ff 100644 --- a/otp_versions.table +++ b/otp_versions.table @@ -1,3 +1,5 @@ +OTP-17.5.6 : inets-5.10.9 ssh-3.2.4 ssl-6.0.1 # asn1-3.0.4 common_test-1.10.1 compiler-5.0.4 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.5 debugger-4.0.3 dialyzer-2.7.4 diameter-1.9.2 edoc-0.7.16 eldap-1.1.1 erl_docgen-0.3.7 erl_interface-3.7.20 erts-6.4.1 et-1.5 eunit-2.2.9 gs-1.5.16 hipe-3.11.3 ic-4.3.6 jinterface-1.5.12 kernel-3.2 megaco-3.17.3 mnesia-4.12.5 observer-2.0.4 odbc-2.10.22 orber-3.7.1 os_mon-2.3.1 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 public_key-0.23 reltool-0.6.6 runtime_tools-1.8.16 sasl-2.4.1 snmp-5.1.2 stdlib-2.4 syntax_tools-1.6.18 test_server-3.8.1 tools-2.7.2 typer-0.9.8 webtool-0.8.10 wx-1.3.3 xmerl-1.3.7 : +OTP-17.5.5 : diameter-1.9.2 # asn1-3.0.4 common_test-1.10.1 compiler-5.0.4 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.5 debugger-4.0.3 dialyzer-2.7.4 edoc-0.7.16 eldap-1.1.1 erl_docgen-0.3.7 erl_interface-3.7.20 erts-6.4.1 et-1.5 eunit-2.2.9 gs-1.5.16 hipe-3.11.3 ic-4.3.6 inets-5.10.8 jinterface-1.5.12 kernel-3.2 megaco-3.17.3 mnesia-4.12.5 observer-2.0.4 odbc-2.10.22 orber-3.7.1 os_mon-2.3.1 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 public_key-0.23 reltool-0.6.6 runtime_tools-1.8.16 sasl-2.4.1 snmp-5.1.2 ssh-3.2.3 ssl-6.0 stdlib-2.4 syntax_tools-1.6.18 test_server-3.8.1 tools-2.7.2 typer-0.9.8 webtool-0.8.10 wx-1.3.3 xmerl-1.3.7 : OTP-17.5.4 : inets-5.10.8 ssh-3.2.3 # asn1-3.0.4 common_test-1.10.1 compiler-5.0.4 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.5 debugger-4.0.3 dialyzer-2.7.4 diameter-1.9.1 edoc-0.7.16 eldap-1.1.1 erl_docgen-0.3.7 erl_interface-3.7.20 erts-6.4.1 et-1.5 eunit-2.2.9 gs-1.5.16 hipe-3.11.3 ic-4.3.6 jinterface-1.5.12 kernel-3.2 megaco-3.17.3 mnesia-4.12.5 observer-2.0.4 odbc-2.10.22 orber-3.7.1 os_mon-2.3.1 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 public_key-0.23 reltool-0.6.6 runtime_tools-1.8.16 sasl-2.4.1 snmp-5.1.2 ssl-6.0 stdlib-2.4 syntax_tools-1.6.18 test_server-3.8.1 tools-2.7.2 typer-0.9.8 webtool-0.8.10 wx-1.3.3 xmerl-1.3.7 : OTP-17.5.3 : common_test-1.10.1 diameter-1.9.1 erts-6.4.1 snmp-5.1.2 test_server-3.8.1 # asn1-3.0.4 compiler-5.0.4 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.5 debugger-4.0.3 dialyzer-2.7.4 edoc-0.7.16 eldap-1.1.1 erl_docgen-0.3.7 erl_interface-3.7.20 et-1.5 eunit-2.2.9 gs-1.5.16 hipe-3.11.3 ic-4.3.6 inets-5.10.7 jinterface-1.5.12 kernel-3.2 megaco-3.17.3 mnesia-4.12.5 observer-2.0.4 odbc-2.10.22 orber-3.7.1 os_mon-2.3.1 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 public_key-0.23 reltool-0.6.6 runtime_tools-1.8.16 sasl-2.4.1 ssh-3.2.2 ssl-6.0 stdlib-2.4 syntax_tools-1.6.18 tools-2.7.2 typer-0.9.8 webtool-0.8.10 wx-1.3.3 xmerl-1.3.7 : OTP-17.5.2 : inets-5.10.7 ssh-3.2.2 # asn1-3.0.4 common_test-1.10 compiler-5.0.4 cosEvent-2.1.15 cosEventDomain-1.1.14 cosFileTransfer-1.1.16 cosNotification-1.1.21 cosProperty-1.1.17 cosTime-1.1.14 cosTransactions-1.2.14 crypto-3.5 debugger-4.0.3 dialyzer-2.7.4 diameter-1.9 edoc-0.7.16 eldap-1.1.1 erl_docgen-0.3.7 erl_interface-3.7.20 erts-6.4 et-1.5 eunit-2.2.9 gs-1.5.16 hipe-3.11.3 ic-4.3.6 jinterface-1.5.12 kernel-3.2 megaco-3.17.3 mnesia-4.12.5 observer-2.0.4 odbc-2.10.22 orber-3.7.1 os_mon-2.3.1 ose-1.0.2 otp_mibs-1.0.10 parsetools-2.0.12 percept-0.8.10 public_key-0.23 reltool-0.6.6 runtime_tools-1.8.16 sasl-2.4.1 snmp-5.1.1 ssl-6.0 stdlib-2.4 syntax_tools-1.6.18 test_server-3.8 tools-2.7.2 typer-0.9.8 webtool-0.8.10 wx-1.3.3 xmerl-1.3.7 : diff --git a/system/doc/efficiency_guide/binaryhandling.xml b/system/doc/efficiency_guide/binaryhandling.xml index 0ac1a7ee32..1d247bcdd5 100644 --- a/system/doc/efficiency_guide/binaryhandling.xml +++ b/system/doc/efficiency_guide/binaryhandling.xml @@ -416,6 +416,7 @@ non_opt_eq([], <<>>) -> how you can find out whether your code can be optimized.</p> <section> + <marker id="bin_opt_info"></marker> <title>Option bin_opt_info</title> <p>Use the <c>bin_opt_info</c> option to have the compiler print a lot of |