diff options
Diffstat (limited to 'lib/stdlib/src')
68 files changed, 5270 insertions, 1584 deletions
diff --git a/lib/stdlib/src/array.erl b/lib/stdlib/src/array.erl index 83576c9fd3..2f69e2b0a4 100644 --- a/lib/stdlib/src/array.erl +++ b/lib/stdlib/src/array.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -163,17 +163,17 @@ -type array_indx() :: non_neg_integer(). --type array_opt() :: 'fixed' | non_neg_integer() - | {'default', term()} | {'fixed', boolean()} - | {'size', non_neg_integer()}. +-type array_opt() :: {'fixed', boolean()} | 'fixed' + | {'default', Value :: term()} + | {'size', N :: non_neg_integer()} + | (N :: non_neg_integer()). -type array_opts() :: array_opt() | [array_opt()]. --type indx_pair() :: {array_indx(), term()}. +-type indx_pair() :: {Index :: array_indx(), Value :: term()}. -type indx_pairs() :: [indx_pair()]. %%-------------------------------------------------------------------------- -%% @spec () -> array() %% @doc Create a new, extendible array with initial size zero. %% @equiv new([]) %% @@ -185,7 +185,6 @@ new() -> new([]). -%% @spec (Options::term()) -> array() %% @doc Create a new array according to the given options. By default, %% the array is extendible and has initial size zero. Array indices %% start at 0. @@ -224,12 +223,11 @@ new() -> %% @see from_list/2 %% @see fix/1 --spec new(array_opts()) -> array(). +-spec new(Options :: array_opts()) -> array(). new(Options) -> new_0(Options, 0, false). -%% @spec (Size::integer(), Options::term()) -> array() %% @doc Create a new array according to the given size and options. If %% `Size' is not a nonnegative integer, the call fails with reason %% `badarg'. By default, the array has fixed size. Note that any size @@ -245,7 +243,7 @@ new(Options) -> %% %% @see new/1 --spec new(non_neg_integer(), array_opts()) -> array(). +-spec new(Size :: non_neg_integer(), Options :: array_opts()) -> array(). new(Size, Options) when is_integer(Size), Size >= 0 -> new_0(Options, Size, true); @@ -293,13 +291,12 @@ find_max(_I, M) -> M. -%% @spec (X::term()) -> boolean() %% @doc Returns `true' if `X' appears to be an array, otherwise `false'. %% Note that the check is only shallow; there is no guarantee that `X' %% is a well-formed array representation even if this function returns %% `true'. --spec is_array(term()) -> boolean(). +-spec is_array(X :: term()) -> boolean(). is_array(#array{size = Size, max = Max}) when is_integer(Size), is_integer(Max) -> @@ -308,25 +305,23 @@ is_array(_) -> false. -%% @spec (array()) -> integer() %% @doc Get the number of entries in the array. Entries are numbered %% from 0 to `size(Array)-1'; hence, this is also the index of the first %% entry that is guaranteed to not have been previously set. %% @see set/3 %% @see sparse_size/1 --spec size(array()) -> non_neg_integer(). +-spec size(Array :: array()) -> non_neg_integer(). size(#array{size = N}) -> N; size(_) -> erlang:error(badarg). -%% @spec (array()) -> term() %% @doc Get the value used for uninitialized entries. %% %% @see new/2 --spec default(array()) -> term(). +-spec default(Array :: array()) -> term(). default(#array{default = D}) -> D; default(_) -> erlang:error(badarg). @@ -405,23 +400,21 @@ new_test_() -> -endif. -%% @spec (array()) -> array() %% @doc Fix the size of the array. This prevents it from growing %% automatically upon insertion; see also {@link set/3}. %% @see relax/1 --spec fix(array()) -> array(). +-spec fix(Array :: array()) -> array(). fix(#array{}=A) -> A#array{max = 0}. -%% @spec (array()) -> boolean() %% @doc Check if the array has fixed size. %% Returns `true' if the array is fixed, otherwise `false'. %% @see fix/1 --spec is_fix(array()) -> boolean(). +-spec is_fix(Array :: array()) -> boolean(). is_fix(#array{max = 0}) -> true; is_fix(#array{}) -> false. @@ -455,12 +448,11 @@ fix_test_() -> -endif. -%% @spec (array()) -> array() %% @doc Make the array resizable. (Reverses the effects of {@link %% fix/1}.) %% @see fix/1 --spec relax(array()) -> array(). +-spec relax(Array :: array()) -> array(). relax(#array{size = N}=A) -> A#array{max = find_max(N-1, ?LEAFSIZE)}. @@ -481,12 +473,11 @@ relax_test_() -> -endif. -%% @spec (integer(), array()) -> array() %% @doc Change the size of the array. If `Size' is not a nonnegative %% integer, the call fails with reason `badarg'. If the given array has %% fixed size, the resulting array will also have fixed size. --spec resize(non_neg_integer(), array()) -> array(). +-spec resize(Size :: non_neg_integer(), Array :: array()) -> array(). resize(Size, #array{size = N, max = M, elements = E}=A) when is_integer(Size), Size >= 0 -> @@ -510,8 +501,6 @@ resize(_Size, _) -> erlang:error(badarg). -%% @spec (array()) -> array() - %% @doc Change the size of the array to that reported by {@link %% sparse_size/1}. If the given array has fixed size, the resulting %% array will also have fixed size. @@ -519,7 +508,7 @@ resize(_Size, _) -> %% @see resize/2 %% @see sparse_size/1 --spec resize(array()) -> array(). +-spec resize(Array :: array()) -> array(). resize(Array) -> resize(sparse_size(Array), Array). @@ -559,7 +548,6 @@ resize_test_() -> -endif. -%% @spec (integer(), term(), array()) -> array() %% @doc Set entry `I' of the array to `Value'. If `I' is not a %% nonnegative integer, or if the array has fixed size and `I' is larger %% than the maximum index, the call fails with reason `badarg'. @@ -570,7 +558,7 @@ resize_test_() -> %% @see get/2 %% @see reset/2 --spec set(array_indx(), term(), array()) -> array(). +-spec set(I :: array_indx(), Value :: term(), Array :: array()) -> array(). set(I, Value, #array{size = N, max = M, default = D, elements = E}=A) when is_integer(I), I >= 0 -> @@ -624,7 +612,6 @@ expand(I, _S, X, D) -> setelement(I+1, ?NEW_LEAF(D), X). -%% @spec (integer(), array()) -> term() %% @doc Get the value of entry `I'. If `I' is not a nonnegative %% integer, or if the array has fixed size and `I' is larger than the %% maximum index, the call fails with reason `badarg'. @@ -634,7 +621,7 @@ expand(I, _S, X, D) -> %% @see set/3 --spec get(array_indx(), array()) -> term(). +-spec get(I :: array_indx(), Array :: array()) -> term(). get(I, #array{size = N, max = M, elements = E, default = D}) when is_integer(I), I >= 0 -> @@ -660,7 +647,6 @@ get_1(I, E, _D) -> element(I+1, E). -%% @spec (integer(), array()) -> array() %% @doc Reset entry `I' to the default value for the array. %% If the value of entry `I' is the default value the array will be %% returned unchanged. Reset will never change size of the array. @@ -675,7 +661,7 @@ get_1(I, E, _D) -> %% TODO: a reset_range function --spec reset(array_indx(), array()) -> array(). +-spec reset(I :: array_indx(), Array :: array()) -> array(). reset(I, #array{size = N, max = M, default = D, elements = E}=A) when is_integer(I), I >= 0 -> @@ -756,13 +742,12 @@ set_get_test_() -> -endif. -%% @spec (array()) -> list() %% @doc Converts the array to a list. %% %% @see from_list/2 %% @see sparse_to_list/1 --spec to_list(array()) -> list(). +-spec to_list(Array :: array()) -> list(). to_list(#array{size = 0}) -> []; @@ -831,12 +816,11 @@ to_list_test_() -> -endif. -%% @spec (array()) -> list() %% @doc Converts the array to a list, skipping default-valued entries. %% %% @see to_list/1 --spec sparse_to_list(array()) -> list(). +-spec sparse_to_list(Array :: array()) -> list(). sparse_to_list(#array{size = 0}) -> []; @@ -901,15 +885,13 @@ sparse_to_list_test_() -> -endif. -%% @spec (list()) -> array() %% @equiv from_list(List, undefined) --spec from_list(list()) -> array(). +-spec from_list(List :: list()) -> array(). from_list(List) -> from_list(List, undefined). -%% @spec (list(), term()) -> array() %% @doc Convert a list to an extendible array. `Default' is used as the value %% for uninitialized entries of the array. If `List' is not a proper list, %% the call fails with reason `badarg'. @@ -917,7 +899,7 @@ from_list(List) -> %% @see new/2 %% @see to_list/1 --spec from_list(list(), term()) -> array(). +-spec from_list(List :: list(), Default :: term()) -> array(). from_list([], Default) -> new({default,Default}); @@ -1011,13 +993,12 @@ from_list_test_() -> -endif. -%% @spec (array()) -> [{Index::integer(), Value::term()}] %% @doc Convert the array to an ordered list of pairs `{Index, Value}'. %% %% @see from_orddict/2 %% @see sparse_to_orddict/1 --spec to_orddict(array()) -> indx_pairs(). +-spec to_orddict(Array :: array()) -> indx_pairs(). to_orddict(#array{size = 0}) -> []; @@ -1104,13 +1085,12 @@ to_orddict_test_() -> -endif. -%% @spec (array()) -> [{Index::integer(), Value::term()}] %% @doc Convert the array to an ordered list of pairs `{Index, Value}', %% skipping default-valued entries. %% %% @see to_orddict/1 --spec sparse_to_orddict(array()) -> indx_pairs(). +-spec sparse_to_orddict(Array :: array()) -> indx_pairs(). sparse_to_orddict(#array{size = 0}) -> []; @@ -1188,15 +1168,13 @@ sparse_to_orddict_test_() -> -endif. -%% @spec (list()) -> array() %% @equiv from_orddict(Orddict, undefined) --spec from_orddict(indx_pairs()) -> array(). +-spec from_orddict(Orddict :: indx_pairs()) -> array(). from_orddict(Orddict) -> from_orddict(Orddict, undefined). -%% @spec (list(), term()) -> array() %% @doc Convert an ordered list of pairs `{Index, Value}' to a %% corresponding extendible array. `Default' is used as the value for %% uninitialized entries of the array. If `List' is not a proper, @@ -1206,7 +1184,7 @@ from_orddict(Orddict) -> %% @see new/2 %% @see to_orddict/1 --spec from_orddict(indx_pairs(), term()) -> array(). +-spec from_orddict(Orddict :: indx_pairs(), Default :: term()) -> array(). from_orddict([], Default) -> new({default,Default}); @@ -1392,7 +1370,6 @@ from_orddict_test_() -> -endif. -%% @spec (Function, array()) -> array() %% Function = (Index::integer(), Value::term()) -> term() %% @doc Map the given function onto each element of the array. The %% elements are visited in order from the lowest index to the highest. @@ -1402,7 +1379,8 @@ from_orddict_test_() -> %% @see foldr/3 %% @see sparse_map/2 --spec map(fun((array_indx(), _) -> _), array()) -> array(). +-spec map(Function, Array :: array()) -> array() when + Function :: fun((Index :: array_indx(), Value :: _) -> _). map(Function, Array=#array{size = N, elements = E, default = D}) when is_function(Function, 2) -> @@ -1485,7 +1463,6 @@ map_test_() -> -endif. -%% @spec (Function, array()) -> array() %% Function = (Index::integer(), Value::term()) -> term() %% @doc Map the given function onto each element of the array, skipping %% default-valued entries. The elements are visited in order from the @@ -1494,7 +1471,8 @@ map_test_() -> %% %% @see map/2 --spec sparse_map(fun((array_indx(), _) -> _), array()) -> array(). +-spec sparse_map(Function, Array :: array()) -> array() when + Function :: fun((Index :: array_indx(), Value :: _) -> _). sparse_map(Function, Array=#array{size = N, elements = E, default = D}) when is_function(Function, 2) -> @@ -1580,9 +1558,6 @@ sparse_map_test_() -> -endif. -%% @spec (Function, InitialAcc::term(), array()) -> term() -%% Function = (Index::integer(), Value::term(), Acc::term()) -> -%% term() %% @doc Fold the elements of the array using the given function and %% initial accumulator value. The elements are visited in order from the %% lowest index to the highest. If `Function' is not a function, the @@ -1592,7 +1567,8 @@ sparse_map_test_() -> %% @see map/2 %% @see sparse_foldl/3 --spec foldl(fun((array_indx(), _, A) -> B), A, array()) -> B. +-spec foldl(Function, InitialAcc :: A, Array :: array()) -> B when + Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). foldl(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> @@ -1656,9 +1632,6 @@ foldl_test_() -> -endif. -%% @spec (Function, InitialAcc::term(), array()) -> term() -%% Function = (Index::integer(), Value::term(), Acc::term()) -> -%% term() %% @doc Fold the elements of the array using the given function and %% initial accumulator value, skipping default-valued entries. The %% elements are visited in order from the lowest index to the highest. @@ -1667,7 +1640,8 @@ foldl_test_() -> %% @see foldl/3 %% @see sparse_foldr/3 --spec sparse_foldl(fun((array_indx(), _, A) -> B), A, array()) -> B. +-spec sparse_foldl(Function, InitialAcc :: A, Array :: array()) -> B when + Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). sparse_foldl(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> @@ -1735,9 +1709,6 @@ sparse_foldl_test_() -> -endif. -%% @spec (Function, InitialAcc::term(), array()) -> term() -%% Function = (Index::integer(), Value::term(), Acc::term()) -> -%% term() %% @doc Fold the elements of the array right-to-left using the given %% function and initial accumulator value. The elements are visited in %% order from the highest index to the lowest. If `Function' is not a @@ -1746,7 +1717,8 @@ sparse_foldl_test_() -> %% @see foldl/3 %% @see map/2 --spec foldr(fun((array_indx(), _, A) -> B), A, array()) -> B. +-spec foldr(Function, InitialAcc :: A, Array :: array()) -> B when + Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). foldr(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> @@ -1815,9 +1787,6 @@ foldr_test_() -> -endif. -%% @spec (Function, InitialAcc::term(), array()) -> term() -%% Function = (Index::integer(), Value::term(), Acc::term()) -> -%% term() %% @doc Fold the elements of the array right-to-left using the given %% function and initial accumulator value, skipping default-valued %% entries. The elements are visited in order from the highest index to @@ -1827,7 +1796,8 @@ foldr_test_() -> %% @see foldr/3 %% @see sparse_foldl/3 --spec sparse_foldr(fun((array_indx(), _, A) -> B), A, array()) -> B. +-spec sparse_foldr(Function, InitialAcc :: A, Array :: array()) -> B when + Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). sparse_foldr(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> @@ -1870,7 +1840,6 @@ sparse_foldr_3(I, T, Ix, A, F, D) -> end. -%% @spec (array()) -> integer() %% @doc Get the number of entries in the array up until the last %% non-default valued entry. In other words, returns `I+1' if `I' is the %% last non-default valued entry in the array, or zero if no such entry @@ -1878,7 +1847,7 @@ sparse_foldr_3(I, T, Ix, A, F, D) -> %% @see size/1 %% @see resize/1 --spec sparse_size(array()) -> non_neg_integer(). +-spec sparse_size(Array :: array()) -> non_neg_integer(). sparse_size(A) -> F = fun (I, _V, _A) -> throw({value, I}) end, diff --git a/lib/stdlib/src/base64.erl b/lib/stdlib/src/base64.erl index a14a72ac6d..5d800e87b8 100644 --- a/lib/stdlib/src/base64.erl +++ b/lib/stdlib/src/base64.erl @@ -38,7 +38,9 @@ %% Description: Encodes a plain ASCII string (or binary) into base64. %%------------------------------------------------------------------------- --spec encode_to_string(string() | binary()) -> ascii_string(). +-spec encode_to_string(Data) -> Base64String when + Data :: string() | binary(), + Base64String :: ascii_string(). encode_to_string(Bin) when is_binary(Bin) -> encode_to_string(binary_to_list(Bin)); @@ -53,7 +55,9 @@ encode_to_string(List) when is_list(List) -> %% Description: Encodes a plain ASCII string (or binary) into base64. %%------------------------------------------------------------------------- --spec encode(string() | binary()) -> binary(). +-spec encode(Data) -> Base64 when + Data :: string() | binary(), + Base64 :: binary(). encode(Bin) when is_binary(Bin) -> encode_binary(Bin); @@ -102,14 +106,18 @@ encode_binary(Bin) -> %% whereas decode crashes if an illegal character is found %%------------------------------------------------------------------------- --spec decode(string() | binary()) -> binary(). +-spec decode(Base64) -> Data when + Base64 :: string() | binary(), + Data :: binary(). decode(Bin) when is_binary(Bin) -> decode_binary(<<>>, Bin); decode(List) when is_list(List) -> list_to_binary(decode_l(List)). --spec mime_decode(string() | binary()) -> binary(). +-spec mime_decode(Base64) -> Data when + Base64 :: string() | binary(), + Data :: binary(). mime_decode(Bin) when is_binary(Bin) -> mime_decode_binary(<<>>, Bin); @@ -139,14 +147,18 @@ mime_decode_l(List) -> %% whereas decode crashes if an illegal character is found %%------------------------------------------------------------------------- --spec decode_to_string(string() | binary()) -> string(). +-spec decode_to_string(Base64) -> DataString when + Base64 :: string() | binary(), + DataString :: string(). decode_to_string(Bin) when is_binary(Bin) -> decode_to_string(binary_to_list(Bin)); decode_to_string(List) when is_list(List) -> decode_l(List). --spec mime_decode_to_string(string() | binary()) -> string(). +-spec mime_decode_to_string(Base64) -> DataString when + Base64 :: string() | binary(), + DataString :: string(). mime_decode_to_string(Bin) when is_binary(Bin) -> mime_decode_to_string(binary_to_list(Bin)); diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl index 74d4ad3da7..e9a5e6831e 100644 --- a/lib/stdlib/src/beam_lib.erl +++ b/lib/stdlib/src/beam_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2010. All Rights Reserved. +%% Copyright Ericsson AB 2000-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -54,13 +54,9 @@ -type beam() :: module() | file:filename() | binary(). -%% XXX: THE FOLLOWING SHOULD BE IMPORTED FROM SOMEWHERE ELSE --type forms() :: term(). +-type forms() :: [erl_parse:abstract_form()]. --type abst_vsn() :: atom(). --type abst_code() :: {abst_vsn(), forms()} | 'no_abstract_code'. --type attribute() :: atom(). --type attrvalue() :: term(). +-type abst_code() :: {AbstVersion :: atom(), forms()} | 'no_abstract_code'. -type dataB() :: binary(). -type index() :: non_neg_integer(). -type label() :: integer(). @@ -74,9 +70,9 @@ | 'atoms'. -type chunkref() :: chunkname() | chunkid(). --type attrib_entry() :: {attribute(), [attrvalue()]}. --type compinfo_entry() :: {atom(), term()}. --type labeled_entry() :: {atom(), arity(), label()}. +-type attrib_entry() :: {Attribute :: atom(), [AttributeValue :: term()]}. +-type compinfo_entry() :: {InfoKey :: atom(), term()}. +-type labeled_entry() :: {Function :: atom(), arity(), label()}. -type chunkdata() :: {chunkid(), dataB()} | {'abstract_code', abst_code()} @@ -85,20 +81,17 @@ | {'exports', [{atom(), arity()}]} | {'labeled_exports', [labeled_entry()]} | {'imports', [mfa()]} - | {'indexed_imports', [{index(), module(), atom(), arity()}]} + | {'indexed_imports', [{index(), module(), Function :: atom(), arity()}]} | {'locals', [{atom(), arity()}]} | {'labeled_locals', [labeled_entry()]} | {'atoms', [{integer(), atom()}]}. --type info_pair() :: {'file', file:filename()} - | {'binary', binary()} - | {'module', module()} - | {'chunks', [{chunkid(), integer(), integer()}]}. - %% Error reasons -type info_rsn() :: {'chunk_too_big', file:filename(), - chunkid(), integer(), integer()} - | {'invalid_beam_file', file:filename(), integer()} + chunkid(), ChunkSize :: non_neg_integer(), + FileSize :: non_neg_integer()} + | {'invalid_beam_file', file:filename(), + Position :: non_neg_integer()} | {'invalid_chunk', file:filename(), chunkid()} | {'missing_chunk', file:filename(), chunkid()} | {'not_a_beam_file', file:filename()} @@ -118,20 +111,34 @@ %% Exported functions %% --spec info(beam()) -> [info_pair()] | {'error', 'beam_lib', info_rsn()}. +-spec info(Beam) -> [InfoPair] | {'error', 'beam_lib', info_rsn()} when + Beam :: beam(), + InfoPair :: {'file', Filename :: file:filename()} + | {'binary', Binary :: binary()} + | {'module', Module :: module()} + | {'chunks', [{ChunkId :: chunkid(), + Pos :: non_neg_integer(), + Size :: non_neg_integer()}]}. info(File) -> read_info(beam_filename(File)). --spec chunks(beam(), [chunkref()]) -> - {'ok', {module(), [chunkdata()]}} | {'error', 'beam_lib', chnk_rsn()}. +-spec chunks(Beam, ChunkRefs) -> + {'ok', {module(), [chunkdata()]}} | + {'error', 'beam_lib', chnk_rsn()} when + Beam :: beam(), + ChunkRefs :: [chunkref()]. chunks(File, Chunks) -> read_chunk_data(File, Chunks). --spec chunks(beam(), [chunkref()], ['allow_missing_chunks']) -> - {'ok', {module(), [{chunkref(), chunkdata() | 'missing_chunk'}]}} - | {'error', 'beam_lib', chnk_rsn()}. +-spec chunks(Beam, ChunkRefs, Options) -> + {'ok', {module(), [ChunkResult]}} | + {'error', 'beam_lib', chnk_rsn()} when + Beam :: beam(), + ChunkRefs :: [chunkref()], + Options :: ['allow_missing_chunks'], + ChunkResult :: chunkdata() | {ChunkRef :: chunkref(), 'missing_chunk'}. chunks(File, Chunks, Options) -> try read_chunk_data(File, Chunks, Options) @@ -142,49 +149,65 @@ chunks(File, Chunks, Options) -> all_chunks(File) -> read_all_chunks(File). --spec cmp(beam(), beam()) -> 'ok' | {'error', 'beam_lib', cmp_rsn()}. +-spec cmp(Beam1, Beam2) -> 'ok' | {'error', 'beam_lib', cmp_rsn()} when + Beam1 :: beam(), + Beam2 :: beam(). cmp(File1, File2) -> try cmp_files(File1, File2) catch Error -> Error end. --spec cmp_dirs(atom() | file:filename(), atom() | file:filename()) -> - {[file:filename()], [file:filename()], - [{file:filename(), file:filename()}]} - | {'error', 'beam_lib', {'not_a_directory', term()} | info_rsn()}. +-spec cmp_dirs(Dir1, Dir2) -> + {Only1, Only2, Different} | {'error', 'beam_lib', Reason} when + Dir1 :: atom() | file:filename(), + Dir2 :: atom() | file:filename(), + Only1 :: [file:filename()], + Only2 :: [file:filename()], + Different :: [{Filename1 :: file:filename(), Filename2 :: file:filename()}], + Reason :: {'not_a_directory', term()} | info_rsn(). cmp_dirs(Dir1, Dir2) -> catch compare_dirs(Dir1, Dir2). --spec diff_dirs(atom() | file:filename(), atom() | file:filename()) -> - 'ok' | {'error', 'beam_lib', {'not_a_directory', term()} | info_rsn()}. +-spec diff_dirs(Dir1, Dir2) -> 'ok' | {'error', 'beam_lib', Reason} when + Dir1 :: atom() | file:filename(), + Dir2 :: atom() | file:filename(), + Reason :: {'not_a_directory', term()} | info_rsn(). diff_dirs(Dir1, Dir2) -> catch diff_directories(Dir1, Dir2). --spec strip(beam()) -> - {'ok', {module(), beam()}} | {'error', 'beam_lib', info_rsn()}. +-spec strip(Beam1) -> + {'ok', {module(), Beam2}} | {'error', 'beam_lib', info_rsn()} when + Beam1 :: beam(), + Beam2 :: beam(). strip(FileName) -> try strip_file(FileName) catch Error -> Error end. --spec strip_files([beam()]) -> - {'ok', [{module(), beam()}]} | {'error', 'beam_lib', info_rsn()}. +-spec strip_files(Files) -> + {'ok', [{module(), Beam}]} | {'error', 'beam_lib', info_rsn()} when + Files :: [beam()], + Beam :: beam(). strip_files(Files) when is_list(Files) -> try strip_fils(Files) catch Error -> Error end. --spec strip_release(atom() | file:filename()) -> +-spec strip_release(Dir) -> {'ok', [{module(), file:filename()}]} - | {'error', 'beam_lib', {'not_a_directory', term()} | info_rsn()}. + | {'error', 'beam_lib', Reason} when + Dir :: atom() | file:filename(), + Reason :: {'not_a_directory', term()} | info_rsn(). strip_release(Root) -> catch strip_rel(Root). --spec version(beam()) -> - {'ok', {module(), [term()]}} | {'error', 'beam_lib', chnk_rsn()}. +-spec version(Beam) -> + {'ok', {module(), [Version :: term()]}} | + {'error', 'beam_lib', chnk_rsn()} when + Beam :: beam(). version(File) -> case catch read_chunk_data(File, [attributes]) of @@ -195,11 +218,13 @@ version(File) -> Error end. --spec md5(beam()) -> - {'ok', {module(), binary()}} | {'error', 'beam_lib', chnk_rsn()}. +-spec md5(Beam) -> + {'ok', {module(), MD5}} | {'error', 'beam_lib', chnk_rsn()} when + Beam :: beam(), + MD5 :: binary(). md5(File) -> - case catch read_significant_chunks(File) of + case catch read_significant_chunks(File, md5_chunks()) of {ok, {Module, Chunks0}} -> Chunks = filter_funtab(Chunks0), {ok, {Module, erlang:md5([C || {_Id, C} <- Chunks])}}; @@ -207,7 +232,8 @@ md5(File) -> Error end. --spec format_error(term()) -> [char() | string()]. +-spec format_error(Reason) -> io_lib:chars() when + Reason :: term(). format_error({error, Error}) -> format_error(Error); @@ -260,12 +286,15 @@ format_error(E) -> | {'debug_info', mode(), module(), file:filename()}. -type crypto_fun() :: fun((crypto_fun_arg()) -> term()). --spec crypto_key_fun(crypto_fun()) -> 'ok' | {'error', term()}. +-spec crypto_key_fun(CryptoKeyFun) -> 'ok' | {'error', Reason} when + CryptoKeyFun :: crypto_fun(), + Reason :: badfun | exists | term(). crypto_key_fun(F) -> call_crypto_server({crypto_key_fun, F}). --spec clear_crypto_key_fun() -> 'undefined' | {'ok', term()}. +-spec clear_crypto_key_fun() -> 'undefined' | {'ok', Result} when + Result :: 'undefined' | term(). clear_crypto_key_fun() -> call_crypto_server(clear_crypto_key_fun). @@ -366,7 +395,7 @@ strip_fils(Files) -> %% -> {ok, {Mod, FileName}} | {ok, {Mod, binary()}} | throw(Error) strip_file(File) -> - {ok, {Mod, Chunks}} = read_significant_chunks(File), + {ok, {Mod, Chunks}} = read_significant_chunks(File, significant_chunks()), {ok, Stripped0} = build_module(Chunks), Stripped = compress(Stripped0), case File of @@ -424,8 +453,8 @@ is_useless_chunk("CInf") -> true; is_useless_chunk(_) -> false. %% -> {ok, {Module, Chunks}} | throw(Error) -read_significant_chunks(File) -> - case read_chunk_data(File, significant_chunks(), [allow_missing_chunks]) of +read_significant_chunks(File, ChunkList) -> + case read_chunk_data(File, ChunkList, [allow_missing_chunks]) of {ok, {Module, Chunks0}} -> Mandatory = mandatory_chunks(), Chunks = filter_significant_chunks(Chunks0, Mandatory, File, Module), @@ -806,12 +835,15 @@ file_error(FileName, {error, Reason}) -> error(Reason) -> throw({error, ?MODULE, Reason}). - -%% The following chunks are significant when calculating the MD5 for a module, -%% and also the modules that must be retained when stripping a file. -%% They are listed in the order that they should be MD5:ed. +%% The following chunks must be kept when stripping a BEAM file. significant_chunks() -> + ["Line" | md5_chunks()]. + +%% The following chunks are significant when calculating the MD5 +%% for a module. They are listed in the order that they should be MD5:ed. + +md5_chunks() -> ["Atom", "Code", "StrT", "ImpT", "ExpT", "FunT", "LitT"]. %% The following chunks are mandatory in every Beam file. @@ -864,13 +896,17 @@ call_crypto_server(Req) -> gen_server:call(?CRYPTO_KEY_SERVER, Req, infinity) catch exit:{noproc,_} -> - start_crypto_server(), - erlang:yield(), - call_crypto_server(Req) + %% Not started. + call_crypto_server_1(Req); + exit:{normal,_} -> + %% The process finished just as we called it. + call_crypto_server_1(Req) end. -start_crypto_server() -> - gen_server:start({local,?CRYPTO_KEY_SERVER}, ?MODULE, [], []). +call_crypto_server_1(Req) -> + gen_server:start({local,?CRYPTO_KEY_SERVER}, ?MODULE, [], []), + erlang:yield(), + call_crypto_server(Req). -spec init([]) -> {'ok', #state{}}. diff --git a/lib/stdlib/src/binary.erl b/lib/stdlib/src/binary.erl index f6489788b2..cb1e12ae46 100644 --- a/lib/stdlib/src/binary.erl +++ b/lib/stdlib/src/binary.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010. All Rights Reserved. +%% Copyright Ericsson AB 2010-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -38,11 +38,28 @@ %% Implemented in this module: -export([split/2,split/3,replace/3,replace/4]). +-opaque cp() :: tuple(). +-type part() :: {Start :: non_neg_integer(), Length :: integer()}. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% split %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec split(Subject, Pattern) -> Parts when + Subject :: binary(), + Pattern :: binary() | [binary()] | cp(), + Parts :: [binary()]. + split(H,N) -> split(H,N,[]). + +-spec split(Subject, Pattern, Options) -> Parts when + Subject :: binary(), + Pattern :: binary() | [binary()] | cp(), + Options :: [Option], + Option :: {scope, part()} | trim | global, + Parts :: [binary()]. + split(Haystack,Needles,Options) -> try {Part,Global,Trim} = get_opts_split(Options,{no,false,false}), @@ -89,8 +106,26 @@ do_split(H,[{A,B}|T],N,Trim) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% replace %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec replace(Subject, Pattern, Replacement) -> Result when + Subject :: binary(), + Pattern :: binary() | [ binary() ] | cp(), + Replacement :: binary(), + Result :: binary(). + replace(H,N,R) -> replace(H,N,R,[]). + +-spec replace(Subject, Pattern, Replacement, Options) -> Result when + Subject :: binary(), + Pattern :: binary() | [ binary() ] | cp(), + Replacement :: binary(), + Options :: [Option], + Option :: global | {scope, part()} | {insert_replaced, InsPos}, + InsPos :: OnePos | [ OnePos ], + OnePos :: non_neg_integer(), + Result :: binary(). + replace(Haystack,Needles,Replacement,Options) -> try true = is_binary(Replacement), % Make badarg instead of function clause diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl index 235ea939a8..a920921a5e 100644 --- a/lib/stdlib/src/c.erl +++ b/lib/stdlib/src/c.erl @@ -71,11 +71,16 @@ help() -> %% c(FileName) %% Compile a file/module. --spec c(file:name()) -> {'ok', module()} | 'error'. +-spec c(File) -> {'ok', Module} | 'error' when + File :: file:name(), + Module :: module(). c(File) -> c(File, []). --spec c(file:name(), [compile:option()]) -> {'ok', module()} | 'error'. +-spec c(File, Options) -> {'ok', Module} | 'error' when + File :: file:name(), + Options :: [compile:option()], + Module :: module(). c(File, Opts0) when is_list(Opts0) -> Opts = [report_errors,report_warnings|Opts0], @@ -140,7 +145,8 @@ check_load(_, Mod) -> {ok, Mod}. %% with constant c2 defined, c1=v1 (v1 must be a term!), include dir %% IDir, outdir ODir. --spec lc([erl_compile:cmd_line_arg()]) -> 'ok' | 'error'. +-spec lc(Files) -> 'ok' | 'error' when + Files :: [File :: erl_compile:cmd_line_arg()]. lc(Args) -> case catch split(Args, [], []) of @@ -205,12 +211,17 @@ make_term(Str) -> throw(error) end. --spec nc(file:name()) -> {'ok', module()} | 'error'. +-spec nc(File) -> {'ok', Module} | 'error' when + File :: file:name(), + Module :: module(). nc(File) -> nc(File, []). --spec nc(file:name(), [compile:option()] | compile:option()) -> - {'ok', module} | 'error'. +-spec nc(File, Options) -> {'ok', Module} | 'error' when + File :: file:name(), + Options :: [Option] | Option, + Option:: compile:option(), + Module :: module(). nc(File, Opts0) when is_list(Opts0) -> Opts = Opts0 ++ [report_errors, report_warnings], @@ -234,14 +245,17 @@ nc(File, Opt) when is_atom(Opt) -> %% l(Mod) %% Reload module Mod from file of same name --spec l(module()) -> code:load_ret(). +-spec l(Module) -> code:load_ret() when + Module :: module(). l(Mod) -> code:purge(Mod), code:load_file(Mod). %% Network version of l/1 -%%-spec nl(module()) -> +-spec nl(Module) -> abcast | error when + Module :: module(). + nl(Mod) -> case code:get_object_code(Mod) of {_Module, Bin, Fname} -> @@ -396,15 +410,20 @@ fetch(Key, Info) -> false -> 0 end. --spec pid(non_neg_integer(), non_neg_integer(), non_neg_integer()) -> pid(). +-spec pid(X, Y, Z) -> pid() when + X :: non_neg_integer(), + Y :: non_neg_integer(), + Z :: non_neg_integer(). pid(X, Y, Z) -> list_to_pid("<" ++ integer_to_list(X) ++ "." ++ integer_to_list(Y) ++ "." ++ integer_to_list(Z) ++ ">"). --spec i(non_neg_integer(), non_neg_integer(), non_neg_integer()) -> - [{atom(), term()}]. +-spec i(X, Y, Z) -> [{atom(), term()}] when + X :: non_neg_integer(), + Y :: non_neg_integer(), + Z :: non_neg_integer(). i(X, Y, Z) -> pinfo(pid(X, Y, Z)). @@ -413,7 +432,8 @@ i(X, Y, Z) -> pinfo(pid(X, Y, Z)). q() -> init:stop(). --spec bt(pid()) -> 'ok' | 'undefined'. +-spec bt(Pid) -> 'ok' | 'undefined' when + Pid :: pid(). bt(Pid) -> case catch erlang:process_display(Pid, backtrace) of @@ -476,7 +496,8 @@ bi(I) -> %% %% Short and nice form of module info %% --spec m(module()) -> 'ok'. +-spec m(Module) -> 'ok' when + Module :: module(). m(M) -> L = M:module_info(), @@ -664,7 +685,8 @@ pwd() -> ok = io:format("Cannot determine current directory\n") end. --spec cd(file:name()) -> 'ok'. +-spec cd(Dir) -> 'ok' when + Dir :: file:name(). cd(Dir) -> file:set_cwd(Dir), @@ -679,7 +701,8 @@ cd(Dir) -> ls() -> ls("."). --spec ls(file:name()) -> 'ok'. +-spec ls(Dir) -> 'ok' when + Dir :: file:name(). ls(Dir) -> case file:list_dir(Dir) of @@ -729,12 +752,19 @@ w(X) -> %% memory/[0,1] %% --spec memory() -> [{atom(), non_neg_integer()}]. +-spec memory() -> [{Type, Size}] when + Type :: atom(), + Size :: non_neg_integer(). memory() -> erlang:memory(). --spec memory(atom()) -> non_neg_integer() - ; ([atom()]) -> [{atom(), non_neg_integer()}]. +-spec memory(Type) -> Size when + Type :: atom(), + Size :: non_neg_integer() + ; (Types) -> [{Type, Size}] when + Types :: [Type], + Type :: atom(), + Size :: non_neg_integer(). memory(TypeSpec) -> erlang:memory(TypeSpec). @@ -767,7 +797,7 @@ appcall(App, M, F, Args) -> catch error:undef -> case erlang:get_stacktrace() of - [{M,F,Args}|_] -> + [{M,F,Args,_}|_] -> Arity = length(Args), io:format("Call to ~w:~w/~w in application ~w failed.\n", [M,F,Arity,App]); diff --git a/lib/stdlib/src/calendar.erl b/lib/stdlib/src/calendar.erl index 33725d999c..0320e0cd0e 100644 --- a/lib/stdlib/src/calendar.erl +++ b/lib/stdlib/src/calendar.erl @@ -63,6 +63,8 @@ %% Types %%---------------------------------------------------------------------- +-export_type([date/0, time/0, datetime/0, datetime1970/0]). + -type year() :: non_neg_integer(). -type year1970() :: 1970..10000. % should probably be 1970.. -type month() :: 1..12. @@ -74,13 +76,11 @@ -type ldom() :: 28 | 29 | 30 | 31. % last day of month -type weeknum() :: 1..53. --type t_now() :: {non_neg_integer(),non_neg_integer(),non_neg_integer()}. - --type t_date() :: {year(),month(),day()}. --type t_time() :: {hour(),minute(),second()}. --type t_datetime() :: {t_date(),t_time()}. --type t_datetime1970() :: {{year1970(),month(),day()},t_time()}. --type t_yearweeknum() :: {year(),weeknum()}. +-type date() :: {year(),month(),day()}. +-type time() :: {hour(),minute(),second()}. +-type datetime() :: {date(),time()}. +-type datetime1970() :: {{year1970(),month(),day()},time()}. +-type yearweeknum() :: {year(),weeknum()}. %%---------------------------------------------------------------------- @@ -106,7 +106,11 @@ %% January 1st. %% %% df/2 catches the case Year<0 --spec date_to_gregorian_days(year(),month(),day()) -> non_neg_integer(). +-spec date_to_gregorian_days(Year, Month, Day) -> Days when + Year :: year(), + Month :: month(), + Day :: day(), + Days :: non_neg_integer(). date_to_gregorian_days(Year, Month, Day) when is_integer(Day), Day > 0 -> Last = last_day_of_the_month(Year, Month), if @@ -114,7 +118,9 @@ date_to_gregorian_days(Year, Month, Day) when is_integer(Day), Day > 0 -> dy(Year) + dm(Month) + df(Year, Month) + Day - 1 end. --spec date_to_gregorian_days(t_date()) -> non_neg_integer(). +-spec date_to_gregorian_days(Date) -> Days when + Date :: date(), + Days :: non_neg_integer(). date_to_gregorian_days({Year, Month, Day}) -> date_to_gregorian_days(Year, Month, Day). @@ -124,7 +130,9 @@ date_to_gregorian_days({Year, Month, Day}) -> %% Computes the total number of seconds starting from year 0, %% January 1st. %% --spec datetime_to_gregorian_seconds(t_datetime()) -> non_neg_integer(). +-spec datetime_to_gregorian_seconds(DateTime) -> Seconds when + DateTime :: datetime(), + Seconds :: non_neg_integer(). datetime_to_gregorian_seconds({Date, Time}) -> ?SECONDS_PER_DAY*date_to_gregorian_days(Date) + time_to_seconds(Time). @@ -135,18 +143,23 @@ datetime_to_gregorian_seconds({Date, Time}) -> %% %% Returns: 1 | .. | 7. Monday = 1, Tuesday = 2, ..., Sunday = 7. %% --spec day_of_the_week(year(), month(), day()) -> daynum(). +-spec day_of_the_week(Year, Month, Day) -> daynum() when + Year :: year(), + Month :: month(), + Day :: day(). day_of_the_week(Year, Month, Day) -> (date_to_gregorian_days(Year, Month, Day) + 5) rem 7 + 1. --spec day_of_the_week(t_date()) -> daynum(). +-spec day_of_the_week(Date) -> daynum() when + Date:: date(). day_of_the_week({Year, Month, Day}) -> day_of_the_week(Year, Month, Day). %% gregorian_days_to_date(Days) = {Year, Month, Day} %% --spec gregorian_days_to_date(non_neg_integer()) -> t_date(). +-spec gregorian_days_to_date(Days) -> date() when + Days :: non_neg_integer(). gregorian_days_to_date(Days) -> {Year, DayOfYear} = day_to_year(Days), {Month, DayOfMonth} = year_day_to_date(Year, DayOfYear), @@ -155,7 +168,8 @@ gregorian_days_to_date(Days) -> %% gregorian_seconds_to_datetime(Secs) %% --spec gregorian_seconds_to_datetime(non_neg_integer()) -> t_datetime(). +-spec gregorian_seconds_to_datetime(Seconds) -> datetime() when + Seconds :: non_neg_integer(). gregorian_seconds_to_datetime(Secs) when Secs >= 0 -> Days = Secs div ?SECONDS_PER_DAY, Rest = Secs rem ?SECONDS_PER_DAY, @@ -164,7 +178,8 @@ gregorian_seconds_to_datetime(Secs) when Secs >= 0 -> %% is_leap_year(Year) = true | false %% --spec is_leap_year(year()) -> boolean(). +-spec is_leap_year(Year) -> boolean() when + Year :: year(). is_leap_year(Y) when is_integer(Y), Y >= 0 -> is_leap_year1(Y). @@ -179,7 +194,7 @@ is_leap_year1(_) -> false. %% %% Calculates the iso week number for the current date. %% --spec iso_week_number() -> t_yearweeknum(). +-spec iso_week_number() -> yearweeknum(). iso_week_number() -> {Date, _} = local_time(), iso_week_number(Date). @@ -188,7 +203,8 @@ iso_week_number() -> %% %% Calculates the iso week number for the given date. %% --spec iso_week_number(t_date()) -> t_yearweeknum(). +-spec iso_week_number(Date) -> yearweeknum() when + Date :: date(). iso_week_number({Year, Month, Day}) -> D = date_to_gregorian_days({Year, Month, Day}), W01_1_Year = gregorian_days_of_iso_w01_1(Year), @@ -216,7 +232,10 @@ iso_week_number({Year, Month, Day}) -> %% %% Returns the number of days in a month. %% --spec last_day_of_the_month(year(), month()) -> ldom(). +-spec last_day_of_the_month(Year, Month) -> LastDay when + Year :: year(), + Month :: month(), + LastDay :: ldom(). last_day_of_the_month(Y, M) when is_integer(Y), Y >= 0 -> last_day_of_the_month1(Y, M). @@ -237,24 +256,28 @@ last_day_of_the_month1(_, M) when is_integer(M), M > 0, M < 13 -> %% local_time() %% %% Returns: {date(), time()}, date() = {Y, M, D}, time() = {H, M, S}. --spec local_time() -> t_datetime(). +-spec local_time() -> datetime(). local_time() -> erlang:localtime(). %% local_time_to_universal_time(DateTime) %% --spec local_time_to_universal_time(t_datetime1970()) -> t_datetime1970(). +-spec local_time_to_universal_time(DateTime1) -> DateTime2 when + DateTime1 :: datetime1970(), + DateTime2 :: datetime1970(). local_time_to_universal_time(DateTime) -> erlang:localtime_to_universaltime(DateTime). --spec local_time_to_universal_time(t_datetime1970(), +-spec local_time_to_universal_time(datetime1970(), 'true' | 'false' | 'undefined') -> - t_datetime1970(). + datetime1970(). local_time_to_universal_time(DateTime, IsDst) -> erlang:localtime_to_universaltime(DateTime, IsDst). --spec local_time_to_universal_time_dst(t_datetime1970()) -> [t_datetime1970()]. +-spec local_time_to_universal_time_dst(DateTime1) -> [DateTime] when + DateTime1 :: datetime1970(), + DateTime :: datetime1970(). local_time_to_universal_time_dst(DateTime) -> UtDst = erlang:localtime_to_universaltime(DateTime, true), Ut = erlang:localtime_to_universaltime(DateTime, false), @@ -282,12 +305,14 @@ local_time_to_universal_time_dst(DateTime) -> %% = MilliSec = integer() %% Returns: {date(), time()}, date() = {Y, M, D}, time() = {H, M, S}. %% --spec now_to_datetime(t_now()) -> t_datetime1970(). +-spec now_to_datetime(Now) -> datetime1970() when + Now :: erlang:timestamp(). now_to_datetime({MSec, Sec, _uSec}) -> Sec0 = MSec*1000000 + Sec + ?DAYS_FROM_0_TO_1970*?SECONDS_PER_DAY, gregorian_seconds_to_datetime(Sec0). --spec now_to_universal_time(t_now()) -> t_datetime1970(). +-spec now_to_universal_time(Now) -> datetime1970() when + Now :: erlang:timestamp(). now_to_universal_time(Now) -> now_to_datetime(Now). @@ -296,7 +321,8 @@ now_to_universal_time(Now) -> %% %% Args: Now = now() %% --spec now_to_local_time(t_now()) -> t_datetime1970(). +-spec now_to_local_time(Now) -> datetime1970() when + Now :: erlang:timestamp(). now_to_local_time({MSec, Sec, _uSec}) -> erlang:universaltime_to_localtime( now_to_universal_time({MSec, Sec, _uSec})). @@ -305,7 +331,10 @@ now_to_local_time({MSec, Sec, _uSec}) -> %% seconds_to_daystime(Secs) = {Days, {Hour, Minute, Second}} %% --spec seconds_to_daystime(integer()) -> {integer(), t_time()}. +-spec seconds_to_daystime(Seconds) -> {Days, Time} when + Seconds :: integer(), + Days :: integer(), + Time :: time(). seconds_to_daystime(Secs) -> Days0 = Secs div ?SECONDS_PER_DAY, Secs0 = Secs rem ?SECONDS_PER_DAY, @@ -323,7 +352,8 @@ seconds_to_daystime(Secs) -> %% Wraps. %% -type secs_per_day() :: 0..?SECONDS_PER_DAY. --spec seconds_to_time(secs_per_day()) -> t_time(). +-spec seconds_to_time(Seconds) -> time() when + Seconds :: secs_per_day(). seconds_to_time(Secs) when Secs >= 0, Secs < ?SECONDS_PER_DAY -> Secs0 = Secs rem ?SECONDS_PER_DAY, Hour = Secs0 div ?SECONDS_PER_HOUR, @@ -340,8 +370,11 @@ seconds_to_time(Secs) when Secs >= 0, Secs < ?SECONDS_PER_DAY -> %% Date = {Year, Month, Day}, Time = {Hour, Minute, Sec}, %% Year = Month = Day = Hour = Minute = Sec = integer() %% --type timediff() :: {integer(), t_time()}. --spec time_difference(t_datetime(), t_datetime()) -> timediff(). +-spec time_difference(T1, T2) -> {Days, Time} when + T1 :: datetime(), + T2 :: datetime(), + Days :: integer(), + Time :: time(). time_difference({{Y1, Mo1, D1}, {H1, Mi1, S1}}, {{Y2, Mo2, D2}, {H2, Mi2, S2}}) -> Secs = datetime_to_gregorian_seconds({{Y2, Mo2, D2}, {H2, Mi2, S2}}) - @@ -352,7 +385,8 @@ time_difference({{Y1, Mo1, D1}, {H1, Mi1, S1}}, %% %% time_to_seconds(Time) %% --spec time_to_seconds(t_time()) -> secs_per_day(). +-spec time_to_seconds(Time) -> secs_per_day() when + Time :: time(). time_to_seconds({H, M, S}) when is_integer(H), is_integer(M), is_integer(S) -> H * ?SECONDS_PER_HOUR + M * ?SECONDS_PER_MINUTE + S. @@ -361,14 +395,15 @@ time_to_seconds({H, M, S}) when is_integer(H), is_integer(M), is_integer(S) -> %% universal_time() %% %% Returns: {date(), time()}, date() = {Y, M, D}, time() = {H, M, S}. --spec universal_time() -> t_datetime(). +-spec universal_time() -> datetime(). universal_time() -> erlang:universaltime(). %% universal_time_to_local_time(DateTime) %% --spec universal_time_to_local_time(t_datetime()) -> t_datetime(). +-spec universal_time_to_local_time(DateTime) -> datetime() when + DateTime :: datetime1970(). universal_time_to_local_time(DateTime) -> erlang:universaltime_to_localtime(DateTime). @@ -376,7 +411,10 @@ universal_time_to_local_time(DateTime) -> %% valid_date(Year, Month, Day) = true | false %% valid_date({Year, Month, Day}) = true | false %% --spec valid_date(integer(), integer(), integer()) -> boolean(). +-spec valid_date(Year, Month, Day) -> boolean() when + Year :: integer(), + Month :: integer(), + Day :: integer(). valid_date(Y, M, D) when is_integer(Y), is_integer(M), is_integer(D) -> valid_date1(Y, M, D). @@ -386,7 +424,8 @@ valid_date1(Y, M, D) when Y >= 0, M > 0, M < 13, D > 0 -> valid_date1(_, _, _) -> false. --spec valid_date({integer(),integer(),integer()}) -> boolean(). +-spec valid_date(Date) -> boolean() when + Date :: date(). valid_date({Y, M, D}) -> valid_date(Y, M, D). diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl index 6c91f1efb7..c0f9ce34b0 100644 --- a/lib/stdlib/src/dets.erl +++ b/lib/stdlib/src/dets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -97,10 +97,6 @@ -include("dets.hrl"). --type object() :: tuple(). --type pattern() :: atom() | tuple(). --type tab_name() :: atom() | reference(). - %%% This is the implementation of the mnesia file storage. Each (non %%% ram-copy) table is maintained in a corresponding .DAT file. The %%% dat file is organized as a segmented linear hashlist. The head of @@ -179,6 +175,21 @@ %%-define(PROFILE(C), C). -define(PROFILE(C), void). +-type access() :: 'read' | 'read_write'. +-type auto_save() :: 'infinity' | non_neg_integer(). +-opaque bindings_cont() :: #dets_cont{}. +-opaque cont() :: #dets_cont{}. +-type keypos() :: pos_integer(). +-type match_spec() :: ets:match_spec(). +-type object() :: tuple(). +-type no_slots() :: non_neg_integer() | 'default'. +-opaque object_cont() :: #dets_cont{}. +-type pattern() :: atom() | tuple(). +-opaque select_cont() :: #dets_cont{}. +-type tab_name() :: term(). +-type type() :: 'bag' | 'duplicate_bag' | 'set'. +-type version() :: 8 | 9 | 'default'. + %%% Some further debug code was added in R12B-1 (stdlib-1.15.1): %%% - there is a new open_file() option 'debug'; %%% - there is a new OS environment variable 'DETS_DEBUG'; @@ -203,9 +214,13 @@ add_user(Pid, Tab, Args) -> all() -> dets_server:all(). --type cont() :: #dets_cont{}. --spec bchunk(tab_name(), 'start' | cont()) -> - {cont(), binary() | tuple()} | '$end_of_table' | {'error', term()}. +-spec bchunk(Name, Continuation) -> + {Continuation2, Data} | '$end_of_table' | {'error', Reason} when + Name :: tab_name(), + Continuation :: 'start' | cont(), + Continuation2 :: cont(), + Data :: binary() | tuple(), + Reason :: term(). bchunk(Tab, start) -> badarg(treq(Tab, {bchunk_init, Tab}), [Tab, start]); @@ -214,7 +229,9 @@ bchunk(Tab, #dets_cont{what = bchunk, tab = Tab} = State) -> bchunk(Tab, Term) -> erlang:error(badarg, [Tab, Term]). --spec close(tab_name()) -> 'ok' | {'error', term()}. +-spec close(Name) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Reason :: term(). close(Tab) -> case dets_server:close(Tab) of @@ -224,12 +241,17 @@ close(Tab) -> Reply end. --spec delete(tab_name(), term()) -> 'ok' | {'error', term()}. +-spec delete(Name, Key) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Key :: term(), + Reason :: term(). delete(Tab, Key) -> badarg(treq(Tab, {delete_key, [Key]}), [Tab, Key]). --spec delete_all_objects(tab_name()) -> 'ok' | {'error', term()}. +-spec delete_all_objects(Name) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Reason :: term(). delete_all_objects(Tab) -> case treq(Tab, delete_all_objects) of @@ -241,7 +263,10 @@ delete_all_objects(Tab) -> Reply end. --spec delete_object(tab_name(), object()) -> 'ok' | {'error', term()}. +-spec delete_object(Name, Object) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Object :: object(), + Reason :: term(). delete_object(Tab, O) -> badarg(treq(Tab, {delete_object, [O]}), [Tab, O]). @@ -264,23 +289,42 @@ fsck(Fname, Version) -> end end. --spec first(tab_name()) -> term() | '$end_of_table'. +-spec first(Name) -> Key | '$end_of_table' when + Name :: tab_name(), + Key :: term(). first(Tab) -> badarg_exit(treq(Tab, first), [Tab]). --spec foldr(fun((object(), Acc) -> Acc), Acc, tab_name()) -> Acc | {'error', term()}. +-spec foldr(Function, Acc0, Name) -> Acc | {'error', Reason} when + Name :: tab_name(), + Function :: fun((Object :: object(), AccIn) -> AccOut), + Acc0 :: term(), + Acc :: term(), + AccIn :: term(), + AccOut :: term(), + Reason :: term(). foldr(Fun, Acc, Tab) -> foldl(Fun, Acc, Tab). --spec foldl(fun((object(), Acc) -> Acc), Acc, tab_name()) -> Acc | {'error', term()}. +-spec foldl(Function, Acc0, Name) -> Acc | {'error', Reason} when + Name :: tab_name(), + Function :: fun((Object :: object(), AccIn) -> AccOut), + Acc0 :: term(), + Acc :: term(), + AccIn :: term(), + AccOut :: term(), + Reason :: term(). foldl(Fun, Acc, Tab) -> Ref = make_ref(), do_traverse(Fun, Acc, Tab, Ref). --spec from_ets(tab_name(), ets:tab()) -> 'ok' | {'error', term()}. +-spec from_ets(Name, EtsTab) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + EtsTab :: ets:tab(), + Reason :: term(). from_ets(DTab, ETab) -> ets:safe_fixtable(ETab, true), @@ -304,6 +348,15 @@ from_ets_fun(LC, ETab) -> {L, from_ets_fun(ets:select(C), ETab)} end. +-spec info(Name) -> InfoList | 'undefined' when + Name :: tab_name(), + InfoList :: [InfoTuple], + InfoTuple :: {'file_size', non_neg_integer()} + | {'filename', file:name()} + | {'keypos', keypos()} + | {'size', non_neg_integer()} + | {'type', type()}. + info(Tab) -> case catch dets_server:get_pid(Tab) of {'EXIT', _Reason} -> @@ -312,6 +365,14 @@ info(Tab) -> undefined(req(Pid, info)) end. +-spec info(Name, Item) -> Value | 'undefined' when + Name :: tab_name(), + Item :: 'access' | 'auto_save' | 'bchunk_format' + | 'hash' | 'file_size' | 'filename' | 'keypos' | 'memory' + | 'no_keys' | 'no_objects' | 'no_slots' | 'owner' | 'ram_file' + | 'safe_fixed' | 'size' | 'type' | 'version', + Value :: term(). + info(Tab, owner) -> case catch dets_server:get_pid(Tab) of Pid when is_pid(Pid) -> @@ -334,9 +395,27 @@ info(Tab, Tag) -> undefined(req(Pid, {info, Tag})) end. +-spec init_table(Name, InitFun) -> ok | {'error', Reason} when + Name :: tab_name(), + InitFun :: fun((Arg) -> Res), + Arg :: read | close, + Res :: end_of_input | {[object()], InitFun} | {Data, InitFun} | term(), + Reason :: term(), + Data :: binary() | tuple(). + init_table(Tab, InitFun) -> init_table(Tab, InitFun, []). +-spec init_table(Name, InitFun, Options) -> ok | {'error', Reason} when + Name :: tab_name(), + InitFun :: fun((Arg) -> Res), + Arg :: read | close, + Res :: end_of_input | {[object()], InitFun} | {Data, InitFun} | term(), + Options :: Option | [Option], + Option :: {min_no_slots,no_slots()} | {format,term | bchunk}, + Reason :: term(), + Data :: binary() | tuple(). + init_table(Tab, InitFun, Options) when is_function(InitFun) -> case options(Options, [format, min_no_slots]) of {badarg,_} -> @@ -350,11 +429,20 @@ init_table(Tab, InitFun, Options) when is_function(InitFun) -> init_table(Tab, InitFun, Options) -> erlang:error(badarg, [Tab, InitFun, Options]). +-spec insert(Name, Objects) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Objects :: object() | [object()], + Reason :: term(). + insert(Tab, Objs) when is_list(Objs) -> badarg(treq(Tab, {insert, Objs}), [Tab, Objs]); insert(Tab, Obj) -> badarg(treq(Tab, {insert, [Obj]}), [Tab, Obj]). +-spec insert_new(Name, Objects) -> boolean() when + Name :: tab_name(), + Objects :: object() | [object()]. + insert_new(Tab, Objs) when is_list(Objs) -> badarg(treq(Tab, {insert_new, Objs}), [Tab, Objs]); insert_new(Tab, Obj) -> @@ -366,9 +454,17 @@ internal_close(Pid) -> internal_open(Pid, Ref, Args) -> req(Pid, {internal_open, Ref, Args}). +-spec is_compatible_bchunk_format(Name, BchunkFormat) -> boolean() when + Name :: tab_name(), + BchunkFormat :: binary(). + is_compatible_bchunk_format(Tab, Term) -> badarg(treq(Tab, {is_compatible_bchunk_format, Term}), [Tab, Term]). +-spec is_dets_file(Filename) -> boolean() | {'error', Reason} when + Filename :: file:name(), + Reason :: term(). + is_dets_file(FileName) -> case catch read_file_header(FileName, read, false) of {ok, Fd, FH} -> @@ -382,6 +478,12 @@ is_dets_file(FileName) -> Other end. +-spec lookup(Name, Key) -> Objects | {'error', Reason} when + Name :: tab_name(), + Key :: term(), + Objects :: [object()], + Reason :: term(). + lookup(Tab, Key) -> badarg(treq(Tab, {lookup_keys, [Key]}), [Tab, Key]). @@ -394,19 +496,43 @@ lookup_keys(Tab, Keys) -> erlang:error(badarg, [Tab, Keys]) end. +-spec match(Name, Pattern) -> [Match] | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + Match :: [term()], + Reason :: term(). + match(Tab, Pat) -> badarg(safe_match(Tab, Pat, bindings), [Tab, Pat]). +-spec match(Name, Pattern, N) -> + {[Match], Continuation} | '$end_of_table' | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + N :: 'default' | non_neg_integer(), + Continuation :: bindings_cont(), + Match :: [term()], + Reason :: term(). + match(Tab, Pat, N) -> badarg(init_chunk_match(Tab, Pat, bindings, N), [Tab, Pat, N]). +-spec match(Continuation) -> + {[Match], Continuation2} | '$end_of_table' | {'error', Reason} when + Continuation :: bindings_cont(), + Continuation2 :: bindings_cont(), + Match :: [term()], + Reason :: term(). + match(State) when State#dets_cont.what =:= bindings -> badarg(chunk_match(State), [State]); match(Term) -> erlang:error(badarg, [Term]). --spec match_delete(tab_name(), pattern()) -> - non_neg_integer() | 'ok' | {'error', term()}. +-spec match_delete(Name, Pattern) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + Reason :: term(). match_delete(Tab, Pat) -> badarg(match_delete(Tab, Pat, delete), [Tab, Pat]). @@ -434,23 +560,60 @@ do_match_delete(Tab, _Proc, Error, _What, _N) -> safe_fixtable(Tab, false), Error. +-spec match_object(Name, Pattern) -> Objects | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + Objects :: [object()], + Reason :: term(). + match_object(Tab, Pat) -> badarg(safe_match(Tab, Pat, object), [Tab, Pat]). +-spec match_object(Name, Pattern, N) -> + {Objects, Continuation} | '$end_of_table' | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + N :: 'default' | non_neg_integer(), + Continuation :: object_cont(), + Objects :: [object()], + Reason :: term(). + match_object(Tab, Pat, N) -> badarg(init_chunk_match(Tab, Pat, object, N), [Tab, Pat, N]). +-spec match_object(Continuation) -> + {Objects, Continuation2} | '$end_of_table' | {'error', Reason} when + Continuation :: object_cont(), + Continuation2 :: object_cont(), + Objects :: [object()], + Reason :: term(). + match_object(State) when State#dets_cont.what =:= object -> badarg(chunk_match(State), [State]); match_object(Term) -> erlang:error(badarg, [Term]). +-spec member(Name, Key) -> boolean() | {'error', Reason} when + Name :: tab_name(), + Key :: term(), + Reason :: term(). + member(Tab, Key) -> badarg(treq(Tab, {member, Key}), [Tab, Key]). +-spec next(Name, Key1) -> Key2 | '$end_of_table' when + Name :: tab_name(), + Key1 :: term(), + Key2 :: term(). + next(Tab, Key) -> badarg_exit(treq(Tab, {next, Key}), [Tab, Key]). +-spec open_file(Filename) -> {'ok', Reference} | {'error', Reason} when + Filename :: file:name(), + Reference :: reference(), + Reason :: term(). + %% Assuming that a file already exists, open it with the %% parameters as already specified in the file itself. %% Return a ref leading to the file. @@ -462,6 +625,22 @@ open_file(File) -> einval(Reply, [File]) end. +-spec open_file(Name, Args) -> {'ok', Name} | {'error', Reason} when + Name :: tab_name(), + Args :: [OpenArg], + OpenArg :: {'access', access()} + | {'auto_save', auto_save()} + | {'estimated_no_objects', non_neg_integer()} + | {'file', file:name()} + | {'max_no_slots', no_slots()} + | {'min_no_slots', no_slots()} + | {'keypos', keypos()} + | {'ram_file', boolean()} + | {'repair', boolean() | 'force'} + | {'type', type()} + | {'version', version()}, + Reason :: term(). + open_file(Tab, Args) when is_list(Args) -> case catch defaults(Tab, Args) of OpenArgs when is_record(OpenArgs, open_args) -> @@ -477,12 +656,21 @@ open_file(Tab, Args) when is_list(Args) -> open_file(Tab, Arg) -> open_file(Tab, [Arg]). +-spec pid2name(Pid) -> {'ok', Name} | 'undefined' when + Pid :: pid(), + Name :: tab_name(). + pid2name(Pid) -> dets_server:pid2name(Pid). remove_user(Pid, From) -> req(Pid, {close, From}). +-spec repair_continuation(Continuation, MatchSpec) -> Continuation2 when + Continuation :: select_cont(), + Continuation2 :: select_cont(), + MatchSpec :: match_spec(). + repair_continuation(#dets_cont{match_program = B}=Cont, MS) when is_binary(B) -> case ets:is_compiled_ms(B) of @@ -496,25 +684,63 @@ repair_continuation(#dets_cont{}=Cont, _MS) -> repair_continuation(T, MS) -> erlang:error(badarg, [T, MS]). +-spec safe_fixtable(Name, Fix) -> 'ok' when + Name :: tab_name(), + Fix :: boolean(). + safe_fixtable(Tab, Bool) when Bool; not Bool -> badarg(treq(Tab, {safe_fixtable, Bool}), [Tab, Bool]); safe_fixtable(Tab, Term) -> erlang:error(badarg, [Tab, Term]). +-spec select(Name, MatchSpec) -> Selection | {'error', Reason} when + Name :: tab_name(), + MatchSpec :: match_spec(), + Selection :: [term()], + Reason :: term(). + select(Tab, Pat) -> badarg(safe_match(Tab, Pat, select), [Tab, Pat]). +-spec select(Name, MatchSpec, N) -> + {Selection, Continuation} | '$end_of_table' | {'error', Reason} when + Name :: tab_name(), + MatchSpec :: match_spec(), + N :: 'default' | non_neg_integer(), + Continuation :: select_cont(), + Selection :: [term()], + Reason :: term(). + select(Tab, Pat, N) -> badarg(init_chunk_match(Tab, Pat, select, N), [Tab, Pat, N]). +-spec select(Continuation) -> + {Selection, Continuation2} | '$end_of_table' | {'error', Reason} when + Continuation :: select_cont(), + Continuation2 :: select_cont(), + Selection :: [term()], + Reason :: term(). + select(State) when State#dets_cont.what =:= select -> badarg(chunk_match(State), [State]); select(Term) -> erlang:error(badarg, [Term]). +-spec select_delete(Name, MatchSpec) -> N | {'error', Reason} when + Name :: tab_name(), + MatchSpec :: match_spec(), + N :: non_neg_integer(), + Reason :: term(). + select_delete(Tab, Pat) -> badarg(match_delete(Tab, Pat, select), [Tab, Pat]). +-spec slot(Name, I) -> '$end_of_table' | Objects | {'error', Reason} when + Name :: tab_name(), + I :: non_neg_integer(), + Objects :: [object()], + Reason :: term(). + slot(Tab, Slot) when is_integer(Slot), Slot >= 0 -> badarg(treq(Tab, {slot, Slot}), [Tab, Slot]); slot(Tab, Term) -> @@ -529,12 +755,29 @@ stop() -> istart_link(Server) -> {ok, proc_lib:spawn_link(dets, init, [self(), Server])}. +-spec sync(Name) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Reason :: term(). + sync(Tab) -> badarg(treq(Tab, sync), [Tab]). +-spec table(Name) -> QueryHandle when + Name :: tab_name(), + QueryHandle :: qlc:query_handle(). + table(Tab) -> table(Tab, []). +-spec table(Name, Options) -> QueryHandle when + Name :: tab_name(), + Options :: Option | [Option], + Option :: {'n_objects', Limit} + | {'traverse', TraverseMethod}, + Limit :: 'default' | pos_integer(), + TraverseMethod :: 'first_next' | 'select' | {'select', match_spec()}, + QueryHandle :: qlc:query_handle(). + table(Tab, Opts) -> case options(Opts, [traverse, n_objects]) of {badarg,_} -> @@ -612,6 +855,11 @@ table_info(_Tab, _) -> %% End of table/2. +-spec to_ets(Name, EtsTab) -> EtsTab | {'error', Reason} when + Name :: tab_name(), + EtsTab :: ets:tab(), + Reason :: term(). + to_ets(DTab, ETab) -> case ets:info(ETab, protection) of undefined -> @@ -621,6 +869,20 @@ to_ets(DTab, ETab) -> foldl(Fun, ETab, DTab) end. +-spec traverse(Name, Fun) -> Return | {'error', Reason} when + Name :: tab_name(), + Fun :: fun((Object) -> FunReturn), + Object :: object(), + FunReturn :: 'continue' + | {'continue', Val} + | {'done', Value} + | OtherValue, + Return :: [term()] | OtherValue, + Val :: term(), + Value :: term(), + OtherValue :: term(), + Reason :: term(). + traverse(Tab, Fun) -> Ref = make_ref(), TFun = @@ -638,6 +900,14 @@ traverse(Tab, Fun) -> end, do_traverse(TFun, [], Tab, Ref). +-spec update_counter(Name, Key, Increment) -> Result when + Name :: tab_name(), + Key :: term(), + Increment :: {Pos, Incr} | Incr, + Pos :: integer(), + Incr :: integer(), + Result :: integer(). + update_counter(Tab, Key, C) -> badarg(treq(Tab, {update_counter, Key, C}), [Tab, Key, C]). @@ -1484,17 +1754,6 @@ system_code_change(State, _Module, _OldVsn, _Extra) -> %%% Internal functions %%%---------------------------------------------------------------------- -constants(FH, FileName) -> - Version = FH#fileheader.version, - if - Version =< 8 -> - dets_v8:constants(); - Version =:= 9 -> - dets_v9:constants(); - true -> - throw({error, {not_a_dets_file, FileName}}) - end. - %% -> {ok, Fd, fileheader()} | throw(Error) read_file_header(FileName, Access, RamFile) -> BF = if @@ -1572,7 +1831,11 @@ do_bchunk_init(Head, Tab) -> {H2, {error, old_version}}; Parms -> L = dets_utils:all_allocated(H2), - C0 = #dets_cont{no_objs = default, bin = <<>>, alloc = L}, + Bin = if + L =:= <<>> -> eof; + true -> <<>> + end, + C0 = #dets_cont{no_objs = default, bin = Bin, alloc = L}, BinParms = term_to_binary(Parms), {H2, {C0#dets_cont{tab = Tab, proc = self(),what = bchunk}, [BinParms]}} @@ -2205,10 +2468,23 @@ fopen2(Fname, Tab) -> %% Fd is not always closed upon error, but exit is soon called. {ok, Fd, FH} = read_file_header(Fname, Acc, Ram), Mod = FH#fileheader.mod, - case Mod:check_file_header(FH, Fd) of - {error, not_closed} -> - io:format(user,"dets: file ~p not properly closed, " - "repairing ...~n", [Fname]), + Do = case Mod:check_file_header(FH, Fd) of + {ok, Head1, ExtraInfo} -> + Head2 = Head1#head{filename = Fname}, + try {ok, Mod:init_freelist(Head2, ExtraInfo)} + catch + throw:_ -> + {repair, " has bad free lists, repairing ..."} + end; + {error, not_closed} -> + M = " not properly closed, repairing ...", + {repair, M}; + Else -> + Else + end, + case Do of + {repair, Mess} -> + io:format(user, "dets: file ~p~s~n", [Fname, Mess]), Version = default, case fsck(Fd, Tab, Fname, FH, default, default, Version) of ok -> @@ -2216,9 +2492,9 @@ fopen2(Fname, Tab) -> Error -> throw(Error) end; - {ok, Head, ExtraInfo} -> + {ok, Head} -> open_final(Head, Fname, Acc, Ram, ?DEFAULT_CACHE, - Tab, ExtraInfo, false); + Tab, false); {error, Reason} -> throw({error, {Reason, Fname}}) end; @@ -2250,12 +2526,13 @@ fopen_existing_file(Tab, OpenArgs) -> V9 = (Version =:= 9) or (Version =:= default), MinF = (MinSlots =:= default) or (MinSlots =:= FH#fileheader.min_no_slots), MaxF = (MaxSlots =:= default) or (MaxSlots =:= FH#fileheader.max_no_slots), - Do = case (FH#fileheader.mod):check_file_header(FH, Fd) of + Mod = (FH#fileheader.mod), + Wh = case Mod:check_file_header(FH, Fd) of {ok, Head, true} when Rep =:= force, Acc =:= read_write, FH#fileheader.version =:= 9, FH#fileheader.no_colls =/= undefined, MinF, MaxF, V9 -> - {compact, Head}; + {compact, Head, true}; {ok, _Head, _Extra} when Rep =:= force, Acc =:= read -> throw({error, {access_mode, Fname}}); {ok, Head, need_compacting} when Acc =:= read -> @@ -2285,6 +2562,17 @@ fopen_existing_file(Tab, OpenArgs) -> {error, Reason} -> throw({error, {Reason, Fname}}) end, + Do = case Wh of + {Tag, Hd, Extra} when Tag =:= final; Tag =:= compact -> + Hd1 = Hd#head{filename = Fname}, + try {Tag, Mod:init_freelist(Hd1, Extra)} + catch + throw:_ -> + {repair, " has bad free lists, repairing ..."} + end; + Else -> + Else + end, case Do of _ when FH#fileheader.type =/= Type -> throw({error, {type_mismatch, Fname}}); @@ -2293,8 +2581,7 @@ fopen_existing_file(Tab, OpenArgs) -> {compact, SourceHead} -> io:format(user, "dets: file ~p is now compacted ...~n", [Fname]), {ok, NewSourceHead} = open_final(SourceHead, Fname, read, false, - ?DEFAULT_CACHE, Tab, true, - Debug), + ?DEFAULT_CACHE, Tab, Debug), case catch compact(NewSourceHead) of ok -> erlang:garbage_collect(), @@ -2314,9 +2601,9 @@ fopen_existing_file(Tab, OpenArgs) -> Version, OpenArgs); _ when FH#fileheader.version =/= Version, Version =/= default -> throw({error, {version_mismatch, Fname}}); - {final, H, EI} -> + {final, H} -> H1 = H#head{auto_save = Auto}, - open_final(H1, Fname, Acc, Ram, CacheSz, Tab, EI, Debug) + open_final(H1, Fname, Acc, Ram, CacheSz, Tab, Debug) end. do_repair(Fd, Tab, Fname, FH, MinSlots, MaxSlots, Version, OpenArgs) -> @@ -2330,19 +2617,16 @@ do_repair(Fd, Tab, Fname, FH, MinSlots, MaxSlots, Version, OpenArgs) -> end. %% -> {ok, head()} | throw(Error) -open_final(Head, Fname, Acc, Ram, CacheSz, Tab, ExtraInfo, Debug) -> +open_final(Head, Fname, Acc, Ram, CacheSz, Tab, Debug) -> Head1 = Head#head{access = Acc, ram_file = Ram, filename = Fname, name = Tab, cache = dets_utils:new_cache(CacheSz)}, init_disk_map(Head1#head.version, Tab, Debug), - Mod = Head#head.mod, - Mod:cache_segps(Head1#head.fptr, Fname, Head1#head.next), - Ftab = Mod:init_freelist(Head1, ExtraInfo), + (Head1#head.mod):cache_segps(Head1#head.fptr, Fname, Head1#head.next), check_growth(Head1), - NewHead = Head1#head{freelists = Ftab}, - {ok, NewHead}. + {ok, Head1}. %% -> {ok, head()} | throw(Error) fopen_init_file(Tab, OpenArgs) -> @@ -2869,8 +3153,12 @@ init_scan(Head, NoObjs) -> check_safe_fixtable(Head), FreeLists = dets_utils:get_freelists(Head), Base = Head#head.base, - {From, To} = dets_utils:find_next_allocated(FreeLists, Base, Base), - #dets_cont{no_objs = NoObjs, bin = <<>>, alloc = {From, To, <<>>}}. + case dets_utils:find_next_allocated(FreeLists, Base, Base) of + {From, To} -> + #dets_cont{no_objs = NoObjs, bin = <<>>, alloc = {From,To,<<>>}}; + none -> + #dets_cont{no_objs = NoObjs, bin = eof, alloc = <<>>} + end. check_safe_fixtable(Head) -> case (Head#head.fixed =:= false) andalso @@ -2971,18 +3259,20 @@ view(FileName) -> case catch read_file_header(FileName, read, false) of {ok, Fd, FH} -> Mod = FH#fileheader.mod, - case Mod:check_file_header(FH, Fd) of - {ok, H0, ExtraInfo} -> - Ftab = Mod:init_freelist(H0, ExtraInfo), - {_Bump, Base} = constants(FH, FileName), - H = H0#head{freelists=Ftab, base = Base}, - v_free_list(H), - Mod:v_segments(H), - file:close(Fd); - X -> - file:close(Fd), - X - end; + try Mod:check_file_header(FH, Fd) of + {ok, H0, ExtraInfo} -> + Mod = FH#fileheader.mod, + case Mod:check_file_header(FH, Fd) of + {ok, H0, ExtraInfo} -> + H = Mod:init_freelist(H0, ExtraInfo), + v_free_list(H), + Mod:v_segments(H), + ok; + X -> + X + end + after file:close(Fd) + end; X -> X end. diff --git a/lib/stdlib/src/dets.hrl b/lib/stdlib/src/dets.hrl index fbffc9d008..a3f99357a2 100644 --- a/lib/stdlib/src/dets.hrl +++ b/lib/stdlib/src/dets.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2010. All Rights Reserved. +%% Copyright Ericsson AB 2001-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -92,6 +92,7 @@ %% Info extracted from the file header. -record(fileheader, { freelist, + fl_base, cookie, closed_properly, type, diff --git a/lib/stdlib/src/dets_v8.erl b/lib/stdlib/src/dets_v8.erl index af36958c1c..3e962a1c8b 100644 --- a/lib/stdlib/src/dets_v8.erl +++ b/lib/stdlib/src/dets_v8.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2010. All Rights Reserved. +%% Copyright Ericsson AB 2001-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -21,7 +21,7 @@ %% Dets files, implementation part. This module handles versions up to %% and including 8(c). To be called from dets.erl only. --export([constants/0, mark_dirty/1, read_file_header/2, +-export([mark_dirty/1, read_file_header/2, check_file_header/2, do_perform_save/1, initiate_file/11, init_freelist/2, fsck_input/4, bulk_input/3, output_objs/4, write_cache/1, may_grow/3, @@ -163,7 +163,7 @@ %% The 8(c) version uses a different hashing algorithm, erlang:phash %% (former versions use erlang:hash). %% Version 8(b) files are only converted to version 8(c) if repair is -%% done, so we need compatability with 8(b) for a _long_ time. +%% done, so we need compatibility with 8(b) for a _long_ time. %% %% There are known bugs due to the fact that keys and objects are %% sometimes compared (==) and sometimes matched (=:=). The version @@ -196,10 +196,6 @@ %%-define(DEBUGF(X,Y), io:format(X, Y)). -define(DEBUGF(X,Y), void). -%% {Bump} -constants() -> - {?BUMP, ?BASE}. - %% -> ok | throw({NewHead,Error}) mark_dirty(Head) -> Dirty = [{?CLOSED_PROPERLY_POS, <<?NOT_PROPERLY_CLOSED:32>>}], @@ -308,8 +304,9 @@ init_freelist(Head, {convert_freelist,_Version}) -> Pos = Head#head.freelists_p, case catch prterm(Head, Pos, ?OHDSZ) of {0, _Sz, Term} -> - FreeList = lists:reverse(Term), - dets_utils:init_slots_from_old_file(FreeList, Ftab); + FreeList1 = lists:reverse(Term), + FreeList = dets_utils:init_slots_from_old_file(FreeList1, Ftab), + Head#head{freelists = FreeList, base = ?BASE}; _ -> throw({error, {bad_freelists, Head#head.filename}}) end; @@ -318,7 +315,7 @@ init_freelist(Head, _) -> Pos = Head#head.freelists_p, case catch prterm(Head, Pos, ?OHDSZ) of {0, _Sz, Term} -> - Term; + Head#head{freelists = Term, base = ?BASE}; _ -> throw({error, {bad_freelists, Head#head.filename}}) end. @@ -331,6 +328,7 @@ read_file_header(Fd, FileName) -> {ok, EOF} = dets_utils:position_close(Fd, FileName, eof), {ok, <<FileSize:32>>} = dets_utils:pread_close(Fd, FileName, EOF-4, 4), FH = #fileheader{freelist = Freelist, + fl_base = ?BASE, cookie = Cookie, closed_properly = CP, type = dets_utils:code_to_type(Type2), @@ -413,7 +411,7 @@ check_file_header(FH, Fd) -> version = ?FILE_FORMAT_VERSION, mod = ?MODULE, bump = ?BUMP, - base = ?BASE}, + base = FH#fileheader.fl_base}, {ok, H, ExtraInfo}; Error -> Error diff --git a/lib/stdlib/src/dets_v9.erl b/lib/stdlib/src/dets_v9.erl index 132af01f79..f577b4410f 100644 --- a/lib/stdlib/src/dets_v9.erl +++ b/lib/stdlib/src/dets_v9.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2010. All Rights Reserved. +%% Copyright Ericsson AB 2001-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -21,7 +21,7 @@ %% Dets files, implementation part. This module handles version 9. %% To be called from dets.erl only. --export([constants/0, mark_dirty/1, read_file_header/2, +-export([mark_dirty/1, read_file_header/2, check_file_header/2, do_perform_save/1, initiate_file/11, prep_table_copy/9, init_freelist/2, fsck_input/4, bulk_input/3, output_objs/4, bchunk_init/2, @@ -70,6 +70,17 @@ %% 16 MD5-sum for the 44 plus 112 bytes before the MD5-sum. %% (FreelistsPointer, Cookie and ClosedProperly are not digested.) %% 128 Reserved for future versions. Initially zeros. +%% Version 9(d), introduced in R15A, has instead: +%% 112 28 counters for the buddy system sizes (as for 9(b)). +%% 16 MD5-sum for the 44 plus 112 bytes before the MD5-sum. +%% (FreelistsPointer, Cookie and ClosedProperly are not digested.) +%% 4 Base of the buddy system. +%% 0 (zero) if the base is equal to ?BASE. Compatible with R14B. +%% File size at the end of the file is RealFileSize - Base. +%% The reason for modifying file size is that when a file created +%% by R15 is read by R14 a repair takes place immediately, which +%% is acceptable when downgrading. +%% 124 Reserved for future versions. Initially zeros. %% --- %% ------------------ end of file header %% 4*256 SegmentArray Pointers. @@ -86,7 +97,7 @@ %% ----------------------------- %% ??? Free lists %% ----------------------------- -%% 4 File size, in bytes. +%% 4 File size, in bytes. See 9(d) obove. %% Before we can find an object we must find the slot where the %% object resides. Each slot is a (possibly empty) list (or chain) of @@ -177,14 +188,14 @@ %%% File header %%% --define(RESERVED, 128). % Reserved for future use. +-define(RESERVED, 124). % Reserved for future use. -define(COLL_CNTRS, (28*4)). % Counters for the buddy system. -define(MD5SZ, 16). +-define(FL_BASE, 4). --define(HEADSZ, - 56+?COLL_CNTRS+?MD5SZ). % The size of the file header, in bytes, - % not including the reserved part. +-define(HEADSZ, 56+?COLL_CNTRS % The size of the file header, in bytes, + +?MD5SZ+?FL_BASE). % not including the reserved part. -define(HEADEND, (?HEADSZ+?RESERVED)). % End of header and reserved area. -define(SEGSZ, 512). % Size of a segment, in words. SZOBJP*SEGSZP. @@ -270,10 +281,6 @@ %%-define(DEBUGF(X,Y), io:format(X, Y)). -define(DEBUGF(X,Y), void). -%% {Bump} -constants() -> - {?BUMP, ?BASE}. - %% -> ok | throw({NewHead,Error}) mark_dirty(Head) -> Dirty = [{?CLOSED_PROPERLY_POS, <<?NOT_PROPERLY_CLOSED:32>>}], @@ -356,7 +363,7 @@ init_file(Fd, Tab, Fname, Type, Kp, MinSlots, MaxSlots, Ram, CacheSz, cache = dets_utils:new_cache(CacheSz), version = ?FILE_FORMAT_VERSION, bump = ?BUMP, - base = ?BASE, + base = ?BASE, % to be overwritten mod = ?MODULE }, @@ -378,13 +385,20 @@ init_file(Fd, Tab, Fname, Type, Kp, MinSlots, MaxSlots, Ram, CacheSz, {Head1, Ws1} = init_parts(Head0, 0, no_parts(Next), Zero, []), NoSegs = no_segs(Next), - {Head, WsI, WsP} = init_segments(Head1, 0, NoSegs, Zero, [], []), + {Head2, WsI, WsP} = init_segments(Head1, 0, NoSegs, Zero, [], []), Ws2 = if DoInitSegments -> WsP ++ WsI; true -> WsP end, dets_utils:pwrite(Fd, Fname, [W0 | lists:append(Ws1) ++ Ws2]), - true = hash_invars(Head), + true = hash_invars(Head2), + %% The allocations that have been made so far (parts, segments) + %% are permanent; the table will never shrink. Therefore the base + %% of the Buddy system can be set to the first free object. + %% This is used in allocate_all(), see below. + {_, Where, _} = dets_utils:alloc(Head2, ?BUMP), + NewFtab = dets_utils:init_alloc(Where), + Head = Head2#head{freelists = NewFtab, base = Where}, {ok, Head}. %% Returns a power of two not less than 256. @@ -451,8 +465,9 @@ read_file_header(Fd, FileName) -> Version:32, M:32, Next:32, Kp:32, NoObjects:32, NoKeys:32, MinNoSlots:32, MaxNoSlots:32, HashMethod:32, N:32, NoCollsB:?COLL_CNTRS/binary, - MD5:?MD5SZ/binary>> = Bin, - <<_:12/binary,MD5DigestedPart:(?HEADSZ-?MD5SZ-12)/binary,_/binary>> = Bin, + MD5:?MD5SZ/binary, FlBase:32>> = Bin, + <<_:12/binary,MD5DigestedPart:(?HEADSZ-?MD5SZ-?FL_BASE-12)/binary, + _/binary>> = Bin, {ok, EOF} = dets_utils:position_close(Fd, FileName, eof), {ok, <<FileSize:32>>} = dets_utils:pread_close(Fd, FileName, EOF-4, 4), {CL, <<>>} = lists:foldl(fun(LSz, {Acc,<<NN:32,R/binary>>}) -> @@ -468,8 +483,12 @@ read_file_header(Fd, FileName) -> true -> lists:reverse(CL) end, - + Base = case FlBase of + 0 -> ?BASE; + _ -> FlBase + end, FH = #fileheader{freelist = FreeList, + fl_base = Base, cookie = Cookie, closed_properly = CP, type = dets_utils:code_to_type(Type2), @@ -486,7 +505,7 @@ read_file_header(Fd, FileName) -> read_md5 = MD5, has_md5 = <<0:?MD5SZ/unit:8>> =/= MD5, md5 = erlang:md5(MD5DigestedPart), - trailer = FileSize, + trailer = FileSize + FlBase, eof = EOF, n = N, mod = ?MODULE}, @@ -544,7 +563,7 @@ check_file_header(FH, Fd) -> version = ?FILE_FORMAT_VERSION, mod = ?MODULE, bump = ?BUMP, - base = ?BASE}, + base = FH#fileheader.fl_base}, {ok, H, ExtraInfo}; Error -> Error @@ -1185,41 +1204,25 @@ write_loop(Head, BytesToWrite, Bin) -> write_loop(Head, BytesToWrite, SmallBin). %% By allocating bigger objects before smaller ones, holes in the -%% buddy system memory map are avoided. Unfortunately, the segments -%% are always allocated first, so if there are objects bigger than a -%% segment, there is a hole to handle. (Haven't considered placing the -%% segments among other objects of the same size.) +%% buddy system memory map are avoided. allocate_all_objects(Head, SizeT) -> DTL = lists:reverse(lists:keysort(1, ets:tab2list(SizeT))), MaxSz = element(1, hd(DTL)), - SegSize = ?ACTUAL_SEG_SIZE, - {Head1, HSz, HN, HA} = alloc_hole(MaxSz, Head, SegSize), - {Head2, NL} = allocate_all(Head1, DTL, []), + {Head1, NL} = allocate_all(Head, DTL, []), %% Find the position that will be the end of the file by allocating %% a minimal object. - {_Head, EndOfFile, _} = dets_utils:alloc(Head2, ?BUMP), - Head3 = free_hole(Head2, HSz, HN, HA), - NewHead = Head3#head{maxobjsize = max_objsize(Head3#head.no_collections)}, + {_Head, EndOfFile, _} = dets_utils:alloc(Head1, ?BUMP), + NewHead = Head1#head{maxobjsize = max_objsize(Head1#head.no_collections)}, {NewHead, NL, MaxSz, EndOfFile}. -alloc_hole(LSize, Head, SegSz) when ?POW(LSize-1) > SegSz -> - Size = ?POW(LSize-1), - {_, SegAddr, _} = dets_utils:alloc(Head, adjsz(SegSz)), - {_, Addr, _} = dets_utils:alloc(Head, adjsz(Size)), - N = (Addr - SegAddr) div SegSz, - Head1 = dets_utils:alloc_many(Head, SegSz, N, SegAddr), - {Head1, SegSz, N, SegAddr}; -alloc_hole(_MaxSz, Head, _SegSz) -> - {Head, 0, 0, 0}. - -free_hole(Head, _Size, 0, _Addr) -> - Head; -free_hole(Head, Size, N, Addr) -> - {Head1, _} = dets_utils:free(Head, Addr, adjsz(Size)), - free_hole(Head1, Size, N-1, Addr+Size). - %% One (temporary) file for each buddy size, write all objects of that %% size to the file. +%% +%% Before R15 a "hole" was needed before the first bucket if the size +%% of the biggest bucket was greater than the size of a segment. The +%% hole proved to be a problem with almost full tables with huge +%% buckets. Since R15 the hole is no longer needed due to the fact +%% that the base of the Buddy system is flexible. allocate_all(Head, [{?FSCK_SEGMENT,_,Data,_}], L) -> %% And one file for the segments... %% Note that space for the array parts and the segments has @@ -1593,23 +1596,28 @@ do_perform_save(H) -> H1 = H#head{freelists_p = FreeListsPointer}, {FLW, FLSize} = free_lists_to_file(H1), FileSize = FreeListsPointer + FLSize + 4, - ok = dets_utils:write(H1, [FLW | <<FileSize:32>>]), + AdjustedFileSize = case H#head.base of + ?BASE -> FileSize; + Base -> FileSize - Base + end, + ok = dets_utils:write(H1, [FLW | <<AdjustedFileSize:32>>]), FileHeader = file_header(H1, FreeListsPointer, ?CLOSED_PROPERLY), case dets_utils:debug_mode() of true -> - TmpHead = H1#head{freelists = init_freelist(H1, true), - fixed = false}, + TmpHead0 = init_freelist(H1#head{fixed = false}, true), + TmpHead = TmpHead0#head{base = H1#head.base}, case catch dets_utils:all_allocated_as_list(TmpHead) =:= dets_utils:all_allocated_as_list(H1) - of + of true -> dets_utils:pwrite(H1, [{0, FileHeader}]); _ -> + throw( dets_utils:corrupt_reason(H1, {failed_to_save_free_lists, FreeListsPointer, TmpHead#head.freelists, - H1#head.freelists}) + H1#head.freelists})) end; false -> dets_utils:pwrite(H1, [{0, FileHeader}]) @@ -1648,7 +1656,11 @@ file_header(Head, FreeListsPointer, ClosedProperly, NoColls) -> true -> erlang:md5(DigH); false -> <<0:?MD5SZ/unit:8>> end, - [H1, DigH, MD5 | <<0:?RESERVED/unit:8>>]. + Base = case Head#head.base of + ?BASE -> <<0:32>>; + FlBase -> <<FlBase:32>> + end, + [H1, DigH, MD5, Base | <<0:?RESERVED/unit:8>>]. %% Going through some trouble to avoid creating one single binary for %% the free lists. If the free lists are huge, binary_to_term and @@ -1695,8 +1707,8 @@ free_lists_from_file(H, Pos) -> case catch bin_to_tree([], H, start, FL, -1, []) of {'EXIT', _} -> throw({error, {bad_freelists, H#head.filename}}); - Reply -> - Reply + Ftab -> + H#head{freelists = Ftab, base = ?BASE} end. bin_to_tree(Bin, H, LastPos, Ftab, A0, L) -> diff --git a/lib/stdlib/src/dict.erl b/lib/stdlib/src/dict.erl index 7e51141098..2e9eba4bfa 100644 --- a/lib/stdlib/src/dict.erl +++ b/lib/stdlib/src/dict.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. +%% Copyright Ericsson AB 2000-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -80,7 +80,9 @@ new() -> Empty = mk_seg(?seg_size), #dict{empty=Empty,segs={Empty}}. --spec is_key(term(), dict()) -> boolean(). +-spec is_key(Key, Dict) -> boolean() when + Key :: term(), + Dict :: dict(). is_key(Key, D) -> Slot = get_slot(D, Key), @@ -91,21 +93,29 @@ find_key(K, [?kv(K,_Val)|_]) -> true; find_key(K, [_|Bkt]) -> find_key(K, Bkt); find_key(_, []) -> false. --spec to_list(dict()) -> [{term(), term()}]. +-spec to_list(Dict) -> List when + Dict :: dict(), + List :: [{Key :: term(), Value :: term()}]. to_list(D) -> fold(fun (Key, Val, List) -> [{Key,Val}|List] end, [], D). --spec from_list([{term(), term()}]) -> dict(). +-spec from_list(List) -> Dict when + List :: [{Key :: term(), Value :: term()}], + Dict :: dict(). from_list(L) -> lists:foldl(fun ({K,V}, D) -> store(K, V, D) end, new(), L). --spec size(dict()) -> non_neg_integer(). +-spec size(Dict) -> non_neg_integer() when + Dict :: dict(). size(#dict{size=N}) when is_integer(N), N >= 0 -> N. --spec fetch(term(), dict()) -> term(). +-spec fetch(Key, Dict) -> Value when + Key :: term(), + Dict :: dict(), + Value :: term(). fetch(Key, D) -> Slot = get_slot(D, Key), @@ -119,7 +129,10 @@ fetch_val(K, [?kv(K,Val)|_]) -> Val; fetch_val(K, [_|Bkt]) -> fetch_val(K, Bkt); fetch_val(_, []) -> throw(badarg). --spec find(term(), dict()) -> {'ok', term()} | 'error'. +-spec find(Key, Dict) -> {'ok', Value} | 'error' when + Key :: term(), + Dict :: dict(), + Value :: term(). find(Key, D) -> Slot = get_slot(D, Key), @@ -130,12 +143,17 @@ find_val(K, [?kv(K,Val)|_]) -> {ok,Val}; find_val(K, [_|Bkt]) -> find_val(K, Bkt); find_val(_, []) -> error. --spec fetch_keys(dict()) -> [term()]. +-spec fetch_keys(Dict) -> Keys when + Dict :: dict(), + Keys :: [term()]. fetch_keys(D) -> fold(fun (Key, _Val, Keys) -> [Key|Keys] end, [], D). --spec erase(term(), dict()) -> dict(). +-spec erase(Key, Dict1) -> Dict2 when + Key :: term(), + Dict1 :: dict(), + Dict2 :: dict(). %% Erase all elements with key Key. erase(Key, D0) -> @@ -150,7 +168,11 @@ erase_key(Key, [E|Bkt0]) -> {[E|Bkt1],Dc}; erase_key(_, []) -> {[],0}. --spec store(term(), term(), dict()) -> dict(). +-spec store(Key, Value, Dict1) -> Dict2 when + Key :: term(), + Value :: term(), + Dict1 :: dict(), + Dict2 :: dict(). store(Key, Val, D0) -> Slot = get_slot(D0, Key), @@ -166,7 +188,11 @@ store_bkt_val(Key, New, [Other|Bkt0]) -> {[Other|Bkt1],Ic}; store_bkt_val(Key, New, []) -> {[?kv(Key,New)],1}. --spec append(term(), term(), dict()) -> dict(). +-spec append(Key, Value, Dict1) -> Dict2 when + Key :: term(), + Value :: term(), + Dict1 :: dict(), + Dict2 :: dict(). append(Key, Val, D0) -> Slot = get_slot(D0, Key), @@ -182,7 +208,11 @@ append_bkt(Key, Val, [Other|Bkt0]) -> {[Other|Bkt1],Ic}; append_bkt(Key, Val, []) -> {[?kv(Key,[Val])],1}. --spec append_list(term(), [term()], dict()) -> dict(). +-spec append_list(Key, ValList, Dict1) -> Dict2 when + Key :: term(), + ValList :: [Value :: term()], + Dict1 :: dict(), + Dict2 :: dict(). append_list(Key, L, D0) -> Slot = get_slot(D0, Key), @@ -252,7 +282,11 @@ app_list_bkt(Key, L, []) -> {[?kv(Key,L)],1}. %% {Bkt1,Dc} = on_key_bkt(Key, F, Bkt0), %% {[Other|Bkt1],Dc}. --spec update(term(), fun((term()) -> term()), dict()) -> dict(). +-spec update(Key, Fun, Dict1) -> Dict2 when + Key :: term(), + Fun :: fun((Value1 :: term()) -> Value2 :: term()), + Dict1 :: dict(), + Dict2 :: dict(). update(Key, F, D0) -> Slot = get_slot(D0, Key), @@ -271,7 +305,12 @@ update_bkt(Key, F, [Other|Bkt0]) -> update_bkt(_Key, _F, []) -> throw(badarg). --spec update(term(), fun((term()) -> term()), term(), dict()) -> dict(). +-spec update(Key, Fun, Initial, Dict1) -> Dict2 when + Key :: term(), + Initial :: term(), + Fun :: fun((Value1 :: term()) -> Value2 :: term()), + Dict1 :: dict(), + Dict2 :: dict(). update(Key, F, Init, D0) -> Slot = get_slot(D0, Key), @@ -286,7 +325,11 @@ update_bkt(Key, F, I, [Other|Bkt0]) -> {[Other|Bkt1],Ic}; update_bkt(Key, F, I, []) when is_function(F, 1) -> {[?kv(Key,I)],1}. --spec update_counter(term(), number(), dict()) -> dict(). +-spec update_counter(Key, Increment, Dict1) -> Dict2 when + Key :: term(), + Increment :: number(), + Dict1 :: dict(), + Dict2 :: dict(). update_counter(Key, Incr, D0) when is_number(Incr) -> Slot = get_slot(D0, Key), @@ -301,20 +344,38 @@ counter_bkt(Key, I, [Other|Bkt0]) -> {[Other|Bkt1],Ic}; counter_bkt(Key, I, []) -> {[?kv(Key,I)],1}. --spec fold(fun((term(), term(), term()) -> term()), term(), dict()) -> term(). +-spec fold(Fun, Acc0, Dict) -> Acc1 when + Fun :: fun((Key, Value, AccIn) -> AccOut), + Key :: term(), + Value :: term(), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Dict :: dict(). %% Fold function Fun over all "bags" in Table and return Accumulator. fold(F, Acc, D) -> fold_dict(F, Acc, D). --spec map(fun((term(), term()) -> term()), dict()) -> dict(). +-spec map(Fun, Dict1) -> Dict2 when + Fun :: fun((Key :: term(), Value1 :: term()) -> Value2 :: term()), + Dict1 :: dict(), + Dict2 :: dict(). map(F, D) -> map_dict(F, D). --spec filter(fun((term(), term()) -> boolean()), dict()) -> dict(). +-spec filter(Pred, Dict1) -> Dict2 when + Pred :: fun((Key :: term(), Value :: term()) -> boolean()), + Dict1 :: dict(), + Dict2 :: dict(). filter(F, D) -> filter_dict(F, D). --spec merge(fun((term(), term(), term()) -> term()), dict(), dict()) -> dict(). +-spec merge(Fun, Dict1, Dict2) -> Dict3 when + Fun :: fun((Key :: term(), Value1 :: term(), Value2 :: term()) -> Value :: term()), + Dict1 :: dict(), + Dict2 :: dict(), + Dict3 :: dict(). merge(F, D1, D2) when D1#dict.size < D2#dict.size -> fold_dict(fun (K, V1, D) -> diff --git a/lib/stdlib/src/digraph.erl b/lib/stdlib/src/digraph.erl index 5edc868a94..e3f87d2c57 100644 --- a/lib/stdlib/src/digraph.erl +++ b/lib/stdlib/src/digraph.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -53,7 +53,8 @@ -type label() :: term(). -type vertex() :: term(). --type add_edge_err_rsn() :: {'bad_edge', [vertex()]} | {'bad_vertex', vertex()}. +-type add_edge_err_rsn() :: {'bad_edge', Path :: [vertex()]} + | {'bad_vertex', V :: vertex()}. %% %% Type is a list of @@ -70,7 +71,8 @@ new() -> new([]). --spec new([d_type()]) -> digraph(). +-spec new(Type) -> digraph() when + Type :: [d_type()]. new(Type) -> case check_type(Type, protected, []) of @@ -113,16 +115,20 @@ set_type([], G) -> G. %% Data access functions --spec delete(digraph()) -> 'true'. +-spec delete(G) -> 'true' when + G :: digraph(). delete(G) -> ets:delete(G#digraph.vtab), ets:delete(G#digraph.etab), ets:delete(G#digraph.ntab). --spec info(digraph()) -> [{'cyclicity', d_cyclicity()} | - {'memory', non_neg_integer()} | - {'protection', d_protection()}]. +-spec info(G) -> InfoList when + G :: digraph(), + InfoList :: [{'cyclicity', Cyclicity :: d_cyclicity()} | + {'memory', NoWords :: non_neg_integer()} | + {'protection', Protection :: d_protection()}]. + info(G) -> VT = G#digraph.vtab, ET = G#digraph.etab, @@ -135,32 +141,45 @@ info(G) -> Memory = ets:info(VT, memory) + ets:info(ET, memory) + ets:info(NT, memory), [{cyclicity, Cyclicity}, {memory, Memory}, {protection, Protection}]. --spec add_vertex(digraph()) -> vertex(). +-spec add_vertex(G) -> vertex() when + G :: digraph(). add_vertex(G) -> do_add_vertex({new_vertex_id(G), []}, G). --spec add_vertex(digraph(), vertex()) -> vertex(). +-spec add_vertex(G, V) -> vertex() when + G :: digraph(), + V :: vertex(). add_vertex(G, V) -> do_add_vertex({V, []}, G). --spec add_vertex(digraph(), vertex(), label()) -> vertex(). +-spec add_vertex(G, V, Label) -> vertex() when + G :: digraph(), + V :: vertex(), + Label :: label(). add_vertex(G, V, D) -> do_add_vertex({V, D}, G). --spec del_vertex(digraph(), vertex()) -> 'true'. +-spec del_vertex(G, V) -> 'true' when + G :: digraph(), + V :: vertex(). del_vertex(G, V) -> do_del_vertex(V, G). --spec del_vertices(digraph(), [vertex()]) -> 'true'. +-spec del_vertices(G, Vertices) -> 'true' when + G :: digraph(), + Vertices :: [vertex()]. del_vertices(G, Vs) -> do_del_vertices(Vs, G). --spec vertex(digraph(), vertex()) -> {vertex(), label()} | 'false'. +-spec vertex(G, V) -> {V, Label} | 'false' when + G :: digraph(), + V :: vertex(), + Label :: label(). vertex(G, V) -> case ets:lookup(G#digraph.vtab, V) of @@ -168,12 +187,15 @@ vertex(G, V) -> [Vertex] -> Vertex end. --spec no_vertices(digraph()) -> non_neg_integer(). +-spec no_vertices(G) -> non_neg_integer() when + G :: digraph(). no_vertices(G) -> ets:info(G#digraph.vtab, size). --spec vertices(digraph()) -> [vertex()]. +-spec vertices(G) -> Vertices when + G :: digraph(), + Vertices :: [vertex()]. vertices(G) -> ets:select(G#digraph.vtab, [{{'$1', '_'}, [], ['$1']}]). @@ -188,85 +210,125 @@ source_vertices(G) -> sink_vertices(G) -> collect_vertices(G, out). --spec in_degree(digraph(), vertex()) -> non_neg_integer(). +-spec in_degree(G, V) -> non_neg_integer() when + G :: digraph(), + V :: vertex(). in_degree(G, V) -> length(ets:lookup(G#digraph.ntab, {in, V})). --spec in_neighbours(digraph(), vertex()) -> [vertex()]. +-spec in_neighbours(G, V) -> Vertex when + G :: digraph(), + V :: vertex(), + Vertex :: [vertex()]. in_neighbours(G, V) -> ET = G#digraph.etab, NT = G#digraph.ntab, collect_elems(ets:lookup(NT, {in, V}), ET, 2). --spec in_edges(digraph(), vertex()) -> [edge()]. +-spec in_edges(G, V) -> Edges when + G :: digraph(), + V :: vertex(), + Edges :: [edge()]. in_edges(G, V) -> ets:select(G#digraph.ntab, [{{{in, V}, '$1'}, [], ['$1']}]). --spec out_degree(digraph(), vertex()) -> non_neg_integer(). +-spec out_degree(G, V) -> non_neg_integer() when + G :: digraph(), + V :: vertex(). out_degree(G, V) -> length(ets:lookup(G#digraph.ntab, {out, V})). --spec out_neighbours(digraph(), vertex()) -> [vertex()]. +-spec out_neighbours(G, V) -> Vertices when + G :: digraph(), + V :: vertex(), + Vertices :: [vertex()]. out_neighbours(G, V) -> ET = G#digraph.etab, NT = G#digraph.ntab, collect_elems(ets:lookup(NT, {out, V}), ET, 3). --spec out_edges(digraph(), vertex()) -> [edge()]. +-spec out_edges(G, V) -> Edges when + G :: digraph(), + V :: vertex(), + Edges :: [edge()]. out_edges(G, V) -> ets:select(G#digraph.ntab, [{{{out, V}, '$1'}, [], ['$1']}]). --spec add_edge(digraph(), vertex(), vertex()) -> - edge() | {'error', add_edge_err_rsn()}. +-spec add_edge(G, V1, V2) -> edge() | {'error', add_edge_err_rsn()} when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(). add_edge(G, V1, V2) -> do_add_edge({new_edge_id(G), V1, V2, []}, G). --spec add_edge(digraph(), vertex(), vertex(), label()) -> - edge() | {'error', add_edge_err_rsn()}. +-spec add_edge(G, V1, V2, Label) -> edge() | {'error', add_edge_err_rsn()} when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(), + Label :: label(). add_edge(G, V1, V2, D) -> do_add_edge({new_edge_id(G), V1, V2, D}, G). --spec add_edge(digraph(), edge(), vertex(), vertex(), label()) -> - edge() | {'error', add_edge_err_rsn()}. +-spec add_edge(G, E, V1, V2, Label) -> edge() | {'error', add_edge_err_rsn()} when + G :: digraph(), + E :: edge(), + V1 :: vertex(), + V2 :: vertex(), + Label :: label(). add_edge(G, E, V1, V2, D) -> do_add_edge({E, V1, V2, D}, G). --spec del_edge(digraph(), edge()) -> 'true'. +-spec del_edge(G, E) -> 'true' when + G :: digraph(), + E :: edge(). del_edge(G, E) -> do_del_edges([E], G). --spec del_edges(digraph(), [edge()]) -> 'true'. +-spec del_edges(G, Edges) -> 'true' when + G :: digraph(), + Edges :: [edge()]. del_edges(G, Es) -> do_del_edges(Es, G). --spec no_edges(digraph()) -> non_neg_integer(). +-spec no_edges(G) -> non_neg_integer() when + G :: digraph(). no_edges(G) -> ets:info(G#digraph.etab, size). --spec edges(digraph()) -> [edge()]. +-spec edges(G) -> Edges when + G :: digraph(), + Edges :: [edge()]. edges(G) -> ets:select(G#digraph.etab, [{{'$1', '_', '_', '_'}, [], ['$1']}]). --spec edges(digraph(), vertex()) -> [edge()]. +-spec edges(G, V) -> Edges when + G :: digraph(), + V :: vertex(), + Edges :: [edge()]. edges(G, V) -> ets:select(G#digraph.ntab, [{{{out, V},'$1'}, [], ['$1']}, {{{in, V}, '$1'}, [], ['$1']}]). --spec edge(digraph(), edge()) -> {edge(),vertex(),vertex(),label()} | 'false'. +-spec edge(G, E) -> {E, V1, V2, Label} | 'false' when + G :: digraph(), + E :: edge(), + V1 :: vertex(), + V2 :: vertex(), + Label :: label(). edge(G, E) -> case ets:lookup(G#digraph.etab,E) of @@ -277,7 +339,7 @@ edge(G, E) -> %% %% Generate a "unique" edge identifier (relative to this graph) %% --spec new_edge_id(digraph()) -> nonempty_improper_list('$e', non_neg_integer()). +-spec new_edge_id(digraph()) -> edge(). new_edge_id(G) -> NT = G#digraph.ntab, @@ -289,7 +351,7 @@ new_edge_id(G) -> %% %% Generate a "unique" vertex identifier (relative to this graph) %% --spec new_vertex_id(digraph()) -> nonempty_improper_list('$v', non_neg_integer()). +-spec new_vertex_id(digraph()) -> vertex(). new_vertex_id(G) -> NT = G#digraph.ntab, @@ -444,7 +506,10 @@ acyclic_add_edge(E, V1, V2, Label, G) -> %% Delete all paths from vertex V1 to vertex V2 %% --spec del_path(digraph(), vertex(), vertex()) -> 'true'. +-spec del_path(G, V1, V2) -> 'true' when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(). del_path(G, V1, V2) -> case get_path(G, V1, V2) of @@ -463,7 +528,10 @@ del_path(G, V1, V2) -> %% been searched. %% --spec get_cycle(digraph(), vertex()) -> [vertex(),...] | 'false'. +-spec get_cycle(G, V) -> Vertices | 'false' when + G :: digraph(), + V :: vertex(), + Vertices :: [vertex(),...]. get_cycle(G, V) -> case one_path(out_neighbours(G, V), V, [], [V], [V], 2, G, 1) of @@ -481,7 +549,11 @@ get_cycle(G, V) -> %% if no path exists false is returned %% --spec get_path(digraph(), vertex(), vertex()) -> [vertex(),...] | 'false'. +-spec get_path(G, V1, V2) -> Vertices | 'false' when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(), + Vertices :: [vertex(),...]. get_path(G, V1, V2) -> one_path(out_neighbours(G, V1), V2, [], [V1], [V1], 1, G, 1). @@ -516,7 +588,10 @@ one_path([], _, [], _, _, _, _, _Counter) -> false. %% Like get_cycle/2, but a cycle of length one is preferred. %% --spec get_short_cycle(digraph(), vertex()) -> [vertex(),...] | 'false'. +-spec get_short_cycle(G, V) -> Vertices | 'false' when + G :: digraph(), + V :: vertex(), + Vertices :: [vertex(),...]. get_short_cycle(G, V) -> get_short_path(G, V, V). @@ -526,7 +601,11 @@ get_short_cycle(G, V) -> %% to find a short path. %% --spec get_short_path(digraph(), vertex(), vertex()) -> [vertex(),...] | 'false'. +-spec get_short_path(G, V1, V2) -> Vertices | 'false' when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(), + Vertices :: [vertex(),...]. get_short_path(G, V1, V2) -> T = new(), diff --git a/lib/stdlib/src/digraph_utils.erl b/lib/stdlib/src/digraph_utils.erl index 080cae4742..e221be15a1 100644 --- a/lib/stdlib/src/digraph_utils.erl +++ b/lib/stdlib/src/digraph_utils.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -38,48 +38,66 @@ %% A convenient type alias %% --type vertices() :: [digraph:vertex()]. - %% %% Exported functions %% --spec components(digraph()) -> vertices(). +-spec components(Digraph) -> [Component] when + Digraph :: digraph(), + Component :: [digraph:vertex()]. components(G) -> forest(G, fun inout/3). --spec strong_components(digraph()) -> vertices(). +-spec strong_components(Digraph) -> [StrongComponent] when + Digraph :: digraph(), + StrongComponent :: [digraph:vertex()]. strong_components(G) -> forest(G, fun in/3, revpostorder(G)). --spec cyclic_strong_components(digraph()) -> vertices(). +-spec cyclic_strong_components(Digraph) -> [StrongComponent] when + Digraph :: digraph(), + StrongComponent :: [digraph:vertex()]. cyclic_strong_components(G) -> remove_singletons(strong_components(G), G, []). --spec reachable(vertices(), digraph()) -> vertices(). +-spec reachable(Vertices, Digraph) -> Reachable when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + Reachable :: [digraph:vertex()]. reachable(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun out/3, Vs, first)). --spec reachable_neighbours(vertices(), digraph()) -> vertices(). +-spec reachable_neighbours(Vertices, Digraph) -> Reachable when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + Reachable :: [digraph:vertex()]. reachable_neighbours(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun out/3, Vs, not_first)). --spec reaching(vertices(), digraph()) -> vertices(). +-spec reaching(Vertices, Digraph) -> Reaching when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + Reaching :: [digraph:vertex()]. reaching(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun in/3, Vs, first)). --spec reaching_neighbours(vertices(), digraph()) -> vertices(). +-spec reaching_neighbours(Vertices, Digraph) -> Reaching when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + Reaching :: [digraph:vertex()]. reaching_neighbours(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun in/3, Vs, not_first)). --spec topsort(digraph()) -> vertices() | 'false'. +-spec topsort(Digraph) -> Vertices | 'false' when + Digraph :: digraph(), + Vertices :: [digraph:vertex()]. topsort(G) -> L = revpostorder(G), @@ -88,12 +106,15 @@ topsort(G) -> false -> false end. --spec is_acyclic(digraph()) -> boolean(). +-spec is_acyclic(Digraph) -> boolean() when + Digraph :: digraph(). is_acyclic(G) -> loop_vertices(G) =:= [] andalso topsort(G) =/= false. --spec arborescence_root(digraph()) -> 'no' | {'yes', digraph:vertex()}. +-spec arborescence_root(Digraph) -> 'no' | {'yes', Root} when + Digraph :: digraph(), + Root :: digraph:vertex(). arborescence_root(G) -> case digraph:no_edges(G) =:= digraph:no_vertices(G) - 1 of @@ -114,23 +135,30 @@ arborescence_root(G) -> no end. --spec is_arborescence(digraph()) -> boolean(). +-spec is_arborescence(Digraph) -> boolean() when + Digraph :: digraph(). is_arborescence(G) -> arborescence_root(G) =/= no. --spec is_tree(digraph()) -> boolean(). +-spec is_tree(Digraph) -> boolean() when + Digraph :: digraph(). is_tree(G) -> (digraph:no_edges(G) =:= digraph:no_vertices(G) - 1) andalso (length(components(G)) =:= 1). --spec loop_vertices(digraph()) -> vertices(). +-spec loop_vertices(Digraph) -> Vertices when + Digraph :: digraph(), + Vertices :: [digraph:vertex()]. loop_vertices(G) -> [V || V <- digraph:vertices(G), is_reflexive_vertex(V, G)]. --spec subgraph(digraph(), vertices()) -> digraph(). +-spec subgraph(Digraph, Vertices) -> SubGraph when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + SubGraph :: digraph(). subgraph(G, Vs) -> try @@ -140,10 +168,12 @@ subgraph(G, Vs) -> erlang:error(badarg) end. --type option() :: {'type', 'inherit' | [digraph:d_type()]} - | {'keep_labels', boolean()}. - --spec subgraph(digraph(), vertices(), [option()]) -> digraph(). +-spec subgraph(Digraph, Vertices, Options) -> SubGraph when + Digraph :: digraph(), + SubGraph :: digraph(), + Vertices :: [digraph:vertex()], + Options :: [{'type', SubgraphType} | {'keep_labels', boolean()}], + SubgraphType :: 'inherit' | [digraph:d_type()]. subgraph(G, Vs, Opts) -> try @@ -153,7 +183,9 @@ subgraph(G, Vs, Opts) -> erlang:error(badarg) end. --spec condensation(digraph()) -> digraph(). +-spec condensation(Digraph) -> CondensedDigraph when + Digraph :: digraph(), + CondensedDigraph :: digraph(). condensation(G) -> SCs = strong_components(G), @@ -176,12 +208,16 @@ condensation(G) -> ets:delete(I2C), SCG. --spec preorder(digraph()) -> vertices(). +-spec preorder(Digraph) -> Vertices when + Digraph :: digraph(), + Vertices :: [digraph:vertex()]. preorder(G) -> lists:reverse(revpreorder(G)). --spec postorder(digraph()) -> vertices(). +-spec postorder(Digraph) -> Vertices when + Digraph :: digraph(), + Vertices :: [digraph:vertex()]. postorder(G) -> lists:reverse(revpostorder(G)). diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl index e5ccaddbb4..230a4a0612 100644 --- a/lib/stdlib/src/epp.erl +++ b/lib/stdlib/src/epp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -29,6 +29,7 @@ %%------------------------------------------------------------------------ -type macros() :: [{atom(), term()}]. +-type epp_handle() :: pid(). %% Epp state record. -record(epp, {file, %Current file @@ -61,14 +62,23 @@ %% parse_file(FileName, IncludePath, PreDefMacros) %% macro_defs(Epp) --spec open(file:name(), [file:name()]) -> - {'ok', pid()} | {'error', term()}. +-spec open(FileName, IncludePath) -> + {'ok', Epp} | {'error', ErrorDescriptor} when + FileName :: file:name(), + IncludePath :: [DirectoryName :: file:name()], + Epp :: epp_handle(), + ErrorDescriptor :: term(). open(Name, Path) -> open(Name, Path, []). --spec open(file:name(), [file:name()], macros()) -> - {'ok', pid()} | {'error', term()}. +-spec open(FileName, IncludePath, PredefMacros) -> + {'ok', Epp} | {'error', ErrorDescriptor} when + FileName :: file:name(), + IncludePath :: [DirectoryName :: file:name()], + PredefMacros :: macros(), + Epp :: epp_handle(), + ErrorDescriptor :: term(). open(Name, Path, Pdm) -> Self = self(), @@ -80,7 +90,8 @@ open(Name, File, StartLocation, Path, Pdm) -> Epp = spawn(fun() -> server(Self, Name, File, StartLocation,Path,Pdm) end), epp_request(Epp). --spec close(pid()) -> 'ok'. +-spec close(Epp) -> 'ok' when + Epp :: epp_handle(). close(Epp) -> %% Make sure that close is synchronous as a courtesy to test @@ -93,6 +104,13 @@ close(Epp) -> scan_erl_form(Epp) -> epp_request(Epp, scan_erl_form). +-spec parse_erl_form(Epp) -> + {'ok', AbsForm} | {'eof', Line} | {error, ErrorInfo} when + Epp :: epp_handle(), + AbsForm :: erl_parse:abstract_form(), + Line :: erl_scan:line(), + ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(). + parse_erl_form(Epp) -> case epp_request(Epp, scan_erl_form) of {ok,Toks} -> @@ -107,6 +125,9 @@ macro_defs(Epp) -> %% format_error(ErrorDescriptor) -> String %% Return a string describing the error. +-spec format_error(ErrorDescriptor) -> io_lib:chars() when + ErrorDescriptor :: term(). + format_error(cannot_parse) -> io_lib:format("cannot parse file, giving up", []); format_error({bad,W}) -> @@ -146,6 +167,16 @@ format_error(E) -> file:format_error(E). %% parse_file(FileName, IncludePath, [PreDefMacro]) -> %% {ok,[Form]} | {error,OpenError} +-spec parse_file(FileName, IncludePath, PredefMacros) -> + {'ok', [Form]} | {error, OpenError} when + FileName :: file:name(), + IncludePath :: [DirectoryName :: file:name()], + Form :: erl_parse:abstract_form() | {'error', ErrorInfo} | {'eof',Line}, + PredefMacros :: macros(), + Line :: erl_scan:line(), + ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(), + OpenError :: file:posix() | badarg | system_limit. + parse_file(Ifile, Path, Predefs) -> case open(Ifile, Path, Predefs) of {ok,Epp} -> @@ -653,7 +684,7 @@ scan_include_lib([{'(',_Llp},{string,_Lf,NewName0},{')',_Lrp},{dot,_Ld}], {error,_E1} -> case catch find_lib_dir(NewName) of {LibDir, Rest} when is_list(LibDir) -> - LibName = filename:join([LibDir | Rest]), + LibName = fname_join([LibDir | Rest]), case file:open(LibName, [read]) of {ok,NewF} -> ExtraPath = [filename:dirname(LibName)], @@ -1123,7 +1154,12 @@ expand_var1(NewName) -> [[$$ | Var] | Rest] = filename:split(NewName), Value = os:getenv(Var), true = Value =/= false, - {ok, filename:join([Value | Rest])}. + {ok, fname_join([Value | Rest])}. + +fname_join(["." | [_|_]=Rest]) -> + fname_join(Rest); +fname_join(Components) -> + filename:join(Components). %% The line only. (Other tokens may have the column and text as well...) loc_attr(Line) when is_integer(Line) -> diff --git a/lib/stdlib/src/erl_compile.erl b/lib/stdlib/src/erl_compile.erl index abff37e4bc..ff032b129c 100644 --- a/lib/stdlib/src/erl_compile.erl +++ b/lib/stdlib/src/erl_compile.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -41,7 +41,6 @@ compiler(".idl") -> {ic, compile}; compiler(".asn1") -> {asn1ct, compile_asn1}; compiler(".asn") -> {asn1ct, compile_asn}; compiler(".py") -> {asn1ct, compile_py}; -compiler(".xml") -> {xmerl_scan, process}; compiler(_) -> no. %% Entry from command line. diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl index ea1b179ee5..4f4fa16040 100644 --- a/lib/stdlib/src/erl_eval.erl +++ b/lib/stdlib/src/erl_eval.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -34,6 +34,37 @@ -import(lists, [reverse/1,foldl/3,member/2]). +-export_type([binding_struct/0]). + +-type(expression() :: erl_parse:abstract_expr()). +-type(expressions() :: [erl_parse:abstract_expr()]). +-type(expression_list() :: [expression()]). +-type(clauses() :: [erl_parse:abstract_clause()]). +-type(name() :: term()). +-type(value() :: term()). +-type(bindings() :: [{name(), value()}]). +-type(binding_struct() :: orddict:orddict()). + +-type(lfun_value_handler() :: fun((Name :: atom(), + Arguments :: [term()]) -> + Value :: value())). +-type(lfun_eval_handler() :: fun((Name :: atom(), + Arguments :: expression_list(), + Bindings :: binding_struct()) -> + {value, + Value :: value(), + NewBindings :: binding_struct()})). +-type(local_function_handler() :: {value, lfun_value_handler()} + | {eval, lfun_eval_handler()} + | none). + +-type(func_spec() :: {Module :: module(), Function :: atom()} | function()). +-type(nlfun_handler() :: fun((FuncSpec :: func_spec(), + Arguments :: [term()]) -> + term())). +-type(non_local_function_handler() :: {value, nlfun_handler()} + | none). + %% exprs(ExpressionSeq, Bindings) %% exprs(ExpressionSeq, Bindings, LocalFuncHandler) %% exprs(ExpressionSeq, Bindings, LocalFuncHandler, ExternalFuncHandler) @@ -45,6 +76,11 @@ %% that there are valid constructs in Expression to be taken care of %% by a function handler but considerad errors by erl_lint. +-spec(exprs(Expressions, Bindings) -> {value, Value, NewBindings} when + Expressions :: expressions(), + Bindings :: binding_struct(), + Value :: value(), + NewBindings :: binding_struct()). exprs(Exprs, Bs) -> case check_command(Exprs, Bs) of ok -> @@ -53,9 +89,25 @@ exprs(Exprs, Bs) -> erlang:raise(error, Error, [{?MODULE,exprs,2}]) end. +-spec(exprs(Expressions, Bindings, LocalFunctionHandler) -> + {value, Value, NewBindings} when + Expressions :: expressions(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + Value :: value(), + NewBindings :: binding_struct()). exprs(Exprs, Bs, Lf) -> exprs(Exprs, Bs, Lf, none, none). +-spec(exprs(Expressions, Bindings, LocalFunctionHandler, + NonLocalFunctionHandler) -> + {value, Value, NewBindings} when + Expressions :: expressions(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + NonLocalFunctionHandler :: non_local_function_handler(), + Value :: value(), + NewBindings :: binding_struct()). exprs(Exprs, Bs, Lf, Ef) -> exprs(Exprs, Bs, Lf, Ef, none). @@ -75,6 +127,11 @@ exprs([E|Es], Bs0, Lf, Ef, RBs) -> %% %% Only expr/2 checks the command by calling erl_lint. See exprs/2. +-spec(expr(Expression, Bindings) -> {value, Value, NewBindings} when + Expression :: expression(), + Bindings :: binding_struct(), + Value :: value(), + NewBindings :: binding_struct()). expr(E, Bs) -> case check_command([E], Bs) of ok -> @@ -83,9 +140,25 @@ expr(E, Bs) -> erlang:raise(error, Error, [{?MODULE,expr,2}]) end. +-spec(expr(Expression, Bindings, LocalFunctionHandler) -> + {value, Value, NewBindings} when + Expression :: expression(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + Value :: value(), + NewBindings :: binding_struct()). expr(E, Bs, Lf) -> expr(E, Bs, Lf, none, none). +-spec(expr(Expression, Bindings, LocalFunctionHandler, + NonLocalFunctionHandler) -> + {value, Value, NewBindings} when + Expression :: expression(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + NonLocalFunctionHandler :: non_local_function_handler(), + Value :: value(), + NewBindings :: binding_struct()). expr(E, Bs, Lf, Ef) -> expr(E, Bs, Lf, Ef, none). @@ -114,6 +187,16 @@ fun_data(F) when is_function(F) -> fun_data(_T) -> false. +-spec(expr(Expression, Bindings, LocalFunctionHandler, + NonLocalFunctionHandler, ReturnFormat) -> + {value, Value, NewBindings} | Value when + Expression :: expression(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + NonLocalFunctionHandler :: non_local_function_handler(), + ReturnFormat :: none | value, + Value :: value(), + NewBindings :: binding_struct()). expr({var,_,V}, Bs, _Lf, _Ef, RBs) -> case binding(V, Bs) of {value,Val} -> @@ -384,12 +467,9 @@ local_func(Func, As0, Bs0, {value,F}, value) -> local_func(Func, As0, Bs0, {value,F}, RBs) -> {As1,Bs1} = expr_list(As0, Bs0, {value,F}), ret_expr(F(Func, As1), Bs1, RBs); -local_func(Func, As0, Bs0, {value,F,Eas}, value) -> - {As1,_Bs1} = expr_list(As0, Bs0, {value,F,Eas}), - apply(F, [Func,As1|Eas]); local_func(Func, As0, Bs0, {value,F,Eas}, RBs) -> - {As1,Bs1} = expr_list(As0, Bs0, {value,F,Eas}), - ret_expr(apply(F, [Func,As1|Eas]), Bs1, RBs); + Fun = fun(Name, Args) -> apply(F, [Name,Args|Eas]) end, + local_func(Func, As0, Bs0, {value, Fun}, RBs); local_func(Func, As, Bs, {eval,F}, RBs) -> local_func2(F(Func, As, Bs), RBs); local_func(Func, As, Bs, {eval,F,Eas}, RBs) -> @@ -541,7 +621,7 @@ eval_generate(Term, _P, _Bs0, _Lf, _Ef, _CompFun, _Acc) -> erlang:raise(error, {bad_generator,Term}, stacktrace()). eval_b_generate(<<_/bitstring>>=Bin, P, Bs0, Lf, Ef, CompFun, Acc) -> - Mfun = fun(L, R, Bs) -> match1(L, R, Bs, Bs0) end, + Mfun = match_fun(Bs0), Efun = fun(Exp, Bs) -> expr(Exp, Bs, Lf, Ef, none) end, case eval_bits:bin_gen(P, Bin, new_bindings(), Bs0, Mfun, Efun) of {match, Rest, Bs1} -> @@ -613,12 +693,33 @@ eval_fun([], As, _Bs, _Lf, _Ef, _RBs) -> %% expr_list(ExpressionList, Bindings, LocalFuncHandler, ExternalFuncHandler) %% Evaluate a list of expressions "in parallel" at the same level. +-spec(expr_list(ExpressionList, Bindings) -> {ValueList, NewBindings} when + ExpressionList :: expression_list(), + Bindings :: binding_struct(), + ValueList :: [value()], + NewBindings :: binding_struct()). expr_list(Es, Bs) -> expr_list(Es, Bs, none, none). +-spec(expr_list(ExpressionList, Bindings, LocalFunctionHandler) -> + {ValueList, NewBindings} when + ExpressionList :: expression_list(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + ValueList :: [value()], + NewBindings :: binding_struct()). expr_list(Es, Bs, Lf) -> expr_list(Es, Bs, Lf, none). +-spec(expr_list(ExpressionList, Bindings, LocalFunctionHandler, + NonLocalFunctionHandler) -> + {ValueList, NewBindings} when + ExpressionList :: expression_list(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + NonLocalFunctionHandler :: non_local_function_handler(), + ValueList :: [value()], + NewBindings :: binding_struct()). expr_list(Es, Bs, Lf, Ef) -> expr_list(Es, [], Bs, Bs, Lf, Ef). @@ -757,6 +858,15 @@ send_all([], _) -> true. %% match_clause -> {Body, Bindings} or nomatch +-spec(match_clause(Clauses, ValueList, Bindings, LocalFunctionHandler) -> + {Body, NewBindings} | nomatch when + Clauses :: clauses(), + ValueList :: [value()], + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + Body :: expression_list(), + NewBindings :: binding_struct()). + match_clause(Cs, Vs, Bs, Lf) -> match_clause(Cs, Vs, Bs, Lf, none). @@ -914,7 +1024,7 @@ match1({tuple,_,_}, _, _Bs, _BBs) -> throw(nomatch); match1({bin, _, Fs}, <<_/bitstring>>=B, Bs0, BBs) -> eval_bits:match_bits(Fs, B, Bs0, BBs, - fun(L, R, Bs) -> match1(L, R, Bs, BBs) end, + match_fun(BBs), fun(E, Bs) -> expr(E, Bs, none, none, none) end); match1({bin,_,_}, _, _Bs, _BBs) -> throw(nomatch); @@ -943,6 +1053,12 @@ match1({op,Line,Op,L,R}, Term, Bs, BBs) -> match1(_, _, _Bs, _BBs) -> throw(invalid). +match_fun(BBs) -> + fun(match, {L,R,Bs}) -> match1(L, R, Bs, BBs); + (binding, {Name,Bs}) -> binding(Name, Bs); + (add_binding, {Name,Val,Bs}) -> add_binding(Name, Val, Bs) + end. + match_tuple([E|Es], Tuple, I, Bs0, BBs) -> {match,Bs} = match1(E, element(I, Tuple), Bs0, BBs), match_tuple(Es, Tuple, I+1, Bs, BBs); @@ -973,18 +1089,30 @@ match_list(_, _, _Bs, _BBs) -> %% add_binding(Name, Value, Bindings) %% del_binding(Name, Bindings) +-spec(new_bindings() -> binding_struct()). new_bindings() -> orddict:new(). +-spec(bindings(BindingStruct :: binding_struct()) -> bindings()). bindings(Bs) -> orddict:to_list(Bs). +-spec(binding(Name, BindingStruct) -> {value, value()} | unbound when + Name :: name(), + BindingStruct :: binding_struct()). binding(Name, Bs) -> case orddict:find(Name, Bs) of {ok,Val} -> {value,Val}; error -> unbound end. +-spec(add_binding(Name, Value, BindingStruct) -> binding_struct() when + Name :: name(), + Value :: value(), + BindingStruct :: binding_struct()). add_binding(Name, Val, Bs) -> orddict:store(Name, Val, Bs). +-spec(del_binding(Name, BindingStruct) -> binding_struct() when + Name :: name(), + BindingStruct :: binding_struct()). del_binding(Name, Bs) -> orddict:erase(Name, Bs). add_bindings(Bs1, Bs2) -> diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl index 61ce41f714..20fd247cea 100644 --- a/lib/stdlib/src/erl_expand_records.erl +++ b/lib/stdlib/src/erl_expand_records.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -35,9 +35,13 @@ trecords=sets:new(), % Typed records uses_types=false, % Are there -spec or -type in the module strict_ra=[], % strict record accesses - checked_ra=[] % succesfully accessed records + checked_ra=[] % successfully accessed records }). +-spec(module(AbsForms, CompileOptions) -> AbsForms when + AbsForms :: [erl_parse:abstract_form()], + CompileOptions :: [compile:option()]). + %% Is is assumed that Fs is a valid list of forms. It should pass %% erl_lint without errors. module(Fs0, Opts0) -> diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl index b30b02a96f..cd3b531d10 100644 --- a/lib/stdlib/src/erl_internal.erl +++ b/lib/stdlib/src/erl_internal.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% Copyright Ericsson AB 1998-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -54,7 +54,9 @@ %%--------------------------------------------------------------------------- %% Erlang builtin functions allowed in guards. --spec guard_bif(Name::atom(), Arity::arity()) -> boolean(). +-spec guard_bif(Name, Arity) -> boolean() when + Name :: atom(), + Arity :: arity(). guard_bif(abs, 1) -> true; guard_bif(float, 1) -> true; @@ -92,7 +94,9 @@ guard_bif(binary_part, 3) -> true; guard_bif(Name, A) when is_atom(Name), is_integer(A) -> false. %% Erlang type tests. --spec type_test(Name::atom(), Arity::arity()) -> boolean(). +-spec type_test(Name, Arity) -> boolean() when + Name :: atom(), + Arity :: arity(). type_test(Name, Arity) -> new_type_test(Name, Arity) orelse old_type_test(Name, Arity). @@ -135,7 +139,9 @@ old_type_test(record, 2) -> true; old_type_test(function, 1) -> true; old_type_test(Name, A) when is_atom(Name), is_integer(A) -> false. --spec arith_op(Op::atom(), Arity::arity()) -> boolean(). +-spec arith_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). arith_op('+', 1) -> true; arith_op('-', 1) -> true; @@ -153,7 +159,9 @@ arith_op('bsl', 2) -> true; arith_op('bsr', 2) -> true; arith_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec bool_op(Op::atom(), Arity::arity()) -> boolean(). +-spec bool_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). bool_op('not', 1) -> true; bool_op('and', 2) -> true; @@ -161,7 +169,9 @@ bool_op('or', 2) -> true; bool_op('xor', 2) -> true; bool_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec comp_op(Op::atom(), Arity::arity()) -> boolean(). +-spec comp_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). comp_op('==', 2) -> true; comp_op('/=', 2) -> true; @@ -173,18 +183,25 @@ comp_op('=:=', 2) -> true; comp_op('=/=', 2) -> true; comp_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec list_op(Op::atom(), Arity::arity()) -> boolean(). +-spec list_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). list_op('++', 2) -> true; list_op('--', 2) -> true; list_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec send_op(Op::atom(), Arity::arity()) -> boolean(). +-spec send_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). send_op('!', 2) -> true; send_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec op_type(atom(), arity()) -> 'arith' | 'bool' | 'comp' | 'list' | 'send'. +-spec op_type(OpName, Arity) -> Type when + OpName :: atom(), + Arity :: arity(), + Type :: 'arith' | 'bool' | 'comp' | 'list' | 'send'. op_type('+', 1) -> arith; op_type('-', 1) -> arith; @@ -221,7 +238,9 @@ op_type('!', 2) -> send. bif(erlang, Name, Arity) -> bif(Name, Arity); bif(M, F, A) when is_atom(M), is_atom(F), is_integer(A) -> false. --spec bif(Name::atom(), Arity::arity()) -> boolean(). +-spec bif(Name, Arity) -> boolean() when + Name :: atom(), + Arity::arity(). %% Returns true if erlang:Name/Arity is an auto-imported BIF, false otherwise. %% Use erlang:is_bultin(Mod, Name, Arity) to find whether a function is a BIF %% (meaning implemented in C) or not. @@ -243,8 +262,8 @@ bif(bitsize, 1) -> true; bif(bit_size, 1) -> true; bif(bitstring_to_list, 1) -> true; bif(byte_size, 1) -> true; +bif(check_old_code, 1) -> true; bif(check_process_code, 2) -> true; -bif(concat_binary, 1) -> true; bif(date, 0) -> true; bif(delete_module, 1) -> true; bif(demonitor, 1) -> true; @@ -386,7 +405,6 @@ old_bif(bit_size, 1) -> true; old_bif(bitstring_to_list, 1) -> true; old_bif(byte_size, 1) -> true; old_bif(check_process_code, 2) -> true; -old_bif(concat_binary, 1) -> true; old_bif(date, 0) -> true; old_bif(delete_module, 1) -> true; old_bif(disconnect_node, 1) -> true; diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index cfb9f0ca98..78b996d94b 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -123,15 +123,21 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> called= [] :: [{fa(),line()}], %Called functions usage = #usage{} :: #usage{}, specs = dict:new() :: dict(), %Type specifications + callbacks = dict:new() :: dict(), %Callback types types = dict:new() :: dict(), %Type definitions exp_types=gb_sets:empty():: gb_set() %Exported types }). -type lint_state() :: #lint{}. +-type error_description() :: term(). +-type error_info() :: {erl_scan:line(), module(), error_description()}. %% format_error(Error) %% Return a string describing the error. +-spec format_error(ErrorDescriptor) -> io_lib:chars() when + ErrorDescriptor :: error_description(). + format_error(undefined_module) -> "no module definition"; format_error({bad_module_name, M}) -> @@ -305,8 +311,6 @@ format_error({conflicting_behaviours,{Name,Arity},B,FirstL,FirstB}) -> format_error({undefined_behaviour_func, {Func,Arity}, Behaviour}) -> io_lib:format("undefined callback function ~w/~w (behaviour '~w')", [Func,Arity,Behaviour]); -format_error({undefined_behaviour_func, {Func,Arity,_Spec}, Behaviour}) -> - format_error({undefined_behaviour_func, {Func,Arity}, Behaviour}); format_error({undefined_behaviour,Behaviour}) -> io_lib:format("behaviour ~w undefined", [Behaviour]); format_error({undefined_behaviour_callbacks,Behaviour}) -> @@ -315,6 +319,9 @@ format_error({undefined_behaviour_callbacks,Behaviour}) -> format_error({ill_defined_behaviour_callbacks,Behaviour}) -> io_lib:format("behaviour ~w callback functions erroneously defined", [Behaviour]); +format_error({behaviour_info, {_M,F,A}}) -> + io_lib:format("cannot define callback attibute for ~w/~w when " + "behaviour_info is defined",[F,A]); %% --- types and specs --- format_error({singleton_typevar, Name}) -> io_lib:format("type variable ~w is only used once (is unbound)", [Name]); @@ -343,12 +350,16 @@ format_error({type_syntax, Constr}) -> io_lib:format("bad ~w type", [Constr]); format_error({redefine_spec, {M, F, A}}) -> io_lib:format("spec for ~w:~w/~w already defined", [M, F, A]); +format_error({redefine_callback, {M, F, A}}) -> + io_lib:format("callback ~w:~w/~w already defined", [M, F, A]); format_error({spec_fun_undefined, {M, F, A}}) -> io_lib:format("spec for undefined function ~w:~w/~w", [M, F, A]); format_error({missing_spec, {F,A}}) -> io_lib:format("missing specification for function ~w/~w", [F, A]); format_error(spec_wrong_arity) -> "spec has the wrong arity"; +format_error(callback_wrong_arity) -> + "callback has the wrong arity"; format_error({imported_predefined_type, Name}) -> io_lib:format("referring to built-in type ~w as a remote type; " "please take out the module name", [Name]); @@ -419,16 +430,39 @@ used_vars(Exprs, BindingsList) -> %% apply_lambda/2 has been called to shut lint up. N.B. these lists are %% really all ordsets! +-spec(module(AbsForms) -> {ok, Warnings} | {error, Errors, Warnings} when + AbsForms :: [erl_parse:abstract_form()], + Warnings :: [{file:filename(),[ErrorInfo]}], + Errors :: [{FileName2 :: file:filename(),[ErrorInfo]}], + ErrorInfo :: error_info()). + module(Forms) -> Opts = compiler_options(Forms), St = forms(Forms, start("nofile", Opts)), return_status(St). +-spec(module(AbsForms, FileName) -> + {ok, Warnings} | {error, Errors, Warnings} when + AbsForms :: [erl_parse:abstract_form()], + FileName :: atom() | string(), + Warnings :: [{file:filename(),[ErrorInfo]}], + Errors :: [{FileName2 :: file:filename(),[ErrorInfo]}], + ErrorInfo :: error_info()). + module(Forms, FileName) -> Opts = compiler_options(Forms), St = forms(Forms, start(FileName, Opts)), return_status(St). +-spec(module(AbsForms, FileName, CompileOptions) -> + {ok, Warnings} | {error, Errors, Warnings} when + AbsForms :: [erl_parse:abstract_form()], + FileName :: atom() | string(), + CompileOptions :: [compile:option()], + Warnings :: [{file:filename(),[ErrorInfo]}], + Errors :: [{FileName2 :: file:filename(),[ErrorInfo]}], + ErrorInfo :: error_info()). + module(Forms, FileName, Opts0) -> %% We want the options given on the command line to take %% precedence over options in the module. @@ -719,6 +753,8 @@ attribute_state({attribute,L,opaque,{TypeName,TypeDef,Args}}, St) -> type_def(opaque, L, TypeName, TypeDef, Args, St); attribute_state({attribute,L,spec,{Fun,Types}}, St) -> spec_decl(L, Fun, Types, St); +attribute_state({attribute,L,callback,{Fun,Types}}, St) -> + callback_decl(L, Fun, Types, St); attribute_state({attribute,L,on_load,Val}, St) -> on_load(L, Val, St); attribute_state({attribute,_L,_Other,_Val}, St) -> % Ignore others @@ -812,7 +848,8 @@ post_traversal_check(Forms, St0) -> StB = check_unused_types(Forms, StA), StC = check_untyped_records(Forms, StB), StD = check_on_load(StC), - check_unused_records(Forms, StD). + StE = check_unused_records(Forms, StD), + check_callback_information(StE). %% check_behaviour(State0) -> State %% Check that the behaviour attribute is valid. @@ -1111,6 +1148,23 @@ check_unused_records(Forms, St0) -> St0 end. +check_callback_information(#lint{callbacks = Callbacks, + defined = Defined} = State) -> + case gb_sets:is_member({behaviour_info,1}, Defined) of + false -> State; + true -> + case dict:size(Callbacks) of + 0 -> State; + _ -> + CallbacksList = dict:to_list(Callbacks), + FoldL = + fun({Fa,Line},St) -> + add_error(Line, {behaviour_info, Fa}, St) + end, + lists:foldl(FoldL, State, CallbacksList) + end + end. + %% For storing the import list we use the orddict module. %% We know an empty set is []. @@ -1877,6 +1931,9 @@ gexpr_list(Es, Vt, St) -> %% is_guard_test(Expression) -> boolean(). %% Test if a general expression is a guard test. +-spec is_guard_test(Expr) -> boolean() when + Expr :: erl_parse:abstract_expr(). + is_guard_test(E) -> is_guard_test2(E, dict:new()). @@ -2739,6 +2796,20 @@ spec_decl(Line, MFA0, TypeSpecs, St0 = #lint{specs = Specs, module = Mod}) -> false -> check_specs(TypeSpecs, Arity, St1) end. +%% callback_decl(Line, Fun, Types, State) -> State. + +callback_decl(Line, MFA0, TypeSpecs, + St0 = #lint{callbacks = Callbacks, module = Mod}) -> + MFA = case MFA0 of + {F, Arity} -> {Mod, F, Arity}; + {_M, _F, Arity} -> MFA0 + end, + St1 = St0#lint{callbacks = dict:store(MFA, Line, Callbacks)}, + case dict:is_key(MFA, Callbacks) of + true -> add_error(Line, {redefine_callback, MFA}, St1); + false -> check_specs(TypeSpecs, Arity, St1) + end. + check_specs([FunType|Left], Arity, St0) -> {FunType1, CTypes} = case FunType of @@ -3244,6 +3315,8 @@ modify_line1({attribute,L,record,{Name,Fields}}, Mf) -> {attribute,Mf(L),record,{Name,modify_line1(Fields, Mf)}}; modify_line1({attribute,L,spec,{Fun,Types}}, Mf) -> {attribute,Mf(L),spec,{Fun,modify_line1(Types, Mf)}}; +modify_line1({attribute,L,callback,{Fun,Types}}, Mf) -> + {attribute,Mf(L),callback,{Fun,modify_line1(Types, Mf)}}; modify_line1({attribute,L,type,{TypeName,TypeDef,Args}}, Mf) -> {attribute,Mf(L),type,{TypeName,modify_line1(TypeDef, Mf), modify_line1(Args, Mf)}}; diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index 15b45d72f4..709bd83e6f 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -62,7 +62,7 @@ char integer float atom string var '==' '/=' '=<' '<' '>=' '>' '=:=' '=/=' '<=' '<<' '>>' '!' '=' '::' '..' '...' -'spec' % helper +'spec' 'callback' % helper dot. Expect 2. @@ -77,6 +77,7 @@ attribute -> '-' atom attr_val : build_attribute('$2', '$3'). attribute -> '-' atom typed_attr_val : build_typed_attribute('$2','$3'). attribute -> '-' atom '(' typed_attr_val ')' : build_typed_attribute('$2','$4'). attribute -> '-' 'spec' type_spec : build_type_spec('$2', '$3'). +attribute -> '-' 'callback' type_spec : build_type_spec('$2', '$3'). type_spec -> spec_fun type_sigs : {'$1', '$2'}. type_spec -> '(' spec_fun type_sigs ')' : {'$2', '$3'}. @@ -511,6 +512,15 @@ Erlang code. %% of the generated .erl file by the HiPE compiler. Please do not remove. -compile([{hipe,[{regalloc,linear_scan}]}]). +-export_type([abstract_clause/0, abstract_expr/0, abstract_form/0, + error_info/0]). + +-type abstract_clause() :: term(). +-type abstract_expr() :: term(). +-type abstract_form() :: term(). +-type error_description() :: term(). +-type error_info() :: {erl_scan:line(), module(), error_description()}. +-type token() :: {Tag :: atom(), Line :: erl_scan:line()}. %% mkop(Op, Arg) -> {op,Line,Op,Arg}. %% mkop(Left, Op, Right) -> {op,Line,Op,Left,Right}. @@ -534,11 +544,21 @@ Erlang code. %% These really suck and are only here until Calle gets multiple %% entry points working. +-spec parse_form(Tokens) -> {ok, AbsForm} | {error, ErrorInfo} when + Tokens :: [token()], + AbsForm :: abstract_form(), + ErrorInfo :: error_info(). parse_form([{'-',L1},{atom,L2,spec}|Tokens]) -> parse([{'-',L1},{'spec',L2}|Tokens]); +parse_form([{'-',L1},{atom,L2,callback}|Tokens]) -> + parse([{'-',L1},{'callback',L2}|Tokens]); parse_form(Tokens) -> parse(Tokens). +-spec parse_exprs(Tokens) -> {ok, ExprList} | {error, ErrorInfo} when + Tokens :: [token()], + ExprList :: [abstract_expr()], + ErrorInfo :: error_info(). parse_exprs(Tokens) -> case parse([{atom,0,f},{'(',0},{')',0},{'->',0}|Tokens]) of {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],Exprs}]}} -> @@ -546,6 +566,10 @@ parse_exprs(Tokens) -> {error,_} = Err -> Err end. +-spec parse_term(Tokens) -> {ok, Term} | {error, ErrorInfo} when + Tokens :: [token()], + Term :: term(), + ErrorInfo :: error_info(). parse_term(Tokens) -> case parse([{atom,0,f},{'(',0},{')',0},{'->',0}|Tokens]) of {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],[Expr]}]}} -> @@ -582,7 +606,8 @@ build_typed_attribute({atom,La,Attr},_) -> _ -> ret_err(La, "bad attribute") end. -build_type_spec({spec,La}, {SpecFun, TypeSpecs}) -> +build_type_spec({Kind,La}, {SpecFun, TypeSpecs}) + when (Kind =:= spec) or (Kind =:= callback) -> NewSpecFun = case SpecFun of {atom, _, Fun} -> @@ -596,7 +621,7 @@ build_type_spec({spec,La}, {SpecFun, TypeSpecs}) -> %% Old style spec. Allow this for now. {Mod,Fun,Arity} end, - {attribute,La,spec,{NewSpecFun, TypeSpecs}}. + {attribute,La,Kind,{NewSpecFun, TypeSpecs}}. find_arity_from_specs([Spec|_]) -> %% Use the first spec to find the arity. If all are not the same, @@ -830,6 +855,7 @@ check_clauses(Cs, Name, Arity) -> build_try(L,Es,Scs,{Ccs,As}) -> {'try',L,Es,Scs,Ccs,As}. +-spec ret_err(_, _) -> no_return(). ret_err(L, S) -> {location,Location} = get_attribute(L, location), return_error(Location, S). @@ -846,10 +872,11 @@ mapl(F, [H|T]) -> mapl(_, []) -> []. -%% normalise(AbsTerm) -%% abstract(Term) %% Convert between the abstract form of a term and a term. +-spec normalise(AbsTerm) -> Data when + AbsTerm :: abstract_expr(), + Data :: term(). normalise({char,_,C}) -> C; normalise({integer,_,I}) -> I; normalise({float,_,F}) -> F; @@ -887,6 +914,9 @@ normalise_list([H|T]) -> normalise_list([]) -> []. +-spec abstract(Data) -> AbsTerm when + Data :: term(), + AbsTerm :: abstract_expr(). abstract(T) when is_integer(T) -> {integer,0,T}; abstract(T) when is_float(T) -> {float,0,T}; abstract(T) when is_atom(T) -> {atom,0,T}; @@ -955,13 +985,18 @@ abstract_list([H|T], Line) -> abstract_list([], _Line) -> []. -%% tokens(AbsTerm) -> [Token] -%% tokens(AbsTerm, More) -> [Token] %% Generate a list of tokens representing the abstract term. +-spec tokens(AbsTerm) -> Tokens when + AbsTerm :: abstract_expr(), + Tokens :: [token()]. tokens(Abs) -> tokens(Abs, []). +-spec tokens(AbsTerm, MoreTokens) -> Tokens when + AbsTerm :: abstract_expr(), + MoreTokens :: [token()], + Tokens :: [token()]. tokens({char,L,C}, More) -> [{char,L,C}|More]; tokens({integer,L,N}, More) -> [{integer,L,N}|More]; tokens({float,L,F}, More) -> [{float,L,F}|More]; diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index 66c80a45cb..7dc19f2e9b 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -31,25 +31,53 @@ -define(MAXLINE, 72). +-type(hook_function() :: none + | fun((Expr :: erl_parse:abstract_expr(), + CurrentIndentation :: integer(), + CurrentPrecedence :: non_neg_integer(), + HookFunction :: hook_function()) -> + io_lib:chars())). + %%% %%% Exported functions %%% +-spec(form(Form) -> io_lib:chars() when + Form :: erl_parse:abstract_form()). + form(Thing) -> form(Thing, none). +-spec(form(Form, HookFunction) -> io_lib:chars() when + Form :: erl_parse:abstract_form(), + HookFunction :: hook_function()). + form(Thing, Hook) -> frmt(lform(Thing, Hook)). +-spec(attribute(Attribute) -> io_lib:chars() when + Attribute :: erl_parse:abstract_form()). + attribute(Thing) -> attribute(Thing, none). +-spec(attribute(Attribute, HookFunction) -> io_lib:chars() when + Attribute :: erl_parse:abstract_form(), + HookFunction :: hook_function()). + attribute(Thing, Hook) -> frmt(lattribute(Thing, Hook)). +-spec(function(Function) -> io_lib:chars() when + Function :: erl_parse:abstract_form()). + function(F) -> function(F, none). +-spec(function(Function, HookFunction) -> io_lib:chars() when + Function :: erl_parse:abstract_form(), + HookFunction :: hook_function()). + function(F, Hook) -> frmt(lfunction(F, Hook)). @@ -59,30 +87,67 @@ rule(R) -> rule(R, Hook) -> frmt(lrule(R, Hook)). +-spec(guard(Guard) -> io_lib:chars() when + Guard :: [erl_parse:abstract_expr()]). + guard(Gs) -> guard(Gs, none). +-spec(guard(Guard, HookFunction) -> io_lib:chars() when + Guard :: [erl_parse:abstract_expr()], + HookFunction :: hook_function()). + guard(Gs, Hook) -> frmt(lguard(Gs, Hook)). +-spec(exprs(Expressions) -> io_lib:chars() when + Expressions :: [erl_parse:abstract_expr()]). + exprs(Es) -> exprs(Es, 0, none). +-spec(exprs(Expressions, HookFunction) -> io_lib:chars() when + Expressions :: [erl_parse:abstract_expr()], + HookFunction :: hook_function()). + exprs(Es, Hook) -> exprs(Es, 0, Hook). +-spec(exprs(Expressions, Indent, HookFunction) -> io_lib:chars() when + Expressions :: [erl_parse:abstract_expr()], + Indent :: integer(), + HookFunction :: hook_function()). + exprs(Es, I, Hook) -> frmt({seq,[],[],[$,],lexprs(Es, Hook)}, I). +-spec(expr(Expression) -> io_lib:chars() when + Expression :: erl_parse:abstract_expr()). + expr(E) -> frmt(lexpr(E, 0, none)). +-spec(expr(Expression, HookFunction) -> io_lib:chars() when + Expression :: erl_parse:abstract_expr(), + HookFunction :: hook_function()). + expr(E, Hook) -> frmt(lexpr(E, 0, Hook)). +-spec(expr(Expression, Indent, HookFunction) -> io_lib:chars() when + Expression :: erl_parse:abstract_expr(), + Indent :: integer(), + HookFunction :: hook_function()). + expr(E, I, Hook) -> frmt(lexpr(E, 0, Hook), I). +-spec(expr(Expression, Indent, Precedence, HookFunction) -> io_lib:chars() when + Expression :: erl_parse:abstract_expr(), + Indent :: integer(), + Precedence :: non_neg_integer(), + HookFunction :: hook_function()). + expr(E, I, P, Hook) -> frmt(lexpr(E, P, Hook), I). diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl index 18f64c46d0..10b2ed2e49 100644 --- a/lib/stdlib/src/erl_scan.erl +++ b/lib/stdlib/src/erl_scan.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -99,7 +99,8 @@ %%---------------------------------------------------------------------------- --spec format_error(Error :: term()) -> string(). +-spec format_error(ErrorDescriptor) -> string() when + ErrorDescriptor :: error_description(). format_error({string,Quote,Head}) -> lists:flatten(["unterminated " ++ string_thing(Quote) ++ " starting with " ++ @@ -112,20 +113,33 @@ format_error({base,Base}) -> format_error(Other) -> lists:flatten(io_lib:write(Other)). --type string_return() :: {'ok', tokens(), location()} - | {'error', error_info(), location()}. - --spec string(String :: string()) -> string_return(). +-spec string(String) -> Return when + String :: string(), + Return :: {'ok', Tokens :: tokens(), EndLocation} + | {'error', ErrorInfo :: error_info(), ErrorLocation}, + EndLocation :: location(), + ErrorLocation :: location(). string(String) -> string(String, 1, []). --spec string(String :: string(), StartLocation :: location()) -> - string_return(). +-spec string(String, StartLocation) -> Return when + String :: string(), + Return :: {'ok', Tokens :: tokens(), EndLocation} + | {'error', ErrorInfo :: error_info(), ErrorLocation}, + StartLocation :: location(), + EndLocation :: location(), + ErrorLocation :: location(). string(String, StartLocation) -> string(String, StartLocation, []). --spec string(String :: string(), StartLocation :: location(), - Options :: options()) -> string_return(). +-spec string(String, StartLocation, Options) -> Return when + String :: string(), + Options :: options(), + Return :: {'ok', Tokens :: tokens(), EndLocation} + | {'error', ErrorInfo :: error_info(), ErrorLocation}, + StartLocation :: location(), + EndLocation :: location(), + ErrorLocation :: location(). string(String, Line, Options) when ?STRING(String), ?ALINE(Line) -> string1(String, options(Options), Line, no_col, []); string(String, {Line,Column}, Options) when ?STRING(String), @@ -136,29 +150,37 @@ string(String, {Line,Column}, Options) when ?STRING(String), -type char_spec() :: string() | 'eof'. -type cont_fun() :: fun((char_spec(), #erl_scan{}, line(), column(), tokens(), any()) -> any()). --opaque return_cont() :: {string(), column(), tokens(), line(), - #erl_scan{}, cont_fun(), any()}. --type cont() :: return_cont() | []. --type tokens_result() :: {'ok', tokens(), location()} - | {'eof', location()} - | {'error', error_info(), location()}. --type tokens_return() :: {'done', tokens_result(), char_spec()} - | {'more', return_cont()}. - --spec tokens(Cont :: cont(), CharSpec :: char_spec(), - StartLocation :: location()) -> tokens_return(). +-opaque return_cont() :: {erl_scan_continuation, + string(), column(), tokens(), line(), + #erl_scan{}, any(), cont_fun()}. +-type tokens_result() :: {'ok', Tokens :: tokens(), EndLocation :: location()} + | {'eof', EndLocation :: location()} + | {'error', ErrorInfo :: error_info(), + EndLocation :: location()}. + +-spec tokens(Continuation, CharSpec, StartLocation) -> Return when + Continuation :: return_cont() | [], + CharSpec :: char_spec(), + StartLocation :: location(), + Return :: {'done',Result :: tokens_result(),LeftOverChars :: char_spec()} + | {'more', Continuation1 :: return_cont()}. tokens(Cont, CharSpec, StartLocation) -> tokens(Cont, CharSpec, StartLocation, []). --spec tokens(Cont :: cont(), CharSpec :: char_spec(), - StartLocation :: location(), Options :: options()) -> - tokens_return(). +-spec tokens(Continuation, CharSpec, StartLocation, Options) -> Return when + Continuation :: return_cont() | [], + CharSpec :: char_spec(), + StartLocation :: location(), + Options :: options(), + Return :: {'done',Result :: tokens_result(),LeftOverChars :: char_spec()} + | {'more', Continuation1 :: return_cont()}. tokens([], CharSpec, Line, Options) when ?ALINE(Line) -> tokens1(CharSpec, options(Options), Line, no_col, [], fun scan/6, []); tokens([], CharSpec, {Line,Column}, Options) when ?ALINE(Line), ?COLUMN(Column) -> tokens1(CharSpec, options(Options), Line, Column, [], fun scan/6, []); -tokens({Cs,Col,Toks,Line,St,Any,Fun}, CharSpec, _Loc, _Opts) -> +tokens({erl_scan_continuation,Cs,Col,Toks,Line,St,Any,Fun}, + CharSpec, _Loc, _Opts) -> tokens1(Cs++CharSpec, St, Line, Col, Toks, Fun, Any). -type attribute_item() :: 'column' | 'length' | 'line' @@ -172,13 +194,22 @@ tokens({Cs,Col,Toks,Line,St,Any,Fun}, CharSpec, _Loc, _Opts) -> -type token_info() :: {'category', category()} | {'symbol', symbol()} | attribute_info(). --spec token_info(token()) -> [token_info()]. +-spec token_info(Token) -> TokenInfo when + Token :: token(), + TokenInfo :: [TokenInfoTuple :: token_info()]. token_info(Token) -> Items = [category,column,length,line,symbol,text], % undefined order token_info(Token, Items). --spec token_info(token(), token_item()) -> token_info() | 'undefined'; - (token(), [token_item()]) -> [token_info()]. +-spec token_info(Token, TokenItem) -> TokenInfo | 'undefined' when + Token :: token(), + TokenItem :: token_item(), + TokenInfo :: TokenInfoTuple :: token_info(); + (Token, TokenItems) -> [TokenInfo] when + Token :: token(), + TokenItems :: [TokenItem], + TokenItem :: token_item(), + TokenInfo :: [TokenInfoTuple :: token_info()]. token_info(_Token, []) -> []; token_info(Token, [Item|Items]) when is_atom(Item) -> @@ -201,14 +232,23 @@ token_info({_Category,Attrs}, Item) -> token_info({_Category,Attrs,_Symbol}, Item) -> attributes_info(Attrs, Item). --spec attributes_info(attributes()) -> [attribute_info()]. +-spec attributes_info(Attributes) -> AttributesInfo when + Attributes :: attributes(), + AttributesInfo :: [AttributeInfoTuple :: attribute_info()]. attributes_info(Attributes) -> Items = [column,length,line,text], % undefined order attributes_info(Attributes, Items). --spec attributes_info(attributes(), attribute_item()) -> - attribute_info() | 'undefined'; - (attributes(), [attribute_item()]) -> [attribute_info()]. +-spec attributes_info(Attributes, AttributeItem) -> + AttributeInfo | 'undefined' when + Attributes :: attributes(), + AttributeItem :: attribute_item(), + AttributeInfo :: AttributeInfoTuple :: attribute_info(); + (Attributes, AttributeItems) -> [AttributeInfo] when + Attributes :: attributes(), + AttributeItems :: [AttributeItem], + AttributeItem :: attribute_item(), + AttributeInfo :: [AttributeInfoTuple :: attribute_info()]. attributes_info(_Attrs, []) -> []; attributes_info(Attrs, [A|As]) when is_atom(A) -> @@ -265,9 +305,10 @@ attributes_info(Attrs, text=Item) -> attributes_info(T1, T2) -> erlang:error(badarg, [T1,T2]). --type setlineattr_fun() :: fun((info_line()) -> info_line()). - --spec set_attribute('line', attributes(), setlineattr_fun()) -> attributes(). +-spec set_attribute(AttributeItem, Attributes, SetAttributeFun) -> Attributes when + AttributeItem :: 'line', + Attributes :: attributes(), + SetAttributeFun :: fun((info_line()) -> info_line()). set_attribute(Tag, Attributes, Fun) when ?SETATTRFUN(Fun) -> set_attr(Tag, Attributes, Fun). @@ -367,14 +408,19 @@ set_attr(line, {Line,Column}, Fun) when ?ALINE(Line), ?COLUMN(Column) -> end; set_attr(line=Tag, Attrs, Fun) when is_list(Attrs) -> {line,Line} = lists:keyfind(Tag, 1, Attrs), - lists:keyreplace(Tag, 1, Attrs, {line,Fun(Line)}); + case lists:keyreplace(Tag, 1, Attrs, {line,Fun(Line)}) of + [{line,Ln}] when ?ALINE(Ln) -> + Ln; + As -> + As + end; set_attr(T1, T2, T3) -> erlang:error(badarg, [T1,T2,T3]). tokens1(Cs, St, Line, Col, Toks, Fun, Any) when ?STRING(Cs); Cs =:= eof -> case Fun(Cs, St, Line, Col, Toks, Any) of {more,{Cs0,Ncol,Ntoks,Nline,Nany,Nfun}} -> - {more,{Cs0,Ncol,Ntoks,Nline,St,Nany,Nfun}}; + {more,{erl_scan_continuation,Cs0,Ncol,Ntoks,Nline,St,Nany,Nfun}}; {ok,Toks0,eof,Nline,Ncol} -> Res = case Toks0 of [] -> @@ -1285,7 +1331,7 @@ tabs(8) -> "\t\t\t\t\t\t\t\t"; tabs(9) -> "\t\t\t\t\t\t\t\t\t"; tabs(10) -> "\t\t\t\t\t\t\t\t\t\t". --spec reserved_word(atom()) -> boolean(). +-spec reserved_word(Atom :: atom()) -> boolean(). reserved_word('after') -> true; reserved_word('begin') -> true; reserved_word('case') -> true; diff --git a/lib/stdlib/src/erl_tar.erl b/lib/stdlib/src/erl_tar.erl index fd85c7aef5..306834e845 100644 --- a/lib/stdlib/src/erl_tar.erl +++ b/lib/stdlib/src/erl_tar.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -798,30 +798,10 @@ set_extracted_file_info(Name, #tar_header{mode=Mode, mtime=Mtime}) -> %% Makes all directories leading up to the file. -make_dirs(Name, Type) -> - make_dirs1(filename:split(Name), Type). - -make_dirs1([Dir, Next|Rest], Type) -> - case file:read_file_info(Dir) of - {ok, #file_info{type=directory}} -> - make_dirs1([filename:join(Dir, Next)|Rest], Type); - {ok, #file_info{}} -> - throw({error, enotdir}); - {error, _} -> - case file:make_dir(Dir) of - ok -> - make_dirs1([filename:join(Dir, Next)|Rest], Type); - {error, Reason} -> - throw({error, Reason}) - end - end; -make_dirs1([_], file) -> ok; -make_dirs1([Dir], dir) -> - file:make_dir(Dir); -make_dirs1([], _) -> - %% There must be something wrong here. The list was not supposed - %% to be empty. - throw({error, enoent}). +make_dirs(Name, file) -> + filelib:ensure_dir(Name); +make_dirs(Name, dir) -> + filelib:ensure_dir(filename:join(Name,"*")). %% Prints the message on if the verbose option is given (for reading). diff --git a/lib/stdlib/src/error_logger_tty_h.erl b/lib/stdlib/src/error_logger_tty_h.erl index 435e57aa0e..fa13fbb2bd 100644 --- a/lib/stdlib/src/error_logger_tty_h.erl +++ b/lib/stdlib/src/error_logger_tty_h.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -34,10 +34,12 @@ handle_event/2, handle_call/2, handle_info/2, terminate/2, code_change/3]). +-export([write_event/2]). + %% This one is used when we takeover from the simple error_logger. init({[], {error_logger, Buf}}) -> User = set_group_leader(), - write_events(Buf), + write_events(Buf,io), {ok, {User, error_logger}}; %% This one is used if someone took over from us, and now wants to %% go back. @@ -52,7 +54,7 @@ init([]) -> handle_event({_Type, GL, _Msg}, State) when node(GL) =/= node() -> {ok, State}; handle_event(Event, State) -> - write_event(tag_event(Event)), + write_event(tag_event(Event),io), {ok, State}. handle_info({'EXIT', User, _Reason}, {User, PrevHandler}) -> @@ -64,10 +66,10 @@ handle_info({'EXIT', User, _Reason}, {User, PrevHandler}) -> PrevHandler, go_back} end; handle_info({emulator, GL, Chars}, State) when node(GL) == node() -> - write_event(tag_event({emulator, GL, Chars})), + write_event(tag_event({emulator, GL, Chars}),io), {ok, State}; handle_info({emulator, noproc, Chars}, State) -> - write_event(tag_event({emulator, noproc, Chars})), + write_event(tag_event({emulator, noproc, Chars}),io), {ok, State}; handle_info(_, State) -> {ok, State}. @@ -97,65 +99,65 @@ set_group_leader() -> tag_event(Event) -> {erlang:localtime(), Event}. -write_events(Events) -> write_events1(lists:reverse(Events)). +write_events(Events,IOMod) -> write_events1(lists:reverse(Events),IOMod). -write_events1([Event|Es]) -> - write_event(Event), - write_events1(Es); -write_events1([]) -> +write_events1([Event|Es],IOMod) -> + write_event(Event,IOMod), + write_events1(Es,IOMod); +write_events1([],_IOMod) -> ok. -write_event({Time, {error, _GL, {Pid, Format, Args}}}) -> +write_event({Time, {error, _GL, {Pid, Format, Args}}},IOMod) -> T = write_time(maybe_utc(Time)), case catch io_lib:format(add_node(Format,Pid), Args) of S when is_list(S) -> - format(T ++ S); + format(IOMod, T ++ S); _ -> F = add_node("ERROR: ~p - ~p~n", Pid), - format(T ++ F, [Format,Args]) + format(IOMod, T ++ F, [Format,Args]) end; -write_event({Time, {emulator, _GL, Chars}}) -> +write_event({Time, {emulator, _GL, Chars}},IOMod) -> T = write_time(maybe_utc(Time)), case catch io_lib:format(Chars, []) of S when is_list(S) -> - format(T ++ S); + format(IOMod, T ++ S); _ -> - format(T ++ "ERROR: ~p ~n", [Chars]) + format(IOMod, T ++ "ERROR: ~p ~n", [Chars]) end; -write_event({Time, {info, _GL, {Pid, Info, _}}}) -> +write_event({Time, {info, _GL, {Pid, Info, _}}},IOMod) -> T = write_time(maybe_utc(Time)), - format(T ++ add_node("~p~n",Pid),[Info]); -write_event({Time, {error_report, _GL, {Pid, std_error, Rep}}}) -> + format(IOMod, T ++ add_node("~p~n",Pid),[Info]); +write_event({Time, {error_report, _GL, {Pid, std_error, Rep}}},IOMod) -> T = write_time(maybe_utc(Time)), S = format_report(Rep), - format(T ++ S ++ add_node("", Pid)); -write_event({Time, {info_report, _GL, {Pid, std_info, Rep}}}) -> + format(IOMod, T ++ S ++ add_node("", Pid)); +write_event({Time, {info_report, _GL, {Pid, std_info, Rep}}},IOMod) -> T = write_time(maybe_utc(Time), "INFO REPORT"), S = format_report(Rep), - format(T ++ S ++ add_node("", Pid)); -write_event({Time, {info_msg, _GL, {Pid, Format, Args}}}) -> + format(IOMod, T ++ S ++ add_node("", Pid)); +write_event({Time, {info_msg, _GL, {Pid, Format, Args}}},IOMod) -> T = write_time(maybe_utc(Time), "INFO REPORT"), case catch io_lib:format(add_node(Format,Pid), Args) of S when is_list(S) -> - format(T ++ S); + format(IOMod, T ++ S); _ -> F = add_node("ERROR: ~p - ~p~n", Pid), - format(T ++ F, [Format,Args]) + format(IOMod, T ++ F, [Format,Args]) end; -write_event({Time, {warning_report, _GL, {Pid, std_warning, Rep}}}) -> +write_event({Time, {warning_report, _GL, {Pid, std_warning, Rep}}},IOMod) -> T = write_time(maybe_utc(Time), "WARNING REPORT"), S = format_report(Rep), - format(T ++ S ++ add_node("", Pid)); -write_event({Time, {warning_msg, _GL, {Pid, Format, Args}}}) -> + format(IOMod, T ++ S ++ add_node("", Pid)); +write_event({Time, {warning_msg, _GL, {Pid, Format, Args}}},IOMod) -> T = write_time(maybe_utc(Time), "WARNING REPORT"), case catch io_lib:format(add_node(Format,Pid), Args) of S when is_list(S) -> - format(T ++ S); + format(IOMod, T ++ S); _ -> F = add_node("ERROR: ~p - ~p~n", Pid), - format(T ++ F, [Format,Args]) + format(IOMod, T ++ F, [Format,Args]) end; -write_event({_Time, _Error}) -> +write_event({_Time, _Error},_IOMod) -> ok. maybe_utc(Time) -> @@ -178,8 +180,9 @@ maybe_utc(Time) -> Time end. -format(String) -> io:format(user, String, []). -format(String, Args) -> io:format(user, String, Args). +format(IOMod, String) -> format(IOMod, String, []). +format(io_lib, String, Args) -> io_lib:format(String, Args); +format(io, String, Args) -> io:format(user, String, Args). format_report(Rep) when is_list(Rep) -> case string_p(Rep) of diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl index d67617260e..ad49d89908 100644 --- a/lib/stdlib/src/escript.erl +++ b/lib/stdlib/src/escript.erl @@ -62,10 +62,10 @@ -type zip_create_option() :: term(). -type section() :: shebang - | {shebang, shebang()} + | {shebang, shebang() | default | undefined} | comment - | {comment, comment()} - | {emu_args, emu_args()} + | {comment, comment() | default | undefined} + | {emu_args, emu_args() | undefined} | {source, file:filename() | binary()} | {beam, file:filename() | binary()} | {archive, file:filename() | binary()} @@ -866,7 +866,7 @@ hidden_apply(App, M, F, Args) -> catch error:undef -> case erlang:get_stacktrace() of - [{M,F,Args} | _] -> + [{M,F,Args,_} | _] -> Arity = length(Args), Text = io_lib:format("Call to ~w:~w/~w in application ~w failed.\n", [M, F, Arity, App]), diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl index 6e6e949e2c..afa914a456 100644 --- a/lib/stdlib/src/ets.erl +++ b/lib/stdlib/src/ets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -42,7 +42,7 @@ -export([i/0, i/1, i/2, i/3]). --export_type([tab/0, tid/0]). +-export_type([tab/0, tid/0, match_spec/0]). %%----------------------------------------------------------------------------- @@ -51,22 +51,9 @@ %% a similar definition is also in erl_types -opaque tid() :: integer(). --type ext_info() :: 'md5sum' | 'object_count'. --type protection() :: 'private' | 'protected' | 'public'. --type type() :: 'bag' | 'duplicate_bag' | 'ordered_set' | 'set'. - --type table_info() :: {'name', atom()} - | {'type', type()} - | {'protection', protection()} - | {'named_table', boolean()} - | {'keypos', non_neg_integer()} - | {'size', non_neg_integer()} - | {'extended_info', [ext_info()]} - | {'version', {non_neg_integer(), non_neg_integer()}}. - %% these ones are also defined in erl_bif_types -type match_pattern() :: atom() | tuple(). --type match_specs() :: [{match_pattern(), [_], [_]}]. +-type match_spec() :: [{match_pattern(), [_], [_]}]. %%----------------------------------------------------------------------------- @@ -86,6 +73,7 @@ %% insert/2 %% is_compiled_ms/1 %% last/1 +%% member/2 %% next/2 %% prev/2 %% rename/2 @@ -101,11 +89,14 @@ %% select/1 %% select/2 %% select/3 +%% select_count/2 %% select_reverse/1 %% select_reverse/2 %% select_reverse/3 %% select_delete/2 +%% setopts/2 %% update_counter/3 +%% update_element/3 %% -opaque comp_match_spec() :: any(). %% this one is REALLY opaque @@ -119,7 +110,9 @@ match_spec_run(List, CompiledMS) -> | {tab(),integer(),integer(),binary(),list(),integer()} | {tab(),_,_,integer(),binary(),list(),integer(),integer()}. --spec repair_continuation(continuation(), match_specs()) -> continuation(). +-spec repair_continuation(Continuation, MatchSpec) -> Continuation when + Continuation :: continuation(), + MatchSpec :: match_spec(). %% $end_of_table is an allowed continuation in ets... repair_continuation('$end_of_table', _) -> @@ -153,7 +146,9 @@ repair_continuation(Untouched = {Table,N1,N2,Bin,L,N3}, MS) {Table,N1,N2,ets:match_spec_compile(MS),L,N3} end. --spec fun2ms(function()) -> match_specs(). +-spec fun2ms(LiteralFun) -> MatchSpec when + LiteralFun :: function(), + MatchSpec :: match_spec(). fun2ms(ShellFun) when is_function(ShellFun) -> %% Check that this is really a shell fun... @@ -177,7 +172,13 @@ fun2ms(ShellFun) when is_function(ShellFun) -> shell]}}) end. --spec foldl(fun((_, term()) -> term()), term(), tab()) -> term(). +-spec foldl(Function, Acc0, Tab) -> Acc1 when + Function :: fun((Element :: term(), AccIn) -> AccOut), + Tab :: tab(), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(). foldl(F, Accu, T) -> ets:safe_fixtable(T, true), @@ -198,7 +199,13 @@ do_foldl(F, Accu0, Key, T) -> ets:next(T, Key), T) end. --spec foldr(fun((_, term()) -> term()), term(), tab()) -> term(). +-spec foldr(Function, Acc0, Tab) -> Acc1 when + Function :: fun((Element :: term(), AccIn) -> AccOut), + Tab :: tab(), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(). foldr(F, Accu, T) -> ets:safe_fixtable(T, true), @@ -219,7 +226,9 @@ do_foldr(F, Accu0, Key, T) -> ets:prev(T, Key), T) end. --spec from_dets(tab(), dets:tab_name()) -> 'true'. +-spec from_dets(Tab, DetsTab) -> 'true' when + Tab :: tab(), + DetsTab :: dets:tab_name(). from_dets(EtsTable, DetsTable) -> case (catch dets:to_ets(DetsTable, EtsTable)) of @@ -235,7 +244,9 @@ from_dets(EtsTable, DetsTable) -> erlang:error(Unexpected,[EtsTable,DetsTable]) end. --spec to_dets(tab(), dets:tab_name()) -> dets:tab_name(). +-spec to_dets(Tab, DetsTab) -> DetsTab when + Tab :: tab(), + DetsTab :: dets:tab_name(). to_dets(EtsTable, DetsTable) -> case (catch dets:from_ets(DetsTable, EtsTable)) of @@ -251,8 +262,11 @@ to_dets(EtsTable, DetsTable) -> erlang:error(Unexpected,[EtsTable,DetsTable]) end. --spec test_ms(tuple(), match_specs()) -> - {'ok', term()} | {'error', [{'warning'|'error', string()}]}. +-spec test_ms(Tuple, MatchSpec) -> {'ok', Result} | {'error', Errors} when + Tuple :: tuple(), + MatchSpec :: match_spec(), + Result :: term(), + Errors :: [{'warning'|'error', string()}]. test_ms(Term, MS) -> case erlang:match_spec_test(Term, MS, table) of @@ -262,7 +276,11 @@ test_ms(Term, MS) -> Error end. --spec init_table(tab(), fun(('read' | 'close') -> term())) -> 'true'. +-spec init_table(Tab, InitFun) -> 'true' when + Tab :: tab(), + InitFun :: fun((Arg) -> Res), + Arg :: 'read' | 'close', + Res :: 'end_of_input' | {Objects :: [term()], InitFun} | term(). init_table(Table, Fun) -> ets:delete_all_objects(Table), @@ -287,7 +305,9 @@ init_table_sub(Table, [H|T]) -> ets:insert(Table, H), init_table_sub(Table, T). --spec match_delete(tab(), match_pattern()) -> 'true'. +-spec match_delete(Tab, Pattern) -> 'true' when + Tab :: tab(), + Pattern :: match_pattern(). match_delete(Table, Pattern) -> ets:select_delete(Table, [{Pattern,[],[true]}]), @@ -295,7 +315,9 @@ match_delete(Table, Pattern) -> %% Produce a list of tuples from a table --spec tab2list(tab()) -> [tuple()]. +-spec tab2list(Tab) -> [Object] when + Tab :: tab(), + Object :: tuple(). tab2list(T) -> ets:match_object(T, '_'). @@ -334,15 +356,21 @@ do_filter(Tab, Key, F, A, Ack) -> md5sum = false :: boolean() }). --type fname() :: string() | atom(). --type t2f_option() :: {'extended_info', [ext_info()]}. - --spec tab2file(tab(), fname()) -> 'ok' | {'error', term()}. +-spec tab2file(Tab, Filename) -> 'ok' | {'error', Reason} when + Tab :: tab(), + Filename :: file:name(), + Reason :: term(). tab2file(Tab, File) -> tab2file(Tab, File, []). --spec tab2file(tab(), fname(), [t2f_option()]) -> 'ok' | {'error', term()}. +-spec tab2file(Tab, Filename, Options) -> 'ok' | {'error', Reason} when + Tab :: tab(), + Filename :: file:name(), + Options :: [Option], + Option :: {'extended_info', [ExtInfo]}, + ExtInfo :: 'md5sum' | 'object_count', + Reason :: term(). tab2file(Tab, File, Options) -> try @@ -501,14 +529,20 @@ parse_ft_info_options(_,Malformed) -> %% Opt := {verify,boolean()} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --type f2t_option() :: {'verify', boolean()}. - --spec file2tab(fname()) -> {'ok', tab()} | {'error', term()}. +-spec file2tab(Filename) -> {'ok', Tab} | {'error', Reason} when + Filename :: file:name(), + Tab :: tab(), + Reason :: term(). file2tab(File) -> file2tab(File, []). --spec file2tab(fname(), [f2t_option()]) -> {'ok', tab()} | {'error', term()}. +-spec file2tab(Filename, Options) -> {'ok', Tab} | {'error', Reason} when + Filename :: file:name(), + Tab :: tab(), + Options :: [Option], + Option :: {'verify', boolean()}, + Reason :: term(). file2tab(File, Opts) -> try @@ -895,7 +929,22 @@ named_table(false) -> []. %% information %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec tabfile_info(fname()) -> {'ok', [table_info()]} | {'error', term()}. +-spec tabfile_info(Filename) -> {'ok', TableInfo} | {'error', Reason} when + Filename :: file:name(), + TableInfo :: [InfoItem], + InfoItem :: {'name', atom()} + | {'type', Type} + | {'protection', Protection} + | {'named_table', boolean()} + | {'keypos', non_neg_integer()} + | {'size', non_neg_integer()} + | {'extended_info', [ExtInfo]} + | {'version', {Major :: non_neg_integer(), + Minor :: non_neg_integer()}}, + ExtInfo :: 'md5sum' | 'object_count', + Type :: 'bag' | 'duplicate_bag' | 'ordered_set' | 'set', + Protection :: 'private' | 'protected' | 'public', + Reason :: term(). tabfile_info(File) when is_list(File) ; is_atom(File) -> try @@ -932,20 +981,22 @@ tabfile_info(File) when is_list(File) ; is_atom(File) -> {error,ExReason} end. --type qlc__query_handle() :: term(). %% XXX: belongs in 'qlc' - --type num_objects() :: 'default' | pos_integer(). --type trav_method() :: 'first_next' | 'last_prev' - | 'select' | {'select', match_specs()}. --type table_option() :: {'n_objects', num_objects()} - | {'traverse', trav_method()}. - --spec table(tab()) -> qlc__query_handle(). +-spec table(Tab) -> QueryHandle when + Tab :: tab(), + QueryHandle :: qlc:query_handle(). table(Tab) -> table(Tab, []). --spec table(tab(), table_option() | [table_option()]) -> qlc__query_handle(). +-spec table(Tab, Options) -> QueryHandle when + Tab :: tab(), + QueryHandle :: qlc:query_handle(), + Options :: [Option] | Option, + Option :: {'n_objects', NObjects} + | {'traverse', TraverseMethod}, + NObjects :: 'default' | pos_integer(), + TraverseMethod :: 'first_next' | 'last_prev' + | 'select' | {'select', MatchSpec :: match_spec()}. table(Tab, Opts) -> case options(Opts, [traverse, n_objects]) of @@ -1135,7 +1186,8 @@ to_string(X) -> lists:flatten(io_lib:format("~p", [X])). %% view a specific table --spec i(tab()) -> 'ok'. +-spec i(Tab) -> 'ok' when + Tab :: tab(). i(Tab) -> i(Tab, 40). diff --git a/lib/stdlib/src/eval_bits.erl b/lib/stdlib/src/eval_bits.erl index 2cbd6cdae7..f40904df1c 100644 --- a/lib/stdlib/src/eval_bits.erl +++ b/lib/stdlib/src/eval_bits.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2010. All Rights Reserved. +%% Copyright Ericsson AB 1999-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -31,15 +31,16 @@ %% @type evalfun(). A closure which evaluates an expression given an %% environment %% -%% @type matchfun(). A closure which performs a match given a value, a -%% pattern and an environment +%% @type matchfun(). A closure which depending on its first argument +%% can perform a match (given a value, a pattern and an environment), +%% lookup a variable in the bindings, or add a new binding %% -%% @type field() represents a field in a "bin" +%% @type field(). Represents a field in a "bin". %%% Part 1: expression evaluation (binary construction) %% @spec expr_grp(Fields::[field()], Bindings::bindings(), -%% EvalFun::evalfun()) -> +%% EvalFun::evalfun(), term(), term()) -> %% {value, binary(), bindings()} %% %% @doc Returns a tuple with {value,Bin,Bs} where Bin is the binary @@ -144,7 +145,8 @@ eval_exp_field(Val, Size, Unit, binary, _, _) -> bin_gen({bin,_,Fs}, Bin, Bs0, BBs0, Mfun, Efun) -> bin_gen(Fs, Bin, Bs0, BBs0, Mfun, Efun, true). -bin_gen([F|Fs], Bin, Bs0, BBs0, Mfun, Efun, Flag) -> +bin_gen([F|Fs], Bin, Bs0, BBs0, Mfun, Efun, Flag) + when is_function(Mfun, 2), is_function(Efun, 2) -> case bin_gen_field(F, Bin, Bs0, BBs0, Mfun, Efun) of {match,Bs,BBs,Rest} -> bin_gen(Fs, Rest, Bs, BBs, Mfun, Efun, Flag); @@ -175,14 +177,14 @@ bin_gen_field({bin_element,Line,VE,Size0,Options0}, {Size1, [Type,{unit,Unit},Sign,Endian]} = make_bit_type(Line, Size0, Options0), V = erl_eval:partial_eval(VE), - match_check_size(Size1, BBs0), + match_check_size(Mfun, Size1, BBs0), {value, Size, _BBs} = Efun(Size1, BBs0), case catch get_value(Bin, Type, Size, Unit, Sign, Endian) of {Val,<<_/bitstring>>=Rest} -> NewV = coerce_to_float(V, Type), - case catch Mfun(NewV, Val, Bs0) of + case catch Mfun(match, {NewV,Val,Bs0}) of {match,Bs} -> - BBs = add_bin_binding(NewV, Bs, BBs0), + BBs = add_bin_binding(Mfun, NewV, Bs, BBs0), {match,Bs,BBs,Rest}; _ -> {nomatch,Rest} @@ -192,9 +194,9 @@ bin_gen_field({bin_element,Line,VE,Size0,Options0}, end. %%% Part 3: binary pattern matching -%% @spec match_bits(Fields::[field()], Bin::binary() +%% @spec match_bits(Fields::[field()], Bin::binary(), %% GlobalEnv::bindings(), LocalEnv::bindings(), -%% MatchFun::matchfun(),EvalFun::evalfun()) -> +%% MatchFun::matchfun(),EvalFun::evalfun(), term()) -> %% {match, bindings()} %% @doc Used to perform matching. If the match succeeds a new %% environment is returned. If the match have some syntactic or @@ -205,7 +207,8 @@ bin_gen_field({bin_element,Line,VE,Size0,Options0}, match_bits(Fs, Bin, Bs0, BBs, Mfun, Efun, _) -> match_bits(Fs, Bin, Bs0, BBs, Mfun, Efun). -match_bits(Fs, Bin, Bs0, BBs, Mfun, Efun) -> +match_bits(Fs, Bin, Bs0, BBs, Mfun, Efun) + when is_function(Mfun, 2), is_function(Efun, 2) -> case catch match_bits_1(Fs, Bin, Bs0, BBs, Mfun, Efun) of {match,Bs} -> {match,Bs}; invalid -> throw(invalid); @@ -230,12 +233,12 @@ match_field_1({bin_element,Line,VE,Size0,Options0}, make_bit_type(Line, Size0, Options0), V = erl_eval:partial_eval(VE), Size2 = erl_eval:partial_eval(Size1), - match_check_size(Size2, BBs0), + match_check_size(Mfun, Size2, BBs0), {value, Size, _BBs} = Efun(Size2, BBs0), {Val,Rest} = get_value(Bin, Type, Size, Unit, Sign, Endian), NewV = coerce_to_float(V, Type), - {match,Bs} = Mfun(NewV, Val, Bs0), - BBs = add_bin_binding(NewV, Bs, BBs0), + {match,Bs} = Mfun(match, {NewV,Val,Bs0}), + BBs = add_bin_binding(Mfun, NewV, Bs, BBs0), {Bs,BBs,Rest}. %% Almost identical to the one in sys_pre_expand. @@ -249,12 +252,12 @@ coerce_to_float({integer,L,I}=E, float) -> coerce_to_float(E, _Type) -> E. -add_bin_binding({var,_,'_'}, _Bs, BBs) -> +add_bin_binding(_, {var,_,'_'}, _Bs, BBs) -> BBs; -add_bin_binding({var,_,Name}, Bs, BBs) -> - {value,Value} = erl_eval:binding(Name, Bs), - erl_eval:add_binding(Name, Value, BBs); -add_bin_binding(_, _Bs, BBs) -> +add_bin_binding(Mfun, {var,_,Name}, Bs, BBs) -> + {value,Value} = Mfun(binding, {Name,Bs}), + Mfun(add_binding, {Name,Value,BBs}); +add_bin_binding(_, _, _Bs, BBs) -> BBs. get_value(Bin, integer, Size, Unit, Sign, Endian) -> @@ -327,20 +330,20 @@ make_bit_type(_Line, Size, Type0) -> %Size evaluates to an integer or 'all' {error,Reason} -> error(Reason) end. -match_check_size({var,_,V}, Bs) -> - case erl_eval:binding(V, Bs) of +match_check_size(Mfun, {var,_,V}, Bs) -> + case Mfun(binding, {V,Bs}) of {value,_} -> ok; unbound -> throw(invalid) % or, rather, error({unbound,V}) end; -match_check_size({atom,_,all}, _Bs) -> +match_check_size(_, {atom,_,all}, _Bs) -> ok; -match_check_size({atom,_,undefined}, _Bs) -> +match_check_size(_, {atom,_,undefined}, _Bs) -> ok; -match_check_size({integer,_,_}, _Bs) -> +match_check_size(_, {integer,_,_}, _Bs) -> ok; -match_check_size({value,_,_}, _Bs) -> +match_check_size(_, {value,_,_}, _Bs) -> ok; %From the debugger. -match_check_size(_, _Bs) -> +match_check_size(_, _, _Bs) -> throw(invalid). %% error(Reason) -> exception thrown diff --git a/lib/stdlib/src/file_sorter.erl b/lib/stdlib/src/file_sorter.erl index 2a5b08b581..3f31852afc 100644 --- a/lib/stdlib/src/file_sorter.erl +++ b/lib/stdlib/src/file_sorter.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2010. All Rights Reserved. +%% Copyright Ericsson AB 2001-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -50,12 +50,68 @@ %%% Exported functions %%% +-export_type([reason/0]). + +-type(file_name() :: file:name()). +-type(file_names() :: [file:name()]). +-type(i_command() :: read | close). +-type(i_reply() :: end_of_input | {end_of_input, value()} + | {[object()], infun()} | input_reply()). +-type(infun() :: fun((i_command()) -> i_reply())). +-type(input() :: file_names() | infun()). +-type(input_reply() :: term()). +-type(o_command() :: {value, value()} | [object()] | close). +-type(o_reply() :: outfun() | output_reply()). +-type(object() :: term() | binary()). +-type(outfun() :: fun((o_command()) -> o_reply())). +-type(output() :: file_name() | outfun()). +-type(output_reply() :: term()). +-type(value() :: term()). + +-type(options() :: [option()] | option()). +-type(option() :: {compressed, boolean()} + | {header, header_length()} + | {format, format()} + | {no_files, no_files()} + | {order, order()} + | {size, size()} + | {tmpdir, tmp_directory()} + | {unique, boolean()}). +-type(format() :: binary_term | term | binary | format_fun()). +-type(format_fun() :: fun((binary()) -> term())). +-type(header_length() :: pos_integer()). +-type(key_pos() :: pos_integer() | [pos_integer()]). +-type(no_files() :: pos_integer()). % > 1 +-type(order() :: ascending | descending | order_fun()). +-type(order_fun() :: fun((term(), term()) -> boolean())). +-type(size() :: non_neg_integer()). +-type(tmp_directory() :: [] | file:name()). + +-type(reason() :: bad_object + | {bad_object, file_name()} + | {bad_term, file_name()} + | {file_error, file_name(), + file:posix() | badarg | system_limit} + | {premature_eof, file_name()}). + +-spec(sort(FileName) -> Reply when + FileName :: file_name(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). sort(FileName) -> sort([FileName], FileName). +-spec(sort(Input, Output) -> Reply when + Input :: input(), + Output :: output(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). sort(Input, Output) -> sort(Input, Output, []). +-spec(sort(Input, Output, Options) -> Reply when + Input :: input(), + Output :: output(), + Options :: options(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). sort(Input0, Output0, Options) -> case {is_input(Input0), maybe_output(Output0), options(Options)} of {{true,Input}, {true,Output}, #opts{}=Opts} -> @@ -64,12 +120,27 @@ sort(Input0, Output0, Options) -> badarg(culprit(tuple_to_list(T)), [Input0, Output0, Options]) end. +-spec(keysort(KeyPos, FileName) -> Reply when + KeyPos :: key_pos(), + FileName :: file_name(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). keysort(KeyPos, FileName) -> keysort(KeyPos, [FileName], FileName). +-spec(keysort(KeyPos, Input, Output) -> Reply when + KeyPos :: key_pos(), + Input :: input(), + Output :: output(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). keysort(KeyPos, Input, Output) -> keysort(KeyPos, Input, Output, []). +-spec(keysort(KeyPos, Input, Output, Options) -> Reply when + KeyPos :: key_pos(), + Input :: input(), + Output :: output(), + Options :: options(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). keysort(KeyPos, Input0, Output0, Options) -> R = case {is_keypos(KeyPos), is_input(Input0), maybe_output(Output0), options(Options)} of @@ -89,9 +160,18 @@ keysort(KeyPos, Input0, Output0, Options) -> badarg(culprit(O), [KeyPos, Input0, Output0, Options]) end. +-spec(merge(FileNames, Output) -> Reply when + FileNames :: file_names(), + Output :: output(), + Reply :: ok | {error, reason()} | output_reply()). merge(Files, Output) -> merge(Files, Output, []). +-spec(merge(FileNames, Output, Options) -> Reply when + FileNames :: file_names(), + Output :: output(), + Options :: options(), + Reply :: ok | {error, reason()} | output_reply()). merge(Files0, Output0, Options) -> case {is_files(Files0), maybe_output(Output0), options(Options)} of %% size not used @@ -101,9 +181,20 @@ merge(Files0, Output0, Options) -> badarg(culprit(tuple_to_list(T)), [Files0, Output0, Options]) end. +-spec(keymerge(KeyPos, FileNames, Output) -> Reply when + KeyPos :: key_pos(), + FileNames :: file_names(), + Output :: output(), + Reply :: ok | {error, reason()} | output_reply()). keymerge(KeyPos, Files, Output) -> keymerge(KeyPos, Files, Output, []). +-spec(keymerge(KeyPos, FileNames, Output, Options) -> Reply when + KeyPos :: key_pos(), + FileNames :: file_names(), + Output :: output(), + Options :: options(), + Reply :: ok | {error, reason()} | output_reply()). keymerge(KeyPos, Files0, Output0, Options) -> R = case {is_keypos(KeyPos), is_files(Files0), maybe_output(Output0), options(Options)} of @@ -123,9 +214,21 @@ keymerge(KeyPos, Files0, Output0, Options) -> badarg(culprit(O), [KeyPos, Files0, Output0, Options]) end. +-spec(check(FileName) -> Reply when + FileName :: file_name(), + Reply :: {ok, [Result]} | {error, reason()}, + Result :: {FileName, TermPosition, term()}, + TermPosition :: pos_integer()). check(FileName) -> check([FileName], []). +-spec(check(FileNames, Options) -> Reply when + FileNames :: file_names(), + Options :: options(), + Reply :: {ok, [Result]} | {error, reason()}, + Result :: {FileName, TermPosition, term()}, + FileName :: file_name(), + TermPosition :: pos_integer()). check(Files0, Options) -> case {is_files(Files0), options(Options)} of {{true,Files}, #opts{}=Opts} -> @@ -134,9 +237,23 @@ check(Files0, Options) -> badarg(culprit(tuple_to_list(T)), [Files0, Options]) end. +-spec(keycheck(KeyPos, FileName) -> Reply when + KeyPos :: key_pos(), + FileName :: file_name(), + Reply :: {ok, [Result]} | {error, reason()}, + Result :: {FileName, TermPosition, term()}, + TermPosition :: pos_integer()). keycheck(KeyPos, FileName) -> keycheck(KeyPos, [FileName], []). +-spec(keycheck(KeyPos, FileNames, Options) -> Reply when + KeyPos :: key_pos(), + FileNames :: file_names(), + Options :: options(), + Reply :: {ok, [Result]} | {error, reason()}, + Result :: {FileName, TermPosition, term()}, + FileName :: file_name(), + TermPosition :: pos_integer()). keycheck(KeyPos, Files0, Options) -> R = case {is_keypos(KeyPos), is_files(Files0), options(Options)} of {_, _, #opts{format = binary}} -> diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl index c845b61204..d532cea187 100644 --- a/lib/stdlib/src/filelib.erl +++ b/lib/stdlib/src/filelib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -40,13 +40,19 @@ erlang:error(UnUsUalVaRiAbLeNaMe) end). +-type filename() :: file:name(). +-type dirname() :: filename(). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec wildcard(file:name()) -> [file:filename()]. +-spec wildcard(Wildcard) -> [file:filename()] when + Wildcard :: filename() | dirname(). wildcard(Pattern) when is_list(Pattern) -> ?HANDLE_ERROR(do_wildcard(Pattern, file)). --spec wildcard(file:name(), file:name() | atom()) -> [file:filename()]. +-spec wildcard(Wildcard, Cwd) -> [file:filename()] when + Wildcard :: filename() | dirname(), + Cwd :: dirname(). wildcard(Pattern, Cwd) when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)) -> ?HANDLE_ERROR(do_wildcard(Pattern, Cwd, file)); wildcard(Pattern, Mod) when is_list(Pattern), is_atom(Mod) -> @@ -57,7 +63,8 @@ wildcard(Pattern, Cwd, Mod) when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)), is_atom(Mod) -> ?HANDLE_ERROR(do_wildcard(Pattern, Cwd, Mod)). --spec is_dir(file:name()) -> boolean(). +-spec is_dir(Name) -> boolean() when + Name :: filename() | dirname(). is_dir(Dir) -> do_is_dir(Dir, file). @@ -65,7 +72,8 @@ is_dir(Dir) -> is_dir(Dir, Mod) when is_atom(Mod) -> do_is_dir(Dir, Mod). --spec is_file(file:name()) -> boolean(). +-spec is_file(Name) -> boolean() when + Name :: filename() | dirname(). is_file(File) -> do_is_file(File, file). @@ -73,7 +81,8 @@ is_file(File) -> is_file(File, Mod) when is_atom(Mod) -> do_is_file(File, Mod). --spec is_regular(file:name()) -> boolean(). +-spec is_regular(Name) -> boolean() when + Name :: filename(). is_regular(File) -> do_is_regular(File, file). @@ -81,7 +90,13 @@ is_regular(File) -> is_regular(File, Mod) when is_atom(Mod) -> do_is_regular(File, Mod). --spec fold_files(file:name(), string(), boolean(), fun((_,_) -> _), _) -> _. +-spec fold_files(Dir, RegExp, Recursive, Fun, AccIn) -> AccOut when + Dir :: dirname(), + RegExp :: string(), + Recursive :: boolean(), + Fun :: fun((F :: file:filename(), AccIn) -> AccOut), + AccIn :: term(), + AccOut :: term(). fold_files(Dir, RegExp, Recursive, Fun, Acc) -> do_fold_files(Dir, RegExp, Recursive, Fun, Acc, file). @@ -89,7 +104,8 @@ fold_files(Dir, RegExp, Recursive, Fun, Acc) -> fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod) when is_atom(Mod) -> do_fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod). --spec last_modified(file:name()) -> file:date_time() | 0. +-spec last_modified(Name) -> file:date_time() | 0 when + Name :: filename() | dirname(). last_modified(File) -> do_last_modified(File, file). @@ -97,7 +113,8 @@ last_modified(File) -> last_modified(File, Mod) when is_atom(Mod) -> do_last_modified(File, Mod). --spec file_size(file:name()) -> non_neg_integer(). +-spec file_size(Filename) -> non_neg_integer() when + Filename :: filename(). file_size(File) -> do_file_size(File, file). @@ -237,7 +254,9 @@ do_file_size(File, Mod) -> %% +type X = filename() | dirname() %% ensures that the directory name required to create D exists --spec ensure_dir(file:name()) -> 'ok' | {'error', file:posix()}. +-spec ensure_dir(Name) -> 'ok' | {'error', Reason} when + Name :: filename() | dirname(), + Reason :: file:posix(). ensure_dir("/") -> ok; ensure_dir(F) -> diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl index 24abf1e977..1cb9e4a25e 100644 --- a/lib/stdlib/src/filename.erl +++ b/lib/stdlib/src/filename.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -61,12 +61,15 @@ %% (for WIN32): absname("/") -> "D:/" --spec absname(file:name()) -> file:filename(). +-spec absname(Filename) -> file:filename() when + Filename :: file:name(). absname(Name) -> {ok, Cwd} = file:get_cwd(), absname(Name, Cwd). --spec absname(file:name(), file:filename()) -> file:filename(). +-spec absname(Filename, Dir) -> file:filename() when + Filename :: file:name(), + Dir :: file:filename(). absname(Name, AbsBase) when is_binary(Name), is_list(AbsBase) -> absname(Name,filename_string_to_binary(AbsBase)); absname(Name, AbsBase) when is_list(Name), is_binary(AbsBase) -> @@ -119,7 +122,9 @@ absname_vr([[X, $:]|Name], _, _AbsBase) -> %% This is just a join/2, but assumes that %% AbsBase must be absolute and Name must be relative. --spec absname_join(file:filename(), file:name()) -> file:filename(). +-spec absname_join(Dir, Filename) -> file:filename() when + Dir :: file:filename(), + Filename :: file:name(). absname_join(AbsBase, Name) -> join(AbsBase, flatten(Name)). @@ -131,7 +136,8 @@ absname_join(AbsBase, Name) -> %% basename("/usr/foo/") -> "foo" (trailing slashes ignored) %% basename("/") -> [] --spec basename(file:name()) -> file:filename(). +-spec basename(Filename) -> file:filename() when + Filename :: file:name(). basename(Name) when is_binary(Name) -> case os:type() of {win32,_} -> @@ -192,7 +198,9 @@ skip_prefix(Name, _) -> %% rootname(basename("xxx.jam")) -> "xxx" %% rootname(basename("xxx.erl")) -> "xxx" --spec basename(file:name(), file:name()) -> file:filename(). +-spec basename(Filename, Ext) -> file:filename() when + Filename :: file:name(), + Ext :: file:name(). basename(Name, Ext) when is_binary(Name), is_list(Ext) -> basename(Name,filename_string_to_binary(Ext)); basename(Name, Ext) when is_list(Name), is_binary(Ext) -> @@ -240,7 +248,8 @@ basename([], _Ext, Tail, _DrvSep2) -> %% Example: dirname("/usr/src/kalle.erl") -> "/usr/src", %% dirname("kalle.erl") -> "." --spec dirname(file:name()) -> file:filename(). +-spec dirname(Filename) -> file:filename() when + Filename :: file:name(). dirname(Name) when is_binary(Name) -> {Dsep,Drivesep} = separators(), SList = case Dsep of @@ -332,7 +341,8 @@ dirjoin1([H|T],Acc,Sep) -> %% %% On Windows: fn:dirname("\\usr\\src/kalle.erl") -> "/usr/src" --spec extension(file:name()) -> file:filename(). +-spec extension(Filename) -> file:filename() when + Filename :: file:name(). extension(Name) when is_binary(Name) -> {Dsep,_} = separators(), SList = case Dsep of @@ -374,7 +384,8 @@ extension([], Result, _OsType) -> %% Joins a list of filenames with directory separators. --spec join([file:filename()]) -> file:filename(). +-spec join(Components) -> file:filename() when + Components :: [file:filename()]. join([Name1, Name2|Rest]) -> join([join(Name1, Name2)|Rest]); join([Name]) when is_list(Name) -> @@ -386,7 +397,9 @@ join([Name]) when is_atom(Name) -> %% Joins two filenames with directory separators. --spec join(file:filename(), file:filename()) -> file:filename(). +-spec join(Name1, Name2) -> file:filename() when + Name1 :: file:filename(), + Name2 :: file:filename(). join(Name1, Name2) when is_list(Name1), is_list(Name2) -> OsType = major_os_type(), case pathtype(Name2) of @@ -494,7 +507,8 @@ append(Dir, Name) -> %% current working volume. (Windows only) %% Example: a:bar.erl, /temp/foo.erl --spec pathtype(file:name()) -> 'absolute' | 'relative' | 'volumerelative'. +-spec pathtype(Path) -> 'absolute' | 'relative' | 'volumerelative' when + Path :: file:name(). pathtype(Atom) when is_atom(Atom) -> pathtype(atom_to_list(Atom)); pathtype(Name) when is_list(Name) or is_binary(Name) -> @@ -547,7 +561,8 @@ win32_pathtype(_) -> relative. %% Examples: rootname("/jam.src/kalle") -> "/jam.src/kalle" %% rootname("/jam.src/foo.erl") -> "/jam.src/foo" --spec rootname(file:name()) -> file:filename(). +-spec rootname(Filename) -> file:filename() when + Filename :: file:name(). rootname(Name) when is_binary(Name) -> list_to_binary(rootname(binary_to_list(Name))); % No need to handle unicode, . is < 128 rootname(Name0) -> @@ -576,7 +591,9 @@ rootname([], Root, _Ext, _OsType) -> %% Examples: rootname("/jam.src/kalle.jam", ".erl") -> "/jam.src/kalle.jam" %% rootname("/jam.src/foo.erl", ".erl") -> "/jam.src/foo" --spec rootname(file:name(), file:name()) -> file:filename(). +-spec rootname(Filename, Ext) -> file:filename() when + Filename :: file:name(), + Ext :: file:name(). rootname(Name, Ext) when is_binary(Name), is_binary(Ext) -> list_to_binary(rootname(binary_to_list(Name),binary_to_list(Ext))); rootname(Name, Ext) when is_binary(Name) -> @@ -602,7 +619,9 @@ rootname2([Char|Rest], Ext, Result) when is_integer(Char) -> %% split("foo/bar") -> ["foo", "bar"] %% split("a:\\msdev\\include") -> ["a:/", "msdev", "include"] --spec split(file:name()) -> [file:filename()]. +-spec split(Filename) -> Components when + Filename :: file:name(), + Components :: [file:filename()]. split(Name) when is_binary(Name) -> case os:type() of {win32, _} -> win32_splitb(Name); @@ -695,7 +714,8 @@ split([], Comp, Components, OsType) -> %% will be converted to backslashes. On all platforms, the %% name will be normalized as done by join/1. --spec nativename(file:filename()) -> file:filename(). +-spec nativename(Path) -> file:filename() when + Path :: file:filename(). nativename(Name0) -> Name = join([Name0]), %Normalize. case os:type() of @@ -747,12 +767,17 @@ separators() -> %% The paths in the {outdir, Path} and {i, Path} options are guaranteed %% to be absolute. --type rule() :: {string(), string()}. --type ecode() :: 'non_existing' | 'preloaded' | 'interpreted'. --type option() :: {'i', string()} | {'outdir', string()} | {'d', atom()}. - --spec find_src(atom() | string()) -> - {string(), [option()]} | {'error', {ecode(), atom()}}. +-spec find_src(Beam) -> {SourceFile, Options} + | {error, {ErrorReason, Module}} when + Beam :: Module | Filename, + Filename :: atom() | string(), + Module :: module(), + SourceFile :: string(), + Options :: [Option], + Option :: {'i', Path :: string()} + | {'outdir', Path :: string()} + | {'d', atom()}, + ErrorReason :: 'non_existing' | 'preloaded' | 'interpreted'. find_src(Mod) -> Default = [{"", ""}, {"ebin", "src"}, {"ebin", "esrc"}], Rules = @@ -763,8 +788,18 @@ find_src(Mod) -> end, find_src(Mod, Rules). --spec find_src(atom() | string(), [rule()]) -> - {string(), [option()]} | {'error', {ecode(), atom()}}. +-spec find_src(Beam, Rules) -> {SourceFile, Options} + | {error, {ErrorReason, Module}} when + Beam :: Module | Filename, + Filename :: atom() | string(), + Rules :: [{BinSuffix :: string(), SourceSuffix :: string()}], + Module :: module(), + SourceFile :: string(), + Options :: [Option], + Option :: {'i', Path :: string()} + | {'outdir', Path :: string()} + | {'d', atom()}, + ErrorReason :: 'non_existing' | 'preloaded' | 'interpreted'. find_src(Mod, Rules) when is_atom(Mod) -> find_src(atom_to_list(Mod), Rules); find_src(File0, Rules) when is_list(File0) -> @@ -890,7 +925,8 @@ major_os_type() -> %% flatten(List) %% Flatten a list, also accepting atoms. --spec flatten(file:name()) -> file:filename(). +-spec flatten(Filename) -> file:filename() when + Filename :: file:name(). flatten(Bin) when is_binary(Bin) -> Bin; flatten(List) -> diff --git a/lib/stdlib/src/gb_sets.erl b/lib/stdlib/src/gb_sets.erl index fc5beb28b0..91d21d869c 100644 --- a/lib/stdlib/src/gb_sets.erl +++ b/lib/stdlib/src/gb_sets.erl @@ -197,6 +197,7 @@ %% Some types. -type gb_set_node() :: 'nil' | {term(), _, _}. +-opaque iter() :: [gb_set_node()]. %% A declaration equivalent to the following is currently hard-coded %% in erl_types.erl @@ -205,38 +206,47 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec empty() -> gb_set(). +-spec empty() -> Set when + Set :: gb_set(). empty() -> {0, nil}. --spec new() -> gb_set(). +-spec new() -> Set when + Set :: gb_set(). new() -> empty(). --spec is_empty(gb_set()) -> boolean(). +-spec is_empty(Set) -> boolean() when + Set :: gb_set(). is_empty({0, nil}) -> true; is_empty(_) -> false. --spec size(gb_set()) -> non_neg_integer(). +-spec size(Set) -> non_neg_integer() when + Set :: gb_set(). size({Size, _}) -> Size. --spec singleton(term()) -> gb_set(). +-spec singleton(Element) -> gb_set() when + Element :: term(). singleton(Key) -> {1, {Key, nil, nil}}. --spec is_element(term(), gb_set()) -> boolean(). +-spec is_element(Element, Set) -> boolean() when + Element :: term(), + Set :: gb_set(). is_element(Key, S) -> is_member(Key, S). --spec is_member(term(), gb_set()) -> boolean(). +-spec is_member(Element, Set) -> boolean() when + Element :: term(), + Set :: gb_set(). is_member(Key, {_, T}) -> is_member_1(Key, T). @@ -250,7 +260,10 @@ is_member_1(_, {_, _, _}) -> is_member_1(_, nil) -> false. --spec insert(term(), gb_set()) -> gb_set(). +-spec insert(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). insert(Key, {S, T}) -> S1 = S + 1, @@ -306,7 +319,9 @@ count({_, Sm, Bi}) -> count(nil) -> {1, 0}. --spec balance(gb_set()) -> gb_set(). +-spec balance(Set1) -> Set2 when + Set1 :: gb_set(), + Set2 :: gb_set(). balance({S, T}) -> {S, balance(T, S)}. @@ -331,12 +346,18 @@ balance_list_1([Key | L], 1) -> balance_list_1(L, 0) -> {nil, L}. --spec add_element(term(), gb_set()) -> gb_set(). +-spec add_element(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). add_element(X, S) -> add(X, S). --spec add(term(), gb_set()) -> gb_set(). +-spec add(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). add(X, S) -> case is_member(X, S) of @@ -346,23 +367,33 @@ add(X, S) -> insert(X, S) end. --spec from_list([term()]) -> gb_set(). +-spec from_list(List) -> Set when + List :: [term()], + Set :: gb_set(). from_list(L) -> from_ordset(ordsets:from_list(L)). --spec from_ordset([term()]) -> gb_set(). +-spec from_ordset(List) -> Set when + List :: [term()], + Set :: gb_set(). from_ordset(L) -> S = length(L), {S, balance_list(L, S)}. --spec del_element(term(), gb_set()) -> gb_set(). +-spec del_element(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). del_element(Key, S) -> delete_any(Key, S). --spec delete_any(term(), gb_set()) -> gb_set(). +-spec delete_any(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). delete_any(Key, S) -> case is_member(Key, S) of @@ -372,7 +403,10 @@ delete_any(Key, S) -> S end. --spec delete(term(), gb_set()) -> gb_set(). +-spec delete(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). delete(Key, {S, T}) -> {S - 1, delete_1(Key, T)}. @@ -394,7 +428,10 @@ merge(Smaller, Larger) -> {Key, Larger1} = take_smallest1(Larger), {Key, Smaller, Larger1}. --spec take_smallest(gb_set()) -> {term(), gb_set()}. +-spec take_smallest(Set1) -> {Element, Set2} when + Set1 :: gb_set(), + Set2 :: gb_set(), + Element :: term(). take_smallest({S, T}) -> {Key, Larger} = take_smallest1(T), @@ -406,7 +443,8 @@ take_smallest1({Key, Smaller, Larger}) -> {Key1, Smaller1} = take_smallest1(Smaller), {Key1, {Key, Smaller1, Larger}}. --spec smallest(gb_set()) -> term(). +-spec smallest(Set) -> term() when + Set :: gb_set(). smallest({_, T}) -> smallest_1(T). @@ -416,7 +454,10 @@ smallest_1({Key, nil, _Larger}) -> smallest_1({_Key, Smaller, _Larger}) -> smallest_1(Smaller). --spec take_largest(gb_set()) -> {term(), gb_set()}. +-spec take_largest(Set1) -> {Element, Set2} when + Set1 :: gb_set(), + Set2 :: gb_set(), + Element :: term(). take_largest({S, T}) -> {Key, Smaller} = take_largest1(T), @@ -428,7 +469,8 @@ take_largest1({Key, Smaller, Larger}) -> {Key1, Larger1} = take_largest1(Larger), {Key1, {Key, Smaller, Larger1}}. --spec largest(gb_set()) -> term(). +-spec largest(Set) -> term() when + Set :: gb_set(). largest({_, T}) -> largest_1(T). @@ -438,7 +480,9 @@ largest_1({Key, _Smaller, nil}) -> largest_1({_Key, _Smaller, Larger}) -> largest_1(Larger). --spec to_list(gb_set()) -> [term()]. +-spec to_list(Set) -> List when + Set :: gb_set(), + List :: [term()]. to_list({_, T}) -> to_list(T, []). @@ -449,7 +493,9 @@ to_list({Key, Small, Big}, L) -> to_list(Small, [Key | to_list(Big, L)]); to_list(nil, L) -> L. --spec iterator(gb_set()) -> [term()]. +-spec iterator(Set) -> Iter when + Set :: gb_set(), + Iter :: iter(). iterator({_, T}) -> iterator(T, []). @@ -464,7 +510,10 @@ iterator({_, L, _} = T, As) -> iterator(nil, As) -> As. --spec next([term()]) -> {term(), [term()]} | 'none'. +-spec next(Iter1) -> {Element, Iter2} | 'none' when + Iter1 :: iter(), + Iter2 :: iter(), + Element :: term(). next([{X, _, T} | As]) -> {X, iterator(T, As)}; @@ -494,7 +543,10 @@ next([]) -> %% traversing the elements can be devised, but they all have higher %% overhead. --spec union(gb_set(), gb_set()) -> gb_set(). +-spec union(Set1, Set2) -> Set3 when + Set1 :: gb_set(), + Set2 :: gb_set(), + Set3 :: gb_set(). union({N1, T1}, {N2, T2}) when N2 < N1 -> union(to_list_1(T2), N2, T1, N1); @@ -596,7 +648,9 @@ balance_revlist_1([Key | L], 1) -> balance_revlist_1(L, 0) -> {nil, L}. --spec union([gb_set()]) -> gb_set(). +-spec union(SetList) -> Set when + SetList :: [gb_set(),...], + Set :: gb_set(). union([S | Ss]) -> union_list(S, Ss); @@ -609,7 +663,10 @@ union_list(S, []) -> S. %% The rest is modelled on the above. --spec intersection(gb_set(), gb_set()) -> gb_set(). +-spec intersection(Set1, Set2) -> Set3 when + Set1 :: gb_set(), + Set2 :: gb_set(), + Set3 :: gb_set(). intersection({N1, T1}, {N2, T2}) when N2 < N1 -> intersection(to_list_1(T2), N2, T1, N1); @@ -657,7 +714,9 @@ intersection_2([], _, As, S) -> intersection_2(_, [], As, S) -> {S, balance_revlist(As, S)}. --spec intersection([gb_set(),...]) -> gb_set(). +-spec intersection(SetList) -> Set when + SetList :: [gb_set(),...], + Set :: gb_set(). intersection([S | Ss]) -> intersection_list(S, Ss). @@ -666,7 +725,9 @@ intersection_list(S, [S1 | Ss]) -> intersection_list(intersection(S, S1), Ss); intersection_list(S, []) -> S. --spec is_disjoint(gb_set(), gb_set()) -> boolean(). +-spec is_disjoint(Set1, Set2) -> boolean() when + Set1 :: gb_set(), + Set2 :: gb_set(). is_disjoint({N1, T1}, {N2, T2}) when N1 < N2 -> is_disjoint_1(T1, T2); @@ -694,12 +755,18 @@ is_disjoint_1(_, nil) -> %% the sets. Therefore, we always build a new tree, and thus we need to %% traverse the whole element list of the left operand. --spec subtract(gb_set(), gb_set()) -> gb_set(). +-spec subtract(Set1, Set2) -> Set3 when + Set1 :: gb_set(), + Set2 :: gb_set(), + Set3 :: gb_set(). subtract(S1, S2) -> difference(S1, S2). --spec difference(gb_set(), gb_set()) -> gb_set(). +-spec difference(Set1, Set2) -> Set3 when + Set1 :: gb_set(), + Set2 :: gb_set(), + Set3 :: gb_set(). difference({N1, T1}, {N2, T2}) -> difference(to_list_1(T1), N1, T2, N2). @@ -747,7 +814,9 @@ difference_2(Xs, [], As, S) -> %% Subset testing is much the same thing as set difference, but %% without the construction of a new set. --spec is_subset(gb_set(), gb_set()) -> boolean(). +-spec is_subset(Set1, Set2) -> boolean() when + Set1 :: gb_set(), + Set2 :: gb_set(). is_subset({N1, T1}, {N2, T2}) -> is_subset(to_list_1(T1), N1, T2, N2). @@ -788,18 +857,28 @@ is_subset_2(_, []) -> %% For compatibility with `sets': --spec is_set(term()) -> boolean(). +-spec is_set(Term) -> boolean() when + Term :: term(). is_set({0, nil}) -> true; is_set({N, {_, _, _}}) when is_integer(N), N >= 0 -> true; is_set(_) -> false. --spec filter(fun((term()) -> boolean()), gb_set()) -> gb_set(). +-spec filter(Pred, Set1) -> Set2 when + Pred :: fun((E :: term()) -> boolean()), + Set1 :: gb_set(), + Set2 :: gb_set(). filter(F, S) -> from_ordset([X || X <- to_list(S), F(X)]). --spec fold(fun((term(), term()) -> term()), term(), gb_set()) -> term(). +-spec fold(Function, Acc0, Set) -> Acc1 when + Function :: fun((E :: term(), AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Set :: gb_set(). fold(F, A, {_, T}) when is_function(F, 2) -> fold_1(F, A, T). diff --git a/lib/stdlib/src/gb_trees.erl b/lib/stdlib/src/gb_trees.erl index d37786a100..6ad861ff5b 100644 --- a/lib/stdlib/src/gb_trees.erl +++ b/lib/stdlib/src/gb_trees.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -153,6 +153,7 @@ %% Some types. -type gb_tree_node() :: 'nil' | {_, _, _, _}. +-opaque iter() :: [gb_tree_node()]. %% A declaration equivalent to the following is currently hard-coded %% in erl_types.erl @@ -166,21 +167,26 @@ empty() -> {0, nil}. --spec is_empty(gb_tree()) -> boolean(). +-spec is_empty(Tree) -> boolean() when + Tree :: gb_tree(). is_empty({0, nil}) -> true; is_empty(_) -> false. --spec size(gb_tree()) -> non_neg_integer(). +-spec size(Tree) -> non_neg_integer() when + Tree :: gb_tree(). size({Size, _}) when is_integer(Size), Size >= 0 -> Size. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec lookup(term(), gb_tree()) -> 'none' | {'value', term()}. +-spec lookup(Key, Tree) -> 'none' | {'value', Val} when + Key :: term(), + Val :: term(), + Tree :: gb_tree(). lookup(Key, {_, T}) -> lookup_1(Key, T). @@ -205,7 +211,9 @@ lookup_1(_, nil) -> %% This is a specialized version of `lookup'. --spec is_defined(term(), gb_tree()) -> boolean(). +-spec is_defined(Key, Tree) -> boolean() when + Key :: term(), + Tree :: gb_tree(). is_defined(Key, {_, T}) -> is_defined_1(Key, T). @@ -223,7 +231,10 @@ is_defined_1(_, nil) -> %% This is a specialized version of `lookup'. --spec get(term(), gb_tree()) -> term(). +-spec get(Key, Tree) -> Val when + Key :: term(), + Tree :: gb_tree(), + Val :: term(). get(Key, {_, T}) -> get_1(Key, T). @@ -237,7 +248,11 @@ get_1(_, {_, Value, _, _}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec update(term(), term(), gb_tree()) -> gb_tree(). +-spec update(Key, Val, Tree1) -> Tree2 when + Key :: term(), + Val :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). update(Key, Val, {S, T}) -> T1 = update_1(Key, Val, T), @@ -254,7 +269,11 @@ update_1(Key, Value, {_, _, Smaller, Bigger}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec insert(term(), term(), gb_tree()) -> gb_tree(). +-spec insert(Key, Val, Tree1) -> Tree2 when + Key :: term(), + Val :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). insert(Key, Val, {S, T}) when is_integer(S) -> S1 = S+1, @@ -303,7 +322,11 @@ insert_1(Key, _, _, _) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec enter(term(), term(), gb_tree()) -> gb_tree(). +-spec enter(Key, Val, Tree1) -> Tree2 when + Key :: term(), + Val :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). enter(Key, Val, T) -> case is_defined(Key, T) of @@ -326,7 +349,9 @@ count(nil) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec balance(gb_tree()) -> gb_tree(). +-spec balance(Tree1) -> Tree2 when + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). balance({S, T}) -> {S, balance(T, S)}. @@ -351,7 +376,9 @@ balance_list_1([{Key, Val} | L], 1) -> balance_list_1(L, 0) -> {nil, L}. --spec from_orddict([{_,_}]) -> gb_tree(). +-spec from_orddict(List) -> Tree when + List :: [{Key :: term(), Val :: term()}], + Tree :: gb_tree(). from_orddict(L) -> S = length(L), @@ -359,7 +386,10 @@ from_orddict(L) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec delete_any(term(), gb_tree()) -> gb_tree(). +-spec delete_any(Key, Tree1) -> Tree2 when + Key :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). delete_any(Key, T) -> case is_defined(Key, T) of @@ -371,7 +401,10 @@ delete_any(Key, T) -> %%% delete. Assumes that key is present. --spec delete(term(), gb_tree()) -> gb_tree(). +-spec delete(Key, Tree1) -> Tree2 when + Key :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). delete(Key, {S, T}) when is_integer(S), S >= 0 -> {S - 1, delete_1(Key, T)}. @@ -397,7 +430,11 @@ merge(Smaller, Larger) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec take_smallest(gb_tree()) -> {term(), term(), gb_tree()}. +-spec take_smallest(Tree1) -> {Key, Val, Tree2} when + Tree1 :: gb_tree(), + Tree2 :: gb_tree(), + Key :: term(), + Val :: term(). take_smallest({Size, Tree}) when is_integer(Size), Size >= 0 -> {Key, Value, Larger} = take_smallest1(Tree), @@ -409,7 +446,10 @@ take_smallest1({Key, Value, Smaller, Larger}) -> {Key1, Value1, Smaller1} = take_smallest1(Smaller), {Key1, Value1, {Key, Value, Smaller1, Larger}}. --spec smallest(gb_tree()) -> {term(), term()}. +-spec smallest(Tree) -> {Key, Val} when + Tree :: gb_tree(), + Key :: term(), + Val :: term(). smallest({_, Tree}) -> smallest_1(Tree). @@ -419,7 +459,11 @@ smallest_1({Key, Value, nil, _Larger}) -> smallest_1({_Key, _Value, Smaller, _Larger}) -> smallest_1(Smaller). --spec take_largest(gb_tree()) -> {term(), term(), gb_tree()}. +-spec take_largest(Tree1) -> {Key, Val, Tree2} when + Tree1 :: gb_tree(), + Tree2 :: gb_tree(), + Key :: term(), + Val :: term(). take_largest({Size, Tree}) when is_integer(Size), Size >= 0 -> {Key, Value, Smaller} = take_largest1(Tree), @@ -431,7 +475,10 @@ take_largest1({Key, Value, Smaller, Larger}) -> {Key1, Value1, Larger1} = take_largest1(Larger), {Key1, Value1, {Key, Value, Smaller, Larger1}}. --spec largest(gb_tree()) -> {term(), term()}. +-spec largest(Tree) -> {Key, Val} when + Tree :: gb_tree(), + Key :: term(), + Val :: term(). largest({_, Tree}) -> largest_1(Tree). @@ -443,7 +490,10 @@ largest_1({_Key, _Value, _Smaller, Larger}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec to_list(gb_tree()) -> [{term(), term()}]. +-spec to_list(Tree) -> [{Key, Val}] when + Tree :: gb_tree(), + Key :: term(), + Val :: term(). to_list({_, T}) -> to_list(T, []). @@ -456,7 +506,9 @@ to_list(nil, L) -> L. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec keys(gb_tree()) -> [term()]. +-spec keys(Tree) -> [Key] when + Tree :: gb_tree(), + Key :: term(). keys({_, T}) -> keys(T, []). @@ -467,7 +519,9 @@ keys(nil, L) -> L. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec values(gb_tree()) -> [term()]. +-spec values(Tree) -> [Val] when + Tree :: gb_tree(), + Val :: term(). values({_, T}) -> values(T, []). @@ -478,7 +532,9 @@ values(nil, L) -> L. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec iterator(gb_tree()) -> [gb_tree_node()]. +-spec iterator(Tree) -> Iter when + Tree :: gb_tree(), + Iter :: iter(). iterator({_, T}) -> iterator_1(T). @@ -496,7 +552,11 @@ iterator({_, _, L, _} = T, As) -> iterator(nil, As) -> As. --spec next([gb_tree_node()]) -> 'none' | {term(), term(), [gb_tree_node()]}. +-spec next(Iter1) -> 'none' | {Key, Val, Iter2} when + Iter1 :: iter(), + Iter2 :: iter(), + Key :: term(), + Val :: term(). next([{X, V, _, T} | As]) -> {X, V, iterator(T, As)}; @@ -505,7 +565,10 @@ next([]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec map(fun((term(), term()) -> term()), gb_tree()) -> gb_tree(). +-spec map(Function, Tree1) -> Tree2 when + Function :: fun((K :: term(), V1 :: term()) -> V2 :: term()), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). map(F, {Size, Tree}) when is_function(F, 2) -> {Size, map_1(F, Tree)}. diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl index 43df6f621d..574146b1cd 100644 --- a/lib/stdlib/src/gen.erl +++ b/lib/stdlib/src/gen.erl @@ -29,6 +29,8 @@ -export([init_it/6, init_it/7]). +-export([format_status_header/2]). + -define(default_timeout, 5000). %%----------------------------------------------------------------- @@ -315,3 +317,10 @@ debug_options(Opts) -> {ok, Options} -> sys:debug_options(Options); _ -> [] end. + +format_status_header(TagLine, Pid) when is_pid(Pid) -> + lists:concat([TagLine, " ", pid_to_list(Pid)]); +format_status_header(TagLine, RegName) when is_atom(RegName) -> + lists:concat([TagLine, " ", RegName]); +format_status_header(TagLine, Name) -> + {TagLine, Name}. diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl index b1e9e3a02f..9879b76391 100644 --- a/lib/stdlib/src/gen_event.erl +++ b/lib/stdlib/src/gen_event.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -36,14 +36,15 @@ add_handler/3, add_sup_handler/3, delete_handler/3, swap_handler/3, swap_sup_handler/3, which_handlers/1, call/3, call/4, wake_hib/4]). --export([behaviour_info/1]). - -export([init_it/6, system_continue/3, system_terminate/4, system_code_change/4, format_status/2]). +-export_type([handler/0, handler_args/0, add_handler_ret/0, + del_handler_ret/0]). + -import(error_logger, [error_msg/2]). -define(reply(X), From ! {element(2,Tag), X}). @@ -57,14 +58,6 @@ %%% API %%%========================================================================= --spec behaviour_info(atom()) -> 'undefined' | [{atom(), arity()}]. - -behaviour_info(callbacks) -> - [{init,1},{handle_event,2},{handle_call,2},{handle_info,2}, - {terminate,2},{code_change,3}]; -behaviour_info(_Other) -> - undefined. - %% gen_event:start(Handler) -> {ok, Pid} | {error, What} %% gen_event:add_handler(Handler, Mod, Args) -> ok | Other %% gen_event:notify(Handler, Event) -> ok @@ -75,45 +68,44 @@ behaviour_info(_Other) -> %% gen_event:which_handler(Handler) -> [Mod] %% gen_event:stop(Handler) -> ok - -%% handlers must export -%% Mod:init(Args) -> {ok, State} | Other -%% Mod:handle_event(Event, State) -> -%% {ok, State'} | remove_handler | {swap_handler,Args1,State1,Mod2,Args2} -%% Mod:handle_info(Info, State) -> -%% {ok, State'} | remove_handler | {swap_handler,Args1,State1,Mod2,Args2} -%% Mod:handle_call(Query, State) -> -%% {ok, Reply, State'} | {remove_handler, Reply} | -%% {swap_handler, Reply, Args1,State1,Mod2,Args2} -%% Mod:terminate(Args, State) -> Val - - -%% add_handler(H, Mod, Args) -> ok | Other -%% Mod:init(Args) -> {ok, State} | Other - -%% delete_handler(H, Mod, Args) -> Val -%% Mod:terminate(Args, State) -> Val - -%% notify(H, Event) -%% Mod:handle_event(Event, State) -> -%% {ok, State1} -%% remove_handler -%% Mod:terminate(remove_handler, State) is called -%% the return value is ignored -%% {swap_handler, Args1, State1, Mod2, Args2} -%% State2 = Mod:terminate(Args1, State1) is called -%% the return value is chained into the new module and -%% Mod2:init({Args2, State2}) is called -%% Other -%% Mod:terminate({error, Other}, State) is called -%% The return value is ignored -%% call(H, Mod, Query) -> Val -%% call(H, Mod, Query, Timeout) -> Val -%% Mod:handle_call(Query, State) -> as above +-callback init(InitArgs :: term()) -> + {ok, State :: term()} | + {ok, State :: term(), hibernate}. +-callback handle_event(Event :: term(), State :: term()) -> + {ok, NewState :: term()} | + {ok, NewState :: term(), hibernate} | + {swap_handler, Args1 :: term(), NewState :: term(), + Handler2 :: (atom() | {atom(), Id :: term()}), Args2 :: term()} | + remove_handler. +-callback handle_call(Request :: term(), State :: term()) -> + {ok, Reply :: term(), NewState :: term()} | + {ok, Reply :: term(), NewState :: term(), hibernate} | + {swap_handler, Reply :: term(), Args1 :: term(), NewState :: term(), + Handler2 :: (atom() | {atom(), Id :: term()}), Args2 :: term()} | + {remove_handler, Reply :: term()}. +-callback handle_info(Info :: term(), State :: term()) -> + {ok, NewState :: term()} | + {ok, NewState :: term(), hibernate} | + {swap_handler, Args1 :: term(), NewState :: term(), + Handler2 :: (atom() | {atom(), Id :: term()}), Args2 :: term()} | + remove_handler. +-callback terminate(Args :: (term() | {stop, Reason :: term()} | + stop | remove_handler | + {error, {'EXIT', Reason :: term()}} | + {error, term()}), + State :: term()) -> + term(). +-callback code_change(OldVsn :: (term() | {down, term()}), + State :: term(), Extra :: term()) -> + {ok, NewState :: term()}. %%--------------------------------------------------------------------------- --type handler() :: atom() | {atom(), term()}. +-type handler() :: atom() | {atom(), term()}. +-type handler_args() :: term(). +-type add_handler_ret() :: ok | term() | {'EXIT',term()}. +-type del_handler_ret() :: ok | term() | {'EXIT',term()}. + -type emgr_name() :: {'local', atom()} | {'global', atom()}. -type emgr_ref() :: atom() | {atom(), atom()} | {'global', atom()} | pid(). -type start_ret() :: {'ok', pid()} | {'error', term()}. @@ -660,16 +652,16 @@ report_error(_Handler, {swapped,_,_}, _, _, _) -> ok; report_error(Handler, Reason, State, LastIn, SName) -> Reason1 = case Reason of - {'EXIT',{undef,[{M,F,A}|MFAs]}} -> + {'EXIT',{undef,[{M,F,A,L}|MFAs]}} -> case code:is_loaded(M) of false -> - {'module could not be loaded',[{M,F,A}|MFAs]}; + {'module could not be loaded',[{M,F,A,L}|MFAs]}; _ -> case erlang:function_exported(M, F, length(A)) of true -> - {undef,[{M,F,A}|MFAs]}; + {undef,[{M,F,A,L}|MFAs]}; false -> - {'function not exported',[{M,F,A}|MFAs]} + {'function not exported',[{M,F,A,L}|MFAs]} end end; {'EXIT',Why} -> @@ -724,7 +716,8 @@ get_modules(MSL) -> %%----------------------------------------------------------------- format_status(Opt, StatusData) -> [PDict, SysState, Parent, _Debug, [ServerName, MSL, _Hib]] = StatusData, - Header = lists:concat(["Status for event handler ", ServerName]), + Header = gen:format_status_header("Status for event handler", + ServerName), FmtMSL = [case erlang:function_exported(Mod, format_status, 2) of true -> Args = [PDict, State], diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl index 7d9960b912..3db8c9f4f2 100644 --- a/lib/stdlib/src/gen_fsm.erl +++ b/lib/stdlib/src/gen_fsm.erl @@ -113,8 +113,6 @@ start_timer/2,send_event_after/2,cancel_timer/1, enter_loop/4, enter_loop/5, enter_loop/6, wake_hib/6]). --export([behaviour_info/1]). - %% Internal exports -export([init_it/6, system_continue/3, @@ -128,13 +126,38 @@ %%% Interface functions. %%% --------------------------------------------------- --spec behaviour_info(atom()) -> 'undefined' | [{atom(), arity()}]. - -behaviour_info(callbacks) -> - [{init,1},{handle_event,3},{handle_sync_event,4},{handle_info,3}, - {terminate,3},{code_change,4}]; -behaviour_info(_Other) -> - undefined. +-callback init(Args :: term()) -> + {ok, StateName :: atom(), StateData :: term()} | + {ok, StateName :: atom(), StateData :: term(), timeout() | hibernate} | + {stop, Reason :: term()} | ignore. +-callback handle_event(Event :: term(), StateName :: atom(), + StateData :: term()) -> + {next_state, NextStateName :: atom(), NewStateData :: term()} | + {next_state, NextStateName :: atom(), NewStateData :: term(), + timeout() | hibernate} | + {stop, Reason :: term(), NewStateData :: term()}. +-callback handle_sync_event(Event :: term(), From :: {pid(), Tag :: term()}, + StateName :: atom(), StateData :: term()) -> + {reply, Reply :: term(), NextStateName :: atom(), NewStateData :: term()} | + {reply, Reply :: term(), NextStateName :: atom(), NewStateData :: term(), + timeout() | hibernate} | + {next_state, NextStateName :: atom(), NewStateData :: term()} | + {next_state, NextStateName :: atom(), NewStateData :: term(), + timeout() | hibernate} | + {stop, Reason :: term(), Reply :: term(), NewStateData :: term()} | + {stop, Reason :: term(), NewStateData :: term()}. +-callback handle_info(Info :: term(), StateName :: atom(), + StateData :: term()) -> + {next_state, NextStateName :: atom(), NewStateData :: term()} | + {next_state, NextStateName :: atom(), NewStateData :: term(), + timeout() | hibernate} | + {stop, Reason :: normal | term(), NewStateData :: term()}. +-callback terminate(Reason :: normal | shutdown | {shutdown, term()} + | term(), StateName :: atom(), StateData :: term()) -> + term(). +-callback code_change(OldVsn :: term() | {down, term()}, StateName :: atom(), + StateData :: term(), Extra :: term()) -> + {ok, NextStateName :: atom(), NewStateData :: term()}. %%% --------------------------------------------------- %%% Starts a generic state machine. @@ -561,16 +584,16 @@ terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug) -> error_info(Reason, Name, Msg, StateName, StateData, Debug) -> Reason1 = case Reason of - {undef,[{M,F,A}|MFAs]} -> + {undef,[{M,F,A,L}|MFAs]} -> case code:is_loaded(M) of false -> - {'module could not be loaded',[{M,F,A}|MFAs]}; + {'module could not be loaded',[{M,F,A,L}|MFAs]}; _ -> case erlang:function_exported(M, F, length(A)) of true -> Reason; false -> - {'function not exported',[{M,F,A}|MFAs]} + {'function not exported',[{M,F,A,L}|MFAs]} end end; _ -> @@ -614,15 +637,8 @@ get_msg(Msg) -> Msg. format_status(Opt, StatusData) -> [PDict, SysState, Parent, Debug, [Name, StateName, StateData, Mod, _Time]] = StatusData, - StatusHdr = "Status for state machine", - Header = if - is_pid(Name) -> - lists:concat([StatusHdr, " ", pid_to_list(Name)]); - is_atom(Name); is_list(Name) -> - lists:concat([StatusHdr, " ", Name]); - true -> - {StatusHdr, Name} - end, + Header = gen:format_status_header("Status for state machine", + Name), Log = sys:get_debug(log, Debug, []), DefaultStatus = [{data, [{"StateData", StateData}]}], Specfic = diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl index ac81df9cab..dd0ef74f30 100644 --- a/lib/stdlib/src/gen_server.erl +++ b/lib/stdlib/src/gen_server.erl @@ -94,8 +94,6 @@ multi_call/2, multi_call/3, multi_call/4, enter_loop/3, enter_loop/4, enter_loop/5, wake_hib/5]). --export([behaviour_info/1]). - %% System exports -export([system_continue/3, system_terminate/4, @@ -111,13 +109,32 @@ %%% API %%%========================================================================= --spec behaviour_info(atom()) -> 'undefined' | [{atom(), arity()}]. - -behaviour_info(callbacks) -> - [{init,1},{handle_call,3},{handle_cast,2},{handle_info,2}, - {terminate,2},{code_change,3}]; -behaviour_info(_Other) -> - undefined. +-callback init(Args :: term()) -> + {ok, State :: term()} | {ok, State :: term(), timeout() | hibernate} | + {stop, Reason :: term()} | ignore. +-callback handle_call(Request :: term(), From :: {pid(), Tag :: term()}, + State :: term()) -> + {reply, Reply :: term(), NewState :: term()} | + {reply, Reply :: term(), NewState :: term(), timeout() | hibernate} | + {noreply, NewState :: term()} | + {noreply, NewState :: term(), timeout() | hibernate} | + {stop, Reason :: term(), Reply :: term(), NewState :: term()} | + {stop, Reason :: term(), NewState :: term()}. +-callback handle_cast(Request :: term(), State :: term()) -> + {noreply, NewState :: term()} | + {noreply, NewState :: term(), timeout() | hibernate} | + {stop, Reason :: term(), NewState :: term()}. +-callback handle_info(Info :: timeout() | term(), State :: term()) -> + {noreply, NewState :: term()} | + {noreply, NewState :: term(), timeout() | hibernate} | + {stop, Reason :: term(), NewState :: term()}. +-callback terminate(Reason :: (normal | shutdown | {shutdown, term()} | + term()), + State :: term()) -> + term(). +-callback code_change(OldVsn :: (term() | {down, term()}), State :: term(), + Extra :: term()) -> + {ok, NewState :: term()}. %%% ----------------------------------------------------------------- %%% Starts a generic server. @@ -729,16 +746,16 @@ error_info(_Reason, application_controller, _Msg, _State, _Debug) -> error_info(Reason, Name, Msg, State, Debug) -> Reason1 = case Reason of - {undef,[{M,F,A}|MFAs]} -> + {undef,[{M,F,A,L}|MFAs]} -> case code:is_loaded(M) of false -> - {'module could not be loaded',[{M,F,A}|MFAs]}; + {'module could not be loaded',[{M,F,A,L}|MFAs]}; _ -> case erlang:function_exported(M, F, length(A)) of true -> Reason; false -> - {'function not exported',[{M,F,A}|MFAs]} + {'function not exported',[{M,F,A,L}|MFAs]} end end; _ -> @@ -840,15 +857,8 @@ name_to_pid(Name) -> %%----------------------------------------------------------------- format_status(Opt, StatusData) -> [PDict, SysState, Parent, Debug, [Name, State, Mod, _Time]] = StatusData, - StatusHdr = "Status for generic server", - Header = if - is_pid(Name) -> - lists:concat([StatusHdr, " ", pid_to_list(Name)]); - is_atom(Name); is_list(Name) -> - lists:concat([StatusHdr, " ", Name]); - true -> - {StatusHdr, Name} - end, + Header = gen:format_status_header("Status for generic server", + Name), Log = sys:get_debug(log, Debug, []), DefaultStatus = [{data, [{"State", State}]}], Specfic = diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl index 6aeb076a0b..9f65bbfa3a 100644 --- a/lib/stdlib/src/io.erl +++ b/lib/stdlib/src/io.erl @@ -41,6 +41,7 @@ -type error_description() :: term(). % Whatever the io-server sends. -type request_error() :: {'error',error_description()}. + %% XXX: Some uses of line() in this file may need to read erl_scan:location() -type line() :: pos_integer(). @@ -66,12 +67,15 @@ o_request(Io, Request, Func) -> end. %% Put chars takes mixed *unicode* list from R13 onwards. --spec put_chars(iodata()) -> 'ok'. +-spec put_chars(CharData) -> 'ok' when + CharData :: unicode:chardata(). put_chars(Chars) -> put_chars(default_output(), Chars). --spec put_chars(device(), iodata()) -> 'ok'. +-spec put_chars(IoDevice, IoData) -> 'ok' when + IoDevice :: device(), + IoData :: unicode:chardata(). put_chars(Io, Chars) -> o_request(Io, {put_chars,unicode,Chars}, put_chars). @@ -81,7 +85,8 @@ put_chars(Io, Chars) -> nl() -> nl(default_output()). --spec nl(device()) -> 'ok'. +-spec nl(IoDevice) -> 'ok' when + IoDevice :: device(). nl(Io) -> % o_request(Io, {put_chars,io_lib:nl()}). @@ -92,7 +97,8 @@ nl(Io) -> columns() -> columns(default_output()). --spec columns(device()) -> {'ok', pos_integer()} | {'error', 'enotsup'}. +-spec columns(IoDevice) -> {'ok', pos_integer()} | {'error', 'enotsup'} when + IoDevice :: device(). columns(Io) -> case request(Io, {get_geometry,columns}) of @@ -107,7 +113,8 @@ columns(Io) -> rows() -> rows(default_output()). --spec rows(device()) -> {'ok', pos_integer()} | {'error', 'enotsup'}. +-spec rows(IoDevice) -> {'ok', pos_integer()} | {'error', 'enotsup'} when + IoDevice :: device(). rows(Io) -> case request(Io,{get_geometry,rows}) of @@ -117,22 +124,36 @@ rows(Io) -> {error,enotsup} end. --spec get_chars(prompt(), non_neg_integer()) -> iodata() | 'eof'. +-spec get_chars(Prompt, Count) -> Data | 'eof' when + Prompt :: prompt(), + Count :: non_neg_integer(), + Data :: [unicode:unicode_char()] | unicode:unicode_binary(). get_chars(Prompt, N) -> get_chars(default_input(), Prompt, N). --spec get_chars(device(), prompt(), non_neg_integer()) -> iodata() | 'eof'. +-spec get_chars(IoDevice, Prompt, Count) -> Data | 'eof' | {error, Reason} when + IoDevice :: device(), + Prompt :: prompt(), + Count :: non_neg_integer(), + Reason :: term(), + Data :: [unicode:unicode_char()] | unicode:unicode_binary(). get_chars(Io, Prompt, N) when is_integer(N), N >= 0 -> request(Io, {get_chars,unicode,Prompt,N}). --spec get_line(prompt()) -> iodata() | 'eof' | {'error', term()}. +-spec get_line(Prompt) -> Data | 'eof' | {'error', Reason} when + Prompt :: prompt(), + Reason :: term(), + Data :: [unicode:unicode_char()] | unicode:unicode_binary(). get_line(Prompt) -> get_line(default_input(), Prompt). --spec get_line(device(), prompt()) -> iodata() | 'eof' | {'error', term()}. +-spec get_line(IoDevice, Prompt) -> Data | 'eof' | {'error', term()} when + IoDevice :: device(), + Prompt :: prompt(), + Data :: [unicode:unicode_char()] | unicode:unicode_binary(). get_line(Io, Prompt) -> request(Io, {get_line,unicode,Prompt}). @@ -156,46 +177,62 @@ get_password(Io) -> getopts() -> getopts(default_input()). --spec getopts(device()) -> [opt_pair()]. +-spec getopts(IoDevice) -> [opt_pair()] when + IoDevice :: device(). getopts(Io) -> request(Io, getopts). -type setopt() :: 'binary' | 'list' | opt_pair(). --spec setopts([setopt()]) -> 'ok' | {'error', term()}. +-spec setopts(Opts) -> 'ok' | {'error', Reason} when + Opts :: [setopt()], + Reason :: term(). setopts(Opts) -> setopts(default_input(), Opts). --spec setopts(device(), [setopt()]) -> 'ok' | {'error', term()}. +-spec setopts(IoDevice, Opts) -> 'ok' | {'error', Reason} when + IoDevice :: device(), + Opts :: [setopt()], + Reason :: term(). setopts(Io, Opts) -> request(Io, {setopts, Opts}). %% Writing and reading Erlang terms. --spec write(term()) -> 'ok'. +-spec write(Term) -> 'ok' when + Term :: term(). write(Term) -> write(default_output(), Term). --spec write(device(), term()) -> 'ok'. +-spec write(IoDevice, Term) -> 'ok' when + IoDevice :: device(), + Term :: term(). write(Io, Term) -> o_request(Io, {write,Term}, write). --spec read(prompt()) -> - {'ok', term()} | 'eof' | {'error', erl_scan:error_info()}. +-spec read(Prompt) -> Result when + Prompt :: prompt(), + Result :: {'ok', Term :: term()} + | 'eof' + | {'error', ErrorInfo :: erl_scan:error_info()}. % Read does not use get_until as erl_scan does not work with unicode % XXX:PaN fixme? read(Prompt) -> read(default_input(), Prompt). --spec read(device(), prompt()) -> - {'ok', term()} | 'eof' | {'error', erl_scan:error_info()}. +-spec read(IoDevice, Prompt) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Result :: {'ok', Term :: term()} + | 'eof' + | {'error', ErrorInfo :: erl_scan:error_info()}. read(Io, Prompt) -> case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[1]}) of @@ -211,9 +248,13 @@ read(Io, Prompt) -> Other end. --spec read(device(), prompt(), line()) -> - {'ok', term(), line()} | {'eof', line()} | - {'error', erl_scan:error_info(), line()}. +-spec read(IoDevice, Prompt, StartLine) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLine :: line(), + Result :: {'ok', Term :: term(), EndLine :: line()} + | {'eof', EndLine :: line()} + | {'error', ErrorInfo :: erl_scan:error_info(), ErrorLine :: line()}. read(Io, Prompt, StartLine) when is_integer(StartLine) -> case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[StartLine]}) of @@ -239,28 +280,40 @@ conv_reason(_, _Reason) -> badarg. -type format() :: atom() | string() | binary(). --spec fwrite(format()) -> 'ok'. +-spec fwrite(Format) -> 'ok' when + Format :: format(). fwrite(Format) -> format(Format). --spec fwrite(format(), [term()]) -> 'ok'. +-spec fwrite(Format, Data) -> 'ok' when + Format :: format(), + Data :: [term()]. fwrite(Format, Args) -> format(Format, Args). --spec fwrite(device(), format(), [term()]) -> 'ok'. +-spec fwrite(IoDevice, Format, Data) -> 'ok' when + IoDevice :: device(), + Format :: format(), + Data :: [term()]. fwrite(Io, Format, Args) -> format(Io, Format, Args). --spec fread(prompt(), format()) -> {'ok', [term()]} | 'eof' | {'error',term()}. +-spec fread(Prompt, Format) -> Result when + Prompt :: prompt(), + Format :: format(), + Result :: {'ok', Terms :: [term()]} | 'eof' | {'error', What :: term()}. fread(Prompt, Format) -> fread(default_input(), Prompt, Format). --spec fread(device(), prompt(), format()) -> - {'ok', [term()]} | 'eof' | {'error',term()}. +-spec fread(IoDevice, Prompt, Format) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Format :: format(), + Result :: {'ok', Terms :: [term()]} | 'eof' | {'error', What :: term()}. fread(Io, Prompt, Format) -> case request(Io, {fread,Prompt,Format}) of @@ -270,73 +323,104 @@ fread(Io, Prompt, Format) -> Other end. --spec format(format()) -> 'ok'. +-spec format(Format) -> 'ok' when + Format :: format(). format(Format) -> format(Format, []). --spec format(format(), [term()]) -> 'ok'. +-spec format(Format, Data) -> 'ok' when + Format :: format(), + Data :: [term()]. format(Format, Args) -> format(default_output(), Format, Args). --spec format(device(), format(), [term()]) -> 'ok'. +-spec format(IoDevice, Format, Data) -> 'ok' when + IoDevice :: device(), + Format :: format(), + Data :: [term()]. format(Io, Format, Args) -> o_request(Io, {format,Format,Args}, format). %% Scanning Erlang code. --spec scan_erl_exprs(prompt()) -> erl_scan:tokens_result() | request_error(). +-spec scan_erl_exprs(Prompt) -> Result when + Prompt :: prompt(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_exprs(Prompt) -> scan_erl_exprs(default_input(), Prompt, 1). --spec scan_erl_exprs(device(), prompt()) -> erl_scan:tokens_result() | request_error(). +-spec scan_erl_exprs(Device, Prompt) -> Result when + Device :: device(), + Prompt :: prompt(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_exprs(Io, Prompt) -> scan_erl_exprs(Io, Prompt, 1). --spec scan_erl_exprs(device(), prompt(), line()) -> erl_scan:tokens_result() | request_error(). +-spec scan_erl_exprs(Device, Prompt, StartLine) -> Result when + Device :: device(), + Prompt :: prompt(), + StartLine :: line(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_exprs(Io, Prompt, Pos0) -> request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}). --spec scan_erl_form(prompt()) -> erl_scan:tokens_result() | request_error(). +-spec scan_erl_form(Prompt) -> Result when + Prompt :: prompt(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_form(Prompt) -> scan_erl_form(default_input(), Prompt, 1). --spec scan_erl_form(device(), prompt()) -> erl_scan:tokens_result() | request_error(). +-spec scan_erl_form(IoDevice, Prompt) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_form(Io, Prompt) -> scan_erl_form(Io, Prompt, 1). --spec scan_erl_form(device(), prompt(), line()) -> erl_scan:tokens_result() | request_error(). +-spec scan_erl_form(IoDevice, Prompt, StartLine) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLine :: line(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_form(Io, Prompt, Pos0) -> request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}). %% Parsing Erlang code. --type erl_parse_expr_list() :: [_]. %% XXX: should be imported from erl_parse - --type parse_ret() :: {'ok', erl_parse_expr_list(), line()} - | {'eof', line()} - | {'error', erl_scan:error_info(), line()} +-type parse_ret() :: {'ok', ExprList :: erl_parse:abstract_expr(), EndLine :: line()} + | {'eof', EndLine :: line()} + | {'error', ErrorInfo :: erl_scan:error_info(), ErrorLine :: line()} | request_error(). --spec parse_erl_exprs(prompt()) -> parse_ret(). +-spec parse_erl_exprs(Prompt) -> Result when + Prompt :: prompt(), + Result :: parse_ret(). parse_erl_exprs(Prompt) -> parse_erl_exprs(default_input(), Prompt, 1). --spec parse_erl_exprs(device(), prompt()) -> parse_ret(). +-spec parse_erl_exprs(IoDevice, Prompt) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Result :: parse_ret(). parse_erl_exprs(Io, Prompt) -> parse_erl_exprs(Io, Prompt, 1). --spec parse_erl_exprs(device(), prompt(), line()) -> parse_ret(). +-spec parse_erl_exprs(IoDevice, Prompt, StartLine) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLine :: line(), + Result :: parse_ret(). parse_erl_exprs(Io, Prompt, Pos0) -> case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}) of @@ -349,24 +433,31 @@ parse_erl_exprs(Io, Prompt, Pos0) -> Other end. --type erl_parse_absform() :: _. %% XXX: should be imported from erl_parse - --type parse_form_ret() :: {'ok', erl_parse_absform(), line()} - | {'eof', line()} - | {'error', erl_scan:error_info(), line()} +-type parse_form_ret() :: {'ok', AbsForm :: erl_parse:abstract_form(), EndLine :: line()} + | {'eof', EndLine :: line()} + | {'error', ErrorInfo :: erl_scan:error_info(), ErrorLine :: line()} | request_error(). --spec parse_erl_form(prompt()) -> parse_form_ret(). +-spec parse_erl_form(Prompt) -> Result when + Prompt :: prompt(), + Result :: parse_form_ret(). parse_erl_form(Prompt) -> parse_erl_form(default_input(), Prompt, 1). --spec parse_erl_form(device(), prompt()) -> parse_form_ret(). +-spec parse_erl_form(IoDevice, Prompt) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Result :: parse_form_ret(). parse_erl_form(Io, Prompt) -> parse_erl_form(Io, Prompt, 1). --spec parse_erl_form(device(), prompt(), line()) -> parse_form_ret(). +-spec parse_erl_form(IoDevice, Prompt, StartLine) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLine :: line(), + Result :: parse_form_ret(). parse_erl_form(Io, Prompt, Pos0) -> case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}) of diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl index 4ca9d079b7..0252cdf742 100644 --- a/lib/stdlib/src/io_lib.erl +++ b/lib/stdlib/src/io_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -75,35 +75,57 @@ collect_line/2, collect_line/3, collect_line/4, get_until/3, get_until/4]). --export_type([chars/0]). +-export_type([chars/0, continuation/0]). %%---------------------------------------------------------------------- - %% XXX: overapproximates a deep list of (unicode) characters --type chars() :: [_]. +-type chars() :: [char() | chars()]. -type depth() :: -1 | non_neg_integer(). +-opaque continuation() :: {_, _, _, _}. % XXX: refine + %%---------------------------------------------------------------------- %% Interface calls to sub-modules. --spec fwrite(io:format(), [term()]) -> chars(). +-spec fwrite(Format, Data) -> chars() | UnicodeList when + Format :: io:format(), + Data :: [term()], + UnicodeList :: [unicode:unicode_char()], + Data :: [term()]. fwrite(Format, Args) -> format(Format, Args). --spec fread(string(), string()) -> io_lib_fread:fread_2_ret(). +-spec fread(Format, String) -> Result when + Format :: string(), + String :: string(), + Result :: {'ok', InputList :: [term()], LeftOverChars :: string()} + | {'more', RestFormat :: string(), + Nchars :: non_neg_integer(), + InputStack :: chars()} + | {'error', What :: term()}. fread(Chars, Format) -> io_lib_fread:fread(Chars, Format). --spec fread(io_lib_fread:continuation(), string(), string()) -> - io_lib_fread:fread_3_ret(). +-spec fread(Continuation, CharSpec, Format) -> Return when + Continuation :: continuation() | [], + CharSpec :: string() | eof, + Format :: string(), + Return :: {'more', Continuation1 :: continuation()} + | {'done', Result, LeftOverChars :: string()}, + Result :: {'ok', InputList :: [term()]} + | 'eof' + | {'error', What :: term()}. fread(Cont, Chars, Format) -> io_lib_fread:fread(Cont, Chars, Format). --spec format(io:format(), [term()]) -> chars(). +-spec format(Format, Data) -> chars() | UnicodeList when + Format :: io:format(), + Data :: [term()], + UnicodeList :: [unicode:unicode_char()]. format(Format, Args) -> case catch io_lib_format:fwrite(Format, Args) of @@ -113,17 +135,24 @@ format(Format, Args) -> Other end. --spec print(term()) -> chars(). +-spec print(Term) -> chars() when + Term :: term(). print(Term) -> io_lib_pretty:print(Term). --spec print(term(), non_neg_integer(), non_neg_integer(), depth()) -> chars(). +-spec print(Term, Column, LineLength, Depth) -> chars() when + Term :: term(), + Column :: non_neg_integer(), + LineLength :: non_neg_integer(), + Depth :: depth(). print(Term, Column, LineLength, Depth) -> io_lib_pretty:print(Term, Column, LineLength, Depth). --spec indentation(string(), integer()) -> integer(). +-spec indentation(String, StartIndent) -> integer() when + String :: string(), + StartIndent :: integer(). indentation(Chars, Current) -> io_lib_format:indentation(Chars, Current). @@ -158,7 +187,8 @@ format_prompt(Format, Args) -> %% Return a (non-flattened) list of characters giving a printed %% representation of the term. write/3 is for backward compatibility. --spec write(term()) -> chars(). +-spec write(Term) -> chars() when + Term :: term(). write(Term) -> write(Term, -1). @@ -169,7 +199,9 @@ write(Term, D, true) -> write(Term, D, false) -> write(Term, D). --spec write(term(), depth()) -> chars(). +-spec write(Term, Depth) -> chars() when + Term :: term(), + Depth :: depth(). write(_Term, 0) -> "..."; write(Term, _D) when is_integer(Term) -> integer_to_list(Term); @@ -234,7 +266,8 @@ write_binary_body(B, _D) -> %% write_atom(Atom) -> [Char] %% Generate the list of characters needed to print an atom. --spec write_atom(atom()) -> chars(). +-spec write_atom(Atom) -> chars() when + Atom :: atom(). write_atom(Atom) -> Chars = atom_to_list(Atom), @@ -283,7 +316,8 @@ name_char(_) -> false. %% write_string([Char]) -> [Char] %% Generate the list of characters needed to print a string. --spec write_string(string()) -> chars(). +-spec write_string(String) -> chars() when + String :: string(). write_string(S) -> write_string(S, $"). %" @@ -330,7 +364,8 @@ string_char(_,C, _, Tail) when C < $\240-> %Other control characters. %% Generate the list of characters needed to print a character constant. %% Must special case SPACE, $\s, here. --spec write_char(char()) -> chars(). +-spec write_char(Char) -> chars() when + Char :: char(). write_char($\s) -> "$\\s"; %Must special case this. write_char(C) when is_integer(C), C >= $\000, C =< $\377 -> @@ -346,7 +381,8 @@ write_unicode_char(Uni) -> %% Return true if CharList is a (possibly deep) list of characters, else %% false. --spec char_list(term()) -> boolean(). +-spec char_list(Term) -> boolean() when + Term :: term(). char_list([C|Cs]) when is_integer(C), C >= $\000, C =< $\377 -> char_list(Cs); @@ -362,7 +398,8 @@ unicode_char_list([C|Cs]) when is_integer(C), C >= 0, C < 16#D800; unicode_char_list([]) -> true; unicode_char_list(_) -> false. %Everything else is false --spec deep_char_list(term()) -> boolean(). +-spec deep_char_list(Term) -> boolean() when + Term :: term(). deep_char_list(Cs) -> deep_char_list(Cs, []). @@ -399,7 +436,8 @@ deep_unicode_char_list(_, _More) -> %Everything else is false %% Return true if CharList is a list of printable characters, else %% false. --spec printable_list(term()) -> boolean(). +-spec printable_list(Term) -> boolean() when + Term :: term(). printable_list([C|Cs]) when is_integer(C), C >= $\040, C =< $\176 -> printable_list(Cs); diff --git a/lib/stdlib/src/io_lib_fread.erl b/lib/stdlib/src/io_lib_fread.erl index 33553692bc..ded1346097 100644 --- a/lib/stdlib/src/io_lib_fread.erl +++ b/lib/stdlib/src/io_lib_fread.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -22,27 +22,11 @@ -export([fread/2,fread/3]). --export_type([continuation/0, fread_2_ret/0, fread_3_ret/0]). - -import(lists, [reverse/1,reverse/2]). -%%----------------------------------------------------------------------- -%% Local types -%%----------------------------------------------------------------------- - --type done_arg2() :: {'ok', io_lib:chars()} | 'eof' | {'error', term()}. - -%%----------------------------------------------------------------------- -%% Types also used in other files -%%----------------------------------------------------------------------- - --type continuation() :: [] | {_, _, _, _}. % XXX: refine - --type fread_2_ret() :: {'ok', io_lib:chars(), string()} - | {'more', string(), non_neg_integer(), io_lib:chars()} - | {'error', term()}. --type fread_3_ret() :: {'more', continuation()} - | {'done', done_arg2(), string()}. +-define(is_whitespace(C), + ((C) =:= $\s orelse (C) =:= $\t + orelse (C) =:= $\r orelse (C) =:= $\n)). %%----------------------------------------------------------------------- @@ -51,7 +35,15 @@ %% repeatedly collects lines and calls fread/2 to format the input until %% all the format string has been used. And it counts the characters. --spec fread(io_lib_fread:continuation(), string(), string()) -> fread_3_ret(). +-spec fread(Continuation, String, Format) -> Return when + Continuation :: io_lib:continuation() | [], + String :: string(), + Format :: string(), + Return :: {'more', Continuation1 :: io_lib:continuation()} + | {'done', Result, LeftOverChars :: string()}, + Result :: {'ok', InputList :: io_lib:chars()} + | 'eof' + | {'error', What :: term()}. fread([], Chars, Format) -> %%io:format("FREAD: ~w `~s'~n", [Format,Chars]), @@ -106,36 +98,39 @@ fread_line(Format0, Line, N0, Results0, More, Newline) -> %% WHITE Skip white space %% X Literal X --spec fread(string(), string()) -> fread_2_ret(). +-spec fread(Format, String) -> Result when + Format :: string(), + String :: string(), + Result :: {'ok', InputList :: io_lib:chars(), LeftOverChars :: string()} + | {'more', RestFormat :: string(), + Nchars :: non_neg_integer(), + InputStack :: io_lib:chars()} + | {'error', What :: term()}. fread(Format, Line) -> fread(Format, Line, 0, []). -fread([$~|Format0], Line, N, Results) -> +fread([$~|Format0]=AllFormat, Line, N, Results) -> {Format,F,Sup,Unicode} = fread_field(Format0), - fread1(Format, F, Sup, Unicode, Line, N, Results, Format0); -fread([$\s|Format], Line, N, Results) -> - fread_skip_white(Format, Line, N, Results); -fread([$\t|Format], Line, N, Results) -> - fread_skip_white(Format, Line, N, Results); -fread([$\r|Format], Line, N, Results) -> - fread_skip_white(Format, Line, N, Results); -fread([$\n|Format], Line, N, Results) -> + fread1(Format, F, Sup, Unicode, Line, N, Results, AllFormat); +fread([C|Format], Line, N, Results) when ?is_whitespace(C) -> fread_skip_white(Format, Line, N, Results); fread([C|Format], [C|Line], N, Results) -> fread(Format, Line, N+1, Results); fread([_F|_Format], [_C|_Line], _N, _Results) -> fread_error(input); +fread([_|_]=Format, [], N, Results) -> + {more,Format,N,Results}; +fread([_|_], eof, 0, []) -> + %% This is at start of input so no error. + eof; +fread([_|_], eof, _N, _Results) -> + %% This is an error as there is no more input. + fread_error(input); fread([], Line, _N, Results) -> {ok,reverse(Results),Line}. -fread_skip_white(Format, [$\s|Line], N, Results) -> - fread_skip_white(Format, Line, N+1, Results); -fread_skip_white(Format, [$\t|Line], N, Results) -> - fread_skip_white(Format, Line, N+1, Results); -fread_skip_white(Format, [$\r|Line], N, Results) -> - fread_skip_white(Format, Line, N+1, Results); -fread_skip_white(Format, [$\n|Line], N, Results) -> +fread_skip_white(Format, [C|Line], N, Results) when ?is_whitespace(C) -> fread_skip_white(Format, Line, N+1, Results); fread_skip_white(Format, Line, N, Results) -> fread(Format, Line, N, Results). @@ -171,9 +166,9 @@ fread1([$l|Format], _F, Sup, _U, Line, N, Res, _AllFormat) -> fread(Format, Line, N, fread_result(Sup, N, Res)); fread1(_Format, _F, _Sup, _U, [], N, Res, AllFormat) -> %% Need more input here. - {more,[$~|AllFormat],N,Res}; -fread1(_Format, _F, _Sup, _U, eof, _N, [], _AllFormat) -> - %% This is at start of format string so no error. + {more,AllFormat,N,Res}; +fread1(_Format, _F, _Sup, _U, eof, 0, [], _AllFormat) -> + %% This is at start of input so no error. eof; fread1(_Format, _F, _Sup, _U, eof, _N, _Res, _AllFormat) -> %% This is an error as there is no more input. @@ -391,26 +386,16 @@ fread_string_cs(Line0, N0, true) -> %% fread_digits(Line, N, Base, Characters) %% Read segments of things, return "thing" characters in reverse order. -fread_skip_white([$\s|Line]) -> fread_skip_white(Line); -fread_skip_white([$\t|Line]) -> fread_skip_white(Line); -fread_skip_white([$\r|Line]) -> fread_skip_white(Line); -fread_skip_white([$\n|Line]) -> fread_skip_white(Line); +fread_skip_white([C|Line]) when ?is_whitespace(C) -> + fread_skip_white(Line); fread_skip_white(Line) -> Line. -fread_skip_white([$\s|Line], N) -> - fread_skip_white(Line, N+1); -fread_skip_white([$\t|Line], N) -> - fread_skip_white(Line, N+1); -fread_skip_white([$\r|Line], N) -> - fread_skip_white(Line, N+1); -fread_skip_white([$\n|Line], N) -> +fread_skip_white([C|Line], N) when ?is_whitespace(C) -> fread_skip_white(Line, N+1); fread_skip_white(Line, N) -> {Line,N}. -fread_skip_latin1_nonwhite([$\s|Line], N, Cs) -> {[$\s|Line],N,Cs}; -fread_skip_latin1_nonwhite([$\t|Line], N, Cs) -> {[$\t|Line],N,Cs}; -fread_skip_latin1_nonwhite([$\r|Line], N, Cs) -> {[$\r|Line],N,Cs}; -fread_skip_latin1_nonwhite([$\n|Line], N, Cs) -> {[$\n|Line],N,Cs}; +fread_skip_latin1_nonwhite([C|Line], N, Cs) when ?is_whitespace(C) -> + {[C|Line],N,Cs}; fread_skip_latin1_nonwhite([C|Line], N, []) when C > 255 -> {[C|Line],N,error}; fread_skip_latin1_nonwhite([C|Line], N, Cs) when C > 255 -> @@ -419,10 +404,8 @@ fread_skip_latin1_nonwhite([C|Line], N, Cs) -> fread_skip_latin1_nonwhite(Line, N+1, [C|Cs]); fread_skip_latin1_nonwhite([], N, Cs) -> {[],N,Cs}. -fread_skip_nonwhite([$\s|Line], N, Cs) -> {[$\s|Line],N,Cs}; -fread_skip_nonwhite([$\t|Line], N, Cs) -> {[$\t|Line],N,Cs}; -fread_skip_nonwhite([$\r|Line], N, Cs) -> {[$\r|Line],N,Cs}; -fread_skip_nonwhite([$\n|Line], N, Cs) -> {[$\n|Line],N,Cs}; +fread_skip_nonwhite([C|Line], N, Cs) when ?is_whitespace(C) -> + {[C|Line],N,Cs}; fread_skip_nonwhite([C|Line], N, Cs) -> fread_skip_nonwhite(Line, N+1, [C|Cs]); fread_skip_nonwhite([], N, Cs) -> {[],N,Cs}. diff --git a/lib/stdlib/src/lib.erl b/lib/stdlib/src/lib.erl index b2cfb00de9..314fd60903 100644 --- a/lib/stdlib/src/lib.erl +++ b/lib/stdlib/src/lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -38,7 +38,9 @@ flush_receive() -> %% %% Functions for doing standard system format i/o. %% --spec error_message(atom() | string() | binary(), [term()]) -> 'ok'. +-spec error_message(Format, Args) -> 'ok' when + Format :: io:format(), + Args :: [term()]. error_message(Format, Args) -> io:format(<<"** ~s **\n">>, [io_lib:format(Format, Args)]). @@ -55,17 +57,23 @@ progname() -> no_prog_name end. --spec nonl(string()) -> string(). +-spec nonl(String1) -> String2 when + String1 :: string(), + String2 :: string(). nonl([10]) -> []; nonl([]) -> []; nonl([H|T]) -> [H|nonl(T)]. --spec send(pid() | atom() | {atom(), node()}, term()) -> term(). +-spec send(To, Msg) -> Msg when + To :: pid() | atom() | {atom(), node()}, + Msg :: term(). send(To, Msg) -> To ! Msg. --spec sendw(pid() | atom() | {atom(), node()}, term()) -> term(). +-spec sendw(To, Msg) -> Msg when + To :: pid() | atom() | {atom(), node()}, + Msg :: term(). sendw(To, Msg) -> To ! {self(), Msg}, @@ -89,7 +97,7 @@ eval_str(Str) when is_list(Str) -> true -> case erl_parse:parse_exprs(Toks) of {ok, Exprs} -> - case catch erl_eval:exprs(Exprs, []) of + case catch erl_eval:exprs(Exprs, erl_eval:new_bindings()) of {value, Val, _} -> {ok, Val}; Other -> @@ -165,12 +173,12 @@ format_fun(Fun) when is_function(Fun) -> analyze_exception(error, Term, Stack) -> case {is_stacktrace(Stack), Stack, Term} of - {true, [{_M,_F,As}=MFA|MFAs], function_clause} when is_list(As) -> - {Term,[MFA],MFAs}; - {true, [{shell,F,A}], function_clause} when is_integer(A) -> + {true, [{_,_,As,_}=MFAL|MFAs], function_clause} when is_list(As) -> + {Term,[MFAL],MFAs}; + {true, [{shell,F,A,_}], function_clause} when is_integer(A) -> {Term, [{F,A}], []}; - {true, [{_M,_F,_AorAs}=MFA|MFAs], undef} -> - {Term,[MFA],MFAs}; + {true, [{_,_,_,_}=MFAL|MFAs], undef} -> + {Term,[MFAL],MFAs}; {true, _, _} -> {Term,[],Stack}; {false, _, _} -> @@ -186,9 +194,11 @@ analyze_exception(_Class, Term, Stack) -> is_stacktrace([]) -> true; -is_stacktrace([{M,F,A}|Fs]) when is_atom(M), is_atom(F), is_integer(A) -> +is_stacktrace([{M,F,A,I}|Fs]) + when is_atom(M), is_atom(F), is_integer(A), is_list(I) -> is_stacktrace(Fs); -is_stacktrace([{M,F,As}|Fs]) when is_atom(M), is_atom(F), length(As) >= 0 -> +is_stacktrace([{M,F,As,I}|Fs]) + when is_atom(M), is_atom(F), length(As) >= 0, is_list(I) -> is_stacktrace(Fs); is_stacktrace(_) -> false. @@ -217,9 +227,9 @@ explain_reason(function_clause, error, [{F,A}], _PF, _S) -> %% Shell commands FAs = io_lib:fwrite(<<"~w/~w">>, [F, A]), [<<"no function clause matching call to ">> | FAs]; -explain_reason(function_clause, error=Cl, [{M,F,As}], PF, S) -> +explain_reason(function_clause, error=Cl, [{M,F,As,Loc}], PF, S) -> Str = <<"no function clause matching ">>, - format_errstr_call(Str, Cl, {M,F}, As, PF, S); + [format_errstr_call(Str, Cl, {M,F}, As, PF, S),$\s|location(Loc)]; explain_reason(if_clause, error, [], _PF, _S) -> <<"no true branch found when evaluating an if expression">>; explain_reason(noproc, error, [], _PF, _S) -> @@ -234,11 +244,11 @@ explain_reason({try_clause,V}, error=Cl, [], PF, S) -> %% "there is no try clause with a true guard sequence and a %% pattern matching..." format_value(V, <<"no try clause matching ">>, Cl, PF, S); -explain_reason(undef, error, [{M,F,A}], _PF, _S) -> +explain_reason(undef, error, [{M,F,A,_}], _PF, _S) -> %% Only the arity is displayed, not the arguments, if there are any. io_lib:fwrite(<<"undefined function ~s">>, [mfa_to_string(M, F, n_args(A))]); -explain_reason({shell_undef,F,A}, error, [], _PF, _S) -> +explain_reason({shell_undef,F,A,_}, error, [], _PF, _S) -> %% Give nicer reports for undefined shell functions %% (but not when the user actively calls shell_default:F(...)). io_lib:fwrite(<<"undefined shell command ~s/~w">>, [F, n_args(A)]); @@ -284,17 +294,19 @@ argss(I) -> io_lib:fwrite(<<"~w arguments">>, [I]). format_stacktrace1(S0, Stack0, PF, SF) -> - Stack1 = lists:dropwhile(fun({M,F,A}) -> SF(M, F, A) + Stack1 = lists:dropwhile(fun({M,F,A,_}) -> SF(M, F, A) end, lists:reverse(Stack0)), S = [" " | S0], Stack = lists:reverse(Stack1), format_stacktrace2(S, Stack, 1, PF). -format_stacktrace2(S, [{M,F,A}|Fs], N, PF) when is_integer(A) -> - [io_lib:fwrite(<<"~s~s ~s">>, - [sep(N, S), origin(N, M, F, A), mfa_to_string(M, F, A)]) +format_stacktrace2(S, [{M,F,A,L}|Fs], N, PF) when is_integer(A) -> + [io_lib:fwrite(<<"~s~s ~s ~s">>, + [sep(N, S), origin(N, M, F, A), + mfa_to_string(M, F, A), + location(L)]) | format_stacktrace2(S, Fs, N + 1, PF)]; -format_stacktrace2(S, [{M,F,As}|Fs], N, PF) when is_list(As) -> +format_stacktrace2(S, [{M,F,As,_}|Fs], N, PF) when is_list(As) -> A = length(As), CalledAs = [S,<<" called as ">>], C = format_call("", CalledAs, {M,F}, As, PF), @@ -305,6 +317,16 @@ format_stacktrace2(S, [{M,F,As}|Fs], N, PF) when is_list(As) -> format_stacktrace2(_S, [], _N, _PF) -> "". +location(L) -> + File = proplists:get_value(file, L), + Line = proplists:get_value(line, L), + if + File =/= undefined, Line =/= undefined -> + io_lib:format("(~s, line ~w)", [File, Line]); + true -> + "" + end. + sep(1, S) -> S; sep(_, S) -> [$\n | S]. diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl index c669c1f7c1..e73c087753 100644 --- a/lib/stdlib/src/lists.erl +++ b/lib/stdlib/src/lists.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -54,13 +54,21 @@ %% append(X, Y) appends lists X and Y --spec append([T], [T]) -> [T]. +-spec append(List1, List2) -> List3 when + List1 :: [T], + List2 :: [T], + List3 :: [T], + T :: term(). append(L1, L2) -> L1 ++ L2. %% append(L) appends the list of lists L --spec append([[T]]) -> [T]. +-spec append(ListOfLists) -> List1 when + ListOfLists :: [List], + List :: [T], + List1 :: [T], + T :: term(). append([E]) -> E; append([H|T]) -> H ++ append(T); @@ -68,13 +76,20 @@ append([]) -> []. %% subtract(List1, List2) subtract elements in List2 form List1. --spec subtract([T], [T]) -> [T]. +-spec subtract(List1, List2) -> List3 when + List1 :: [T], + List2 :: [T], + List3 :: [T], + T :: term(). subtract(L1, L2) -> L1 -- L2. %% reverse(L) reverse all elements in the list L. Is now a BIF! --spec reverse([T]) -> [T]. +-spec reverse(List1) -> List2 when + List1 :: [T], + List2 :: [T], + T :: term(). reverse([] = L) -> L; @@ -93,13 +108,21 @@ reverse([A, B | L]) -> %% nth(N, L) returns the N`th element of the list L %% nthtail(N, L) returns the N`th tail of the list L --spec nth(pos_integer(), [T,...]) -> T. +-spec nth(N, List) -> Elem when + N :: pos_integer(), + List :: [T,...], + Elem :: T, + T :: term(). nth(1, [H|_]) -> H; nth(N, [_|T]) when N > 1 -> nth(N - 1, T). --spec nthtail(non_neg_integer(), [T,...]) -> [T]. +-spec nthtail(N, List) -> Tail when + N :: non_neg_integer(), + List :: [T,...], + Tail :: [T], + T :: term(). nthtail(1, [_|T]) -> T; nthtail(N, [_|T]) when N > 1 -> @@ -108,7 +131,10 @@ nthtail(0, L) when is_list(L) -> L. %% prefix(Prefix, List) -> (true | false) --spec prefix([T], [T]) -> boolean(). +-spec prefix(List1, List2) -> boolean() when + List1 :: [T], + List2 :: [T], + T :: term(). prefix([X|PreTail], [X|Tail]) -> prefix(PreTail, Tail); @@ -117,7 +143,10 @@ prefix([_|_], List) when is_list(List) -> false. %% suffix(Suffix, List) -> (true | false) --spec suffix([T], [T]) -> boolean(). +-spec suffix(List1, List2) -> boolean() when + List1 :: [T], + List2 :: [T], + T :: term(). suffix(Suffix, List) -> Delta = length(List) - length(Suffix), @@ -125,7 +154,10 @@ suffix(Suffix, List) -> %% last(List) returns the last element in a list. --spec last([T,...]) -> T. +-spec last(List) -> Last when + List :: [T,...], + Last :: T, + T :: term(). last([E|Es]) -> last(E, Es). @@ -137,7 +169,10 @@ last(E, []) -> E. %% returns the sequence Min..Max %% Min <= Max and Min and Max must be integers --spec seq(integer(), integer()) -> [integer()]. +-spec seq(From, To) -> Seq when + From :: integer(), + To :: integer(), + Seq :: [integer()]. seq(First, Last) when is_integer(First), is_integer(Last), First-1 =< Last -> @@ -152,7 +187,11 @@ seq_loop(1, X, L) -> seq_loop(0, _, L) -> L. --spec seq(integer(), integer(), integer()) -> [integer()]. +-spec seq(From, To, Incr) -> Seq when + From :: integer(), + To :: integer(), + Incr :: integer(), + Seq :: [integer()]. seq(First, Last, Inc) when is_integer(First), is_integer(Last), is_integer(Inc) -> @@ -178,7 +217,8 @@ seq_loop(0, _, _, L) -> %% sum(L) returns the sum of the elements in L --spec sum([number()]) -> number(). +-spec sum(List) -> number() when + List :: [number()]. sum(L) -> sum(L, 0). @@ -188,7 +228,11 @@ sum([], Sum) -> Sum. %% duplicate(N, X) -> [X,X,X,.....,X] (N times) %% return N copies of X --spec duplicate(non_neg_integer(), T) -> [T]. +-spec duplicate(N, Elem) -> List when + N :: non_neg_integer(), + Elem :: T, + List :: [T], + T :: term(). duplicate(N, X) when is_integer(N), N >= 0 -> duplicate(N, X, []). @@ -197,7 +241,10 @@ duplicate(N, X, L) -> duplicate(N-1, X, [X|L]). %% min(L) -> returns the minimum element of the list L --spec min([T,...]) -> T. +-spec min(List) -> Min when + List :: [T,...], + Min :: T, + T :: term(). min([H|T]) -> min(T, H). @@ -207,7 +254,10 @@ min([], Min) -> Min. %% max(L) -> returns the maximum element of the list L --spec max([T,...]) -> T. +-spec max(List) -> Max when + List :: [T,...], + Max :: T, + T :: term(). max([H|T]) -> max(T, H). @@ -218,12 +268,21 @@ max([], Max) -> Max. %% sublist(List, Start, Length) %% Returns the sub-list starting at Start of length Length. --spec sublist([T], pos_integer(), non_neg_integer()) -> [T]. +-spec sublist(List1, Start, Len) -> List2 when + List1 :: [T], + List2 :: [T], + Start :: pos_integer(), + Len :: non_neg_integer(), + T :: term(). sublist(List, S, L) when is_integer(L), L >= 0 -> sublist(nthtail(S-1, List), L). --spec sublist([T], non_neg_integer()) -> [T]. +-spec sublist(List1, Len) -> List2 when + List1 :: [T], + List2 :: [T], + Len :: non_neg_integer(), + T :: term(). sublist(List, L) when is_integer(L), is_list(List) -> sublist_2(List, L). @@ -238,7 +297,11 @@ sublist_2(List, L) when is_list(List), L > 0 -> %% delete(Item, List) -> List' %% Delete the first occurrence of Item from the list L. --spec delete(T, [T]) -> [T]. +-spec delete(Elem, List1) -> List2 when + Elem :: T, + List1 :: [T], + List2 :: [T], + T :: term(). delete(Item, [Item|Rest]) -> Rest; delete(Item, [H|Rest]) -> @@ -248,7 +311,12 @@ delete(_, []) -> []. %% Return [{X0, Y0}, {X1, Y1}, ..., {Xn, Yn}] for lists [X0, X1, ..., %% Xn] and [Y0, Y1, ..., Yn]. --spec zip([A], [B]) -> [{A, B}]. +-spec zip(List1, List2) -> List3 when + List1 :: [A], + List2 :: [B], + List3 :: [{A, B}], + A :: term(), + B :: term(). zip([X | Xs], [Y | Ys]) -> [{X, Y} | zip(Xs, Ys)]; zip([], []) -> []. @@ -256,7 +324,12 @@ zip([], []) -> []. %% Return {[X0, X1, ..., Xn], [Y0, Y1, ..., Yn]}, for a list [{X0, Y0}, %% {X1, Y1}, ..., {Xn, Yn}]. --spec unzip([{A, B}]) -> {[A], [B]}. +-spec unzip(List1) -> {List2, List3} when + List1 :: [{A, B}], + List2 :: [A], + List3 :: [B], + A :: term(), + B :: term(). unzip(Ts) -> unzip(Ts, [], []). @@ -266,7 +339,14 @@ unzip([], Xs, Ys) -> {reverse(Xs), reverse(Ys)}. %% Return [{X0, Y0, Z0}, {X1, Y1, Z1}, ..., {Xn, Yn, Zn}] for lists [X0, %% X1, ..., Xn], [Y0, Y1, ..., Yn] and [Z0, Z1, ..., Zn]. --spec zip3([A], [B], [C]) -> [{A, B, C}]. +-spec zip3(List1, List2, List3) -> List4 when + List1 :: [A], + List2 :: [B], + List3 :: [C], + List4 :: [{A, B, C}], + A :: term(), + B :: term(), + C :: term(). zip3([X | Xs], [Y | Ys], [Z | Zs]) -> [{X, Y, Z} | zip3(Xs, Ys, Zs)]; zip3([], [], []) -> []. @@ -274,7 +354,14 @@ zip3([], [], []) -> []. %% Return {[X0, X1, ..., Xn], [Y0, Y1, ..., Yn], [Z0, Z1, ..., Zn]}, for %% a list [{X0, Y0, Z0}, {X1, Y1, Z1}, ..., {Xn, Yn, Zn}]. --spec unzip3([{A, B, C}]) -> {[A], [B], [C]}. +-spec unzip3(List1) -> {List2, List3, List4} when + List1 :: [{A, B, C}], + List2 :: [A], + List3 :: [B], + List4 :: [C], + A :: term(), + B :: term(), + C :: term(). unzip3(Ts) -> unzip3(Ts, [], [], []). @@ -286,7 +373,14 @@ unzip3([], Xs, Ys, Zs) -> %% Return [F(X0, Y0), F(X1, Y1), ..., F(Xn, Yn)] for lists [X0, X1, ..., %% Xn] and [Y0, Y1, ..., Yn]. --spec zipwith(fun((X, Y) -> R), [X], [Y]) -> [R]. +-spec zipwith(Combine, List1, List2) -> List3 when + Combine :: fun((X, Y) -> T), + List1 :: [X], + List2 :: [Y], + List3 :: [T], + X :: term(), + Y :: term(), + T :: term(). zipwith(F, [X | Xs], [Y | Ys]) -> [F(X, Y) | zipwith(F, Xs, Ys)]; zipwith(F, [], []) when is_function(F, 2) -> []. @@ -294,7 +388,16 @@ zipwith(F, [], []) when is_function(F, 2) -> []. %% Return [F(X0, Y0, Z0), F(X1, Y1, Z1), ..., F(Xn, Yn, Zn)] for lists %% [X0, X1, ..., Xn], [Y0, Y1, ..., Yn] and [Z0, Z1, ..., Zn]. --spec zipwith3(fun((X, Y, Z) -> R), [X], [Y], [Z]) -> [R]. +-spec zipwith3(Combine, List1, List2, List3) -> List4 when + Combine :: fun((X, Y, Z) -> T), + List1 :: [X], + List2 :: [Y], + List3 :: [Z], + List4 :: [T], + X :: term(), + Y :: term(), + Z :: term(), + T :: term(). zipwith3(F, [X | Xs], [Y | Ys], [Z | Zs]) -> [F(X, Y, Z) | zipwith3(F, Xs, Ys, Zs)]; @@ -303,7 +406,10 @@ zipwith3(F, [], [], []) when is_function(F, 3) -> []. %% sort(List) -> L %% sorts the list L --spec sort([T]) -> [T]. +-spec sort(List1) -> List2 when + List1 :: [T], + List2 :: [T], + T :: term(). sort([X, Y | L] = L0) when X =< Y -> case L of @@ -350,7 +456,11 @@ sort_1(X, [], R) -> %% merge(List) -> L %% merges a list of sorted lists --spec merge([[T]]) -> [T]. +-spec merge(ListOfLists) -> List1 when + ListOfLists :: [List], + List :: [T], + List1 :: [T], + T :: term(). merge(L) -> mergel(L, []). @@ -358,7 +468,14 @@ merge(L) -> %% merge3(X, Y, Z) -> L %% merges three sorted lists X, Y and Z --spec merge3([X], [Y], [Z]) -> [(X | Y | Z)]. +-spec merge3(List1, List2, List3) -> List4 when + List1 :: [X], + List2 :: [Y], + List3 :: [Z], + List4 :: [(X | Y | Z)], + X :: term(), + Y :: term(), + Z :: term(). merge3(L1, [], L3) -> merge(L1, L3); @@ -382,7 +499,12 @@ rmerge3(L1, [H2 | T2], [H3 | T3]) -> %% merge(X, Y) -> L %% merges two sorted lists X and Y --spec merge([X], [Y]) -> [(X | Y)]. +-spec merge(List1, List2) -> List3 when + List1 :: [X], + List2 :: [Y], + List3 :: [(X | Y)], + X :: term(), + Y :: term(). merge(T1, []) -> T1; @@ -405,8 +527,9 @@ rmerge(T1, [H2 | T2]) -> %% in L - the elements in L can be atoms, numbers of strings. %% Returns a list of characters. --type concat_thing() :: atom() | integer() | float() | string(). --spec concat([concat_thing()]) -> string(). +-spec concat(Things) -> string() when + Things :: [Thing], + Thing :: atom() | integer() | float() | string(). concat(List) -> flatmap(fun thing_to_list/1, List). @@ -420,12 +543,17 @@ thing_to_list(X) when is_list(X) -> X. %Assumed to be a string %% flatten(List, Tail) %% Flatten a list, adding optional tail. --spec flatten([term()]) -> [term()]. +-spec flatten(DeepList) -> List when + DeepList :: [term() | DeepList], + List :: [term()]. flatten(List) when is_list(List) -> do_flatten(List, []). --spec flatten([term()], [term()]) -> [term()]. +-spec flatten(DeepList, Tail) -> List when + DeepList :: [term() | DeepList], + Tail :: [term()], + List :: [term()]. flatten(List, Tail) when is_list(List), is_list(Tail) -> do_flatten(List, Tail). @@ -440,7 +568,8 @@ do_flatten([], Tail) -> %% flatlength(List) %% Calculate the length of a list of lists. --spec flatlength([term()]) -> non_neg_integer(). +-spec flatlength(DeepList) -> non_neg_integer() when + DeepList :: [term() | DeepList]. flatlength(List) -> flatlength(List, 0). @@ -481,7 +610,12 @@ flatlength([], L) -> L. % keysearch3(Key, N, T); %keysearch3(Key, N, []) -> false. --spec keydelete(term(), pos_integer(), [T]) -> [T] when T :: tuple(). +-spec keydelete(Key, N, TupleList1) -> TupleList2 when + Key :: term(), + N :: pos_integer(), + TupleList1 :: [Tuple], + TupleList2 :: [Tuple], + Tuple :: tuple(). keydelete(K, N, L) when is_integer(N), N > 0 -> keydelete3(K, N, L). @@ -491,7 +625,13 @@ keydelete3(Key, N, [H|T]) -> [H|keydelete3(Key, N, T)]; keydelete3(_, _, []) -> []. --spec keyreplace(term(), pos_integer(), [tuple()], tuple()) -> [tuple()]. +-spec keyreplace(Key, N, TupleList1, NewTuple) -> TupleList2 when + Key :: term(), + N :: pos_integer(), + TupleList1 :: [Tuple], + TupleList2 :: [Tuple], + NewTuple :: Tuple, + Tuple :: tuple(). keyreplace(K, N, L, New) when is_integer(N), N > 0, is_tuple(New) -> keyreplace3(K, N, L, New). @@ -502,8 +642,12 @@ keyreplace3(Key, Pos, [H|T], New) -> [H|keyreplace3(Key, Pos, T, New)]; keyreplace3(_, _, [], _) -> []. --spec keytake(term(), pos_integer(), [tuple()]) -> - {'value', tuple(), [tuple()]} | 'false'. +-spec keytake(Key, N, TupleList1) -> {value, Tuple, TupleList2} | false when + Key :: term(), + N :: pos_integer(), + TupleList1 :: [tuple()], + TupleList2 :: [tuple()], + Tuple :: tuple(). keytake(Key, N, L) when is_integer(N), N > 0 -> keytake(Key, N, L, []). @@ -514,7 +658,13 @@ keytake(Key, N, [H|T], L) -> keytake(Key, N, T, [H|L]); keytake(_K, _N, [], _L) -> false. --spec keystore(term(), pos_integer(), [tuple()], tuple()) -> [tuple(),...]. +-spec keystore(Key, N, TupleList1, NewTuple) -> TupleList2 when + Key :: term(), + N :: pos_integer(), + TupleList1 :: [Tuple], + TupleList2 :: [Tuple, ...], + NewTuple :: Tuple, + Tuple :: tuple(). keystore(K, N, L, New) when is_integer(N), N > 0, is_tuple(New) -> keystore2(K, N, L, New). @@ -526,7 +676,11 @@ keystore2(Key, N, [H|T], New) -> keystore2(_Key, _N, [], New) -> [New]. --spec keysort(pos_integer(), [T]) -> [T] when T :: tuple(). +-spec keysort(N, TupleList1) -> TupleList2 when + N :: pos_integer(), + TupleList1 :: [Tuple], + TupleList2 :: [Tuple], + Tuple :: tuple(). keysort(I, L) when is_integer(I), I > 0 -> case L of @@ -583,8 +737,14 @@ keysort_1(I, X, EX, [Y | L], R) -> keysort_1(_I, X, _EX, [], R) -> lists:reverse(R, [X]). --spec keymerge(pos_integer(), [X], [Y]) -> - [R] when X :: tuple(), Y :: tuple(), R :: tuple(). +-spec keymerge(N, TupleList1, TupleList2) -> TupleList3 when + N :: pos_integer(), + TupleList1 :: [T1], + TupleList2 :: [T2], + TupleList3 :: [(T1 | T2)], + T1 :: Tuple, + T2 :: Tuple, + Tuple :: tuple(). keymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> case L2 of @@ -611,7 +771,11 @@ rkeymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> lists:reverse(M, []) end. --spec ukeysort(pos_integer(), [T]) -> [T] when T :: tuple(). +-spec ukeysort(N, TupleList1) -> TupleList2 when + N :: pos_integer(), + TupleList1 :: [Tuple], + TupleList2 :: [Tuple], + Tuple :: tuple(). ukeysort(I, L) when is_integer(I), I > 0 -> case L of @@ -676,8 +840,14 @@ ukeysort_1(I, X, EX, [Y | L]) -> ukeysort_1(_I, X, _EX, []) -> [X]. --spec ukeymerge(pos_integer(), [X], [Y]) -> - [(X | Y)] when X :: tuple(), Y :: tuple(). +-spec ukeymerge(N, TupleList1, TupleList2) -> TupleList3 when + N :: pos_integer(), + TupleList1 :: [T1], + TupleList2 :: [T2], + TupleList3 :: [(T1 | T2)], + T1 :: Tuple, + T2 :: Tuple, + Tuple :: tuple(). ukeymerge(Index, L1, T2) when is_integer(Index), Index > 0 -> case L1 of @@ -704,7 +874,12 @@ rukeymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> lists:reverse(M, []) end. --spec keymap(fun((term()) -> term()), pos_integer(), [tuple()]) -> [tuple()]. +-spec keymap(Fun, N, TupleList1) -> TupleList2 when + Fun :: fun((Term1 :: term()) -> Term2 :: term()), + N :: pos_integer(), + TupleList1 :: [Tuple], + TupleList2 :: [Tuple], + Tuple :: tuple(). keymap(Fun, Index, [Tup|Tail]) -> [setelement(Index, Tup, Fun(element(Index, Tup)))|keymap(Fun, Index, Tail)]; @@ -713,7 +888,11 @@ keymap(Fun, Index, []) when is_integer(Index), Index >= 1, %%% Suggestion from OTP-2948: sort and merge with Fun. --spec sort(fun((T, T) -> boolean()), [T]) -> [T]. +-spec sort(Fun, List1) -> List2 when + Fun :: fun((A :: T, B :: T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). sort(Fun, []) when is_function(Fun, 2) -> []; @@ -727,7 +906,13 @@ sort(Fun, [X, Y | T]) -> fsplit_2(Y, X, Fun, T, [], []) end. --spec merge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)]. +-spec merge(Fun, List1, List2) -> List3 when + Fun :: fun((A, B) -> boolean()), + List1 :: [A], + List2 :: [B], + List3 :: [(A | B)], + A :: term(), + B :: term(). merge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) -> lists:reverse(fmerge2_1(T1, H2, Fun, T2, []), []); @@ -743,7 +928,11 @@ rmerge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) -> rmerge(Fun, T1, []) when is_function(Fun, 2) -> T1. --spec usort(fun((T, T) -> boolean()), [T]) -> [T]. +-spec usort(Fun, List1) -> List2 when + Fun :: fun((T, T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). usort(Fun, [_] = L) when is_function(Fun, 2) -> L; @@ -770,7 +959,13 @@ usort_1(Fun, X, [Y | L]) -> ufsplit_2(Y, L, Fun, [X]) end. --spec umerge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)]. +-spec umerge(Fun, List1, List2) -> List3 when + Fun :: fun((A, B) -> boolean()), + List1 :: [A], + List2 :: [B], + List3 :: [(A | B)], + A :: term(), + B :: term(). umerge(Fun, [], T2) when is_function(Fun, 2) -> T2; @@ -789,7 +984,10 @@ rumerge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) -> %% usort(List) -> L %% sorts the list L, removes duplicates --spec usort([T]) -> [T]. +-spec usort(List1) -> List2 when + List1 :: [T], + List2 :: [T], + T :: term(). usort([X, Y | L] = L0) when X < Y -> case L of @@ -844,7 +1042,11 @@ usort_1(X, []) -> %% umerge(List) -> L %% merges a list of sorted lists without duplicates, removes duplicates --spec umerge([[T]]) -> [T]. +-spec umerge(ListOfLists) -> List1 when + ListOfLists :: [List], + List :: [T], + List1 :: [T], + T :: term(). umerge(L) -> umergel(L). @@ -853,7 +1055,14 @@ umerge(L) -> %% merges three sorted lists X, Y and Z without duplicates, %% removes duplicates --spec umerge3([X], [Y], [Z]) -> [(X | Y | Z)]. +-spec umerge3(List1, List2, List3) -> List4 when + List1 :: [X], + List2 :: [Y], + List3 :: [Z], + List4 :: [(X | Y | Z)], + X :: term(), + Y :: term(), + Z :: term(). umerge3(L1, [], L3) -> umerge(L1, L3); @@ -878,7 +1087,12 @@ rumerge3(L1, [H2 | T2], [H3 | T3]) -> %% umerge(X, Y) -> L %% merges two sorted lists X and Y without duplicates, removes duplicates --spec umerge([X], [Y]) -> [(X | Y)]. +-spec umerge(List1, List2) -> List3 when + List1 :: [X], + List2 :: [Y], + List3 :: [(X | Y)], + X :: term(), + Y :: term(). umerge([], T2) -> T2; @@ -924,7 +1138,10 @@ rumerge(T1, [H2 | T2]) -> %% There are also versions with an extra argument, ExtraArgs, which is a %% list of extra arguments to each call. --spec all(fun((T) -> boolean()), [T]) -> boolean(). +-spec all(Pred, List) -> boolean() when + Pred :: fun((Elem :: T) -> boolean()), + List :: [T], + T :: term(). all(Pred, [Hd|Tail]) -> case Pred(Hd) of @@ -933,7 +1150,10 @@ all(Pred, [Hd|Tail]) -> end; all(Pred, []) when is_function(Pred, 1) -> true. --spec any(fun((T) -> boolean()), [T]) -> boolean(). +-spec any(Pred, List) -> boolean() when + Pred :: fun((Elem :: T) -> boolean()), + List :: [T], + T :: term(). any(Pred, [Hd|Tail]) -> case Pred(Hd) of @@ -942,31 +1162,59 @@ any(Pred, [Hd|Tail]) -> end; any(Pred, []) when is_function(Pred, 1) -> false. --spec map(fun((D) -> R), [D]) -> [R]. +-spec map(Fun, List1) -> List2 when + Fun :: fun((A) -> B), + List1 :: [A], + List2 :: [B], + A :: term(), + B :: term(). map(F, [H|T]) -> [F(H)|map(F, T)]; map(F, []) when is_function(F, 1) -> []. --spec flatmap(fun((D) -> [R]), [D]) -> [R]. +-spec flatmap(Fun, List1) -> List2 when + Fun :: fun((A) -> [B]), + List1 :: [A], + List2 :: [B], + A :: term(), + B :: term(). flatmap(F, [Hd|Tail]) -> F(Hd) ++ flatmap(F, Tail); flatmap(F, []) when is_function(F, 1) -> []. --spec foldl(fun((T, term()) -> term()), term(), [T]) -> term(). +-spec foldl(Fun, Acc0, List) -> Acc1 when + Fun :: fun((Elem :: T, AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + List :: [T], + T :: term(). foldl(F, Accu, [Hd|Tail]) -> foldl(F, F(Hd, Accu), Tail); foldl(F, Accu, []) when is_function(F, 2) -> Accu. --spec foldr(fun((T, term()) -> term()), term(), [T]) -> term(). +-spec foldr(Fun, Acc0, List) -> Acc1 when + Fun :: fun((Elem :: T, AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + List :: [T], + T :: term(). foldr(F, Accu, [Hd|Tail]) -> F(Hd, foldr(F, Accu, Tail)); foldr(F, Accu, []) when is_function(F, 2) -> Accu. --spec filter(Pred :: fun((T) -> boolean()), List :: [T]) -> [T]. +-spec filter(Pred, List1) -> List2 when + Pred :: fun((Elem :: T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). filter(Pred, List) when is_function(Pred, 1) -> [ E || E <- List, Pred(E) ]. @@ -974,7 +1222,12 @@ filter(Pred, List) when is_function(Pred, 1) -> %% Equivalent to {filter(F, L), filter(NotF, L)}, if NotF = 'fun(X) -> %% not F(X) end'. --spec partition(Pred :: fun((T) -> boolean()), List :: [T]) -> {[T], [T]}. +-spec partition(Pred, List) -> {Satisfying, NotSatisfying} when + Pred :: fun((Elem :: T) -> boolean()), + List :: [T], + Satisfying :: [T], + NotSatisfying :: [T], + T :: term(). partition(Pred, L) -> partition(Pred, L, [], []). @@ -1000,14 +1253,26 @@ zf(F, [Hd|Tail]) -> end; zf(F, []) when is_function(F, 1) -> []. --spec foreach(F :: fun((T) -> term()), List :: [T]) -> 'ok'. +-spec foreach(Fun, List) -> ok when + Fun :: fun((Elem :: T) -> term()), + List :: [T], + T :: term(). foreach(F, [Hd|Tail]) -> F(Hd), foreach(F, Tail); foreach(F, []) when is_function(F, 1) -> ok. --spec mapfoldl(fun((A, term()) -> {B, term()}), term(), [A]) -> {[B], term()}. +-spec mapfoldl(Fun, Acc0, List1) -> {List2, Acc1} when + Fun :: fun((A, AccIn) -> {B, AccOut}), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + List1 :: [A], + List2 :: [B], + A :: term(), + B :: term(). mapfoldl(F, Accu0, [Hd|Tail]) -> {R,Accu1} = F(Hd, Accu0), @@ -1015,7 +1280,16 @@ mapfoldl(F, Accu0, [Hd|Tail]) -> {[R|Rs],Accu2}; mapfoldl(F, Accu, []) when is_function(F, 2) -> {[],Accu}. --spec mapfoldr(fun((A, term()) -> {B, term()}), term(), [A]) -> {[B], term()}. +-spec mapfoldr(Fun, Acc0, List1) -> {List2, Acc1} when + Fun :: fun((A, AccIn) -> {B, AccOut}), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + List1 :: [A], + List2 :: [B], + A :: term(), + B :: term(). mapfoldr(F, Accu0, [Hd|Tail]) -> {Rs,Accu1} = mapfoldr(F, Accu0, Tail), @@ -1023,7 +1297,11 @@ mapfoldr(F, Accu0, [Hd|Tail]) -> {[R|Rs],Accu2}; mapfoldr(F, Accu, []) when is_function(F, 2) -> {[],Accu}. --spec takewhile(fun((T) -> boolean()), [T]) -> [T]. +-spec takewhile(Pred, List1) -> List2 when + Pred :: fun((Elem :: T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). takewhile(Pred, [Hd|Tail]) -> case Pred(Hd) of @@ -1032,7 +1310,11 @@ takewhile(Pred, [Hd|Tail]) -> end; takewhile(Pred, []) when is_function(Pred, 1) -> []. --spec dropwhile(fun((T) -> boolean()), [T]) -> [T]. +-spec dropwhile(Pred, List1) -> List2 when + Pred :: fun((Elem :: T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). dropwhile(Pred, [Hd|Tail]=Rest) -> case Pred(Hd) of @@ -1041,7 +1323,12 @@ dropwhile(Pred, [Hd|Tail]=Rest) -> end; dropwhile(Pred, []) when is_function(Pred, 1) -> []. --spec splitwith(fun((T) -> boolean()), [T]) -> {[T], [T]}. +-spec splitwith(Pred, List) -> {List1, List2} when + Pred :: fun((T) -> boolean()), + List :: [T], + List1 :: [T], + List2 :: [T], + T :: term(). splitwith(Pred, List) when is_function(Pred, 1) -> splitwith(Pred, List, []). @@ -1054,7 +1341,12 @@ splitwith(Pred, [Hd|Tail], Taken) -> splitwith(Pred, [], Taken) when is_function(Pred, 1) -> {reverse(Taken),[]}. --spec split(non_neg_integer(), [T]) -> {[T], [T]}. +-spec split(N, List1) -> {List2, List3} when + N :: non_neg_integer(), + List1 :: [T], + List2 :: [T], + List3 :: [T], + T :: term(). split(N, List) when is_integer(N), N >= 0, is_list(List) -> case split(N, List, []) of diff --git a/lib/stdlib/src/log_mf_h.erl b/lib/stdlib/src/log_mf_h.erl index 5fa5360fa1..f7f128dac7 100644 --- a/lib/stdlib/src/log_mf_h.erl +++ b/lib/stdlib/src/log_mf_h.erl @@ -27,14 +27,13 @@ %%----------------------------------------------------------------- --type dir() :: file:filename(). -type b() :: non_neg_integer(). -type f() :: 1..255. -type pred() :: fun((term()) -> boolean()). %%----------------------------------------------------------------- --record(state, {dir :: dir(), +-record(state, {dir :: file:filename(), maxB :: b(), maxF :: f(), curB :: b(), @@ -67,11 +66,23 @@ %% EventMgr = pid() | atom(). %%----------------------------------------------------------------- --spec init(dir(), b(), f()) -> {dir(), b(), f(), pred()}. +-opaque args() :: {file:filename(), b(), f(), pred()}. + + +-spec init(Dir, MaxBytes, MaxFiles) -> Args when + Dir :: file:filename(), + MaxBytes :: non_neg_integer(), % b() + MaxFiles :: 1..255, % f() + Args :: args(). init(Dir, MaxB, MaxF) -> init(Dir, MaxB, MaxF, fun(_) -> true end). --spec init(dir(), b(), f(), pred()) -> {dir(), b(), f(), pred()}. +-spec init(Dir, MaxBytes, MaxFiles, Pred) -> Args when + Dir :: file:filename(), + MaxBytes :: non_neg_integer(), % b() + MaxFiles :: 1..255, % f() + Pred :: fun((Event :: term()) -> boolean()), % pred() + Args :: args(). init(Dir, MaxB, MaxF, Pred) -> {Dir, MaxB, MaxF, Pred}. @@ -79,7 +90,7 @@ init(Dir, MaxB, MaxF, Pred) -> {Dir, MaxB, MaxF, Pred}. %% Call-back functions from gen_event %%----------------------------------------------------------------- --spec init({dir(), b(), f(), pred()}) -> {'ok', #state{}} | {'error', term()}. +-spec init({file:filename(), non_neg_integer(), f(), pred()}) -> {'ok', #state{}} | {'error', term()}. init({Dir, MaxB, MaxF, Pred}) when is_integer(MaxF), MaxF > 0, MaxF < 256 -> First = diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl index b565eb20f4..48e22e53fa 100644 --- a/lib/stdlib/src/ms_transform.erl +++ b/lib/stdlib/src/ms_transform.erl @@ -66,6 +66,11 @@ %% %% Called by compiler or ets/dbg:fun2ms when errors/warnings occur %% + +-spec(format_error(Error) -> Chars when + Error :: {error, module(), term()}, + Chars :: io_lib:chars()). + format_error({?WARN_SHADOW_VAR,Name}) -> lists:flatten( io_lib:format("variable ~p shadowed in ms_transform fun head", @@ -186,6 +191,12 @@ format_error(Else) -> %% %% Called when translating in shell %% + +-spec transform_from_shell(Dialect, Clauses, BoundEnvironment) -> term() when + Dialect :: ets | dbg, + Clauses :: [erl_parse:abstract_clause()], + BoundEnvironment :: erl_eval:binding_struct(). + transform_from_shell(Dialect, Clauses, BoundEnvironment) -> SaveFilename = setup_filename(), case catch ms_clause_list(1,Clauses,Dialect,gb_sets:new()) of @@ -211,6 +222,11 @@ transform_from_shell(Dialect, Clauses, BoundEnvironment) -> %% %% Called when translating during compiling %% + +-spec parse_transform(Forms, Options) -> Forms when + Forms :: [erl_parse:abstract_form()], + Options :: term(). + parse_transform(Forms, _Options) -> SaveFilename = setup_filename(), %io:format("Forms: ~p~n",[Forms]), diff --git a/lib/stdlib/src/orddict.erl b/lib/stdlib/src/orddict.erl index 4e30c9eefd..45d3c84b3e 100644 --- a/lib/stdlib/src/orddict.erl +++ b/lib/stdlib/src/orddict.erl @@ -25,9 +25,11 @@ -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]). + %%--------------------------------------------------------------------------- --type orddict() :: [{term(), term()}]. +-type orddict() :: [{Key :: term(), Value :: term()}]. %%--------------------------------------------------------------------------- @@ -35,45 +37,63 @@ new() -> []. --spec is_key(Key::term(), Dictionary::orddict()) -> boolean(). +-spec is_key(Key, Orddict) -> boolean() when + Key :: term(), + Orddict :: orddict(). is_key(Key, [{K,_}|_]) when Key < K -> false; is_key(Key, [{K,_}|Dict]) when Key > K -> is_key(Key, Dict); is_key(_Key, [{_K,_Val}|_]) -> true; %Key == K is_key(_, []) -> false. --spec to_list(orddict()) -> [{term(), term()}]. +-spec to_list(Orddict) -> List when + Orddict :: orddict(), + List :: [{Key :: term(), Value :: term()}]. to_list(Dict) -> Dict. --spec from_list([{term(), term()}]) -> orddict(). +-spec from_list(List) -> Orddict when + List :: [{Key :: term(), Value :: term()}], + Orddict :: orddict(). from_list(Pairs) -> lists:foldl(fun ({K,V}, D) -> store(K, V, D) end, [], Pairs). --spec size(orddict()) -> non_neg_integer(). +-spec size(Orddict) -> non_neg_integer() when + Orddict :: orddict(). size(D) -> length(D). --spec fetch(Key::term(), Dictionary::orddict()) -> term(). +-spec fetch(Key, Orddict) -> Value when + Key :: term(), + Value :: term(), + Orddict :: orddict(). fetch(Key, [{K,_}|D]) when Key > K -> fetch(Key, D); fetch(Key, [{K,Value}|_]) when Key == K -> Value. --spec find(Key::term(), Dictionary::orddict()) -> {'ok', term()} | 'error'. +-spec find(Key, Orddict) -> {'ok', Value} | 'error' when + Key :: term(), + Orddict :: orddict(), + Value :: term(). find(Key, [{K,_}|_]) when Key < K -> error; find(Key, [{K,_}|D]) when Key > K -> find(Key, D); find(_Key, [{_K,Value}|_]) -> {ok,Value}; %Key == K find(_, []) -> error. --spec fetch_keys(Dictionary::orddict()) -> [term()]. +-spec fetch_keys(Orddict) -> Keys when + Orddict :: orddict(), + Keys :: [term()]. fetch_keys([{Key,_}|Dict]) -> [Key|fetch_keys(Dict)]; fetch_keys([]) -> []. --spec erase(Key::term(), Dictionary::orddict()) -> orddict(). +-spec erase(Key, Orddict1) -> Orddict2 when + Key :: term(), + Orddict1 :: orddict(), + Orddict2 :: orddict(). erase(Key, [{K,_}=E|Dict]) when Key < K -> [E|Dict]; erase(Key, [{K,_}=E|Dict]) when Key > K -> @@ -81,7 +101,11 @@ erase(Key, [{K,_}=E|Dict]) when Key > K -> erase(_Key, [{_K,_Val}|Dict]) -> Dict; %Key == K erase(_, []) -> []. --spec store(Key::term(), Value::term(), Dictionary::orddict()) -> orddict(). +-spec store(Key, Value, Orddict1) -> Orddict2 when + Key :: term(), + Value :: term(), + Orddict1 :: orddict(), + Orddict2 :: orddict(). store(Key, New, [{K,_}=E|Dict]) when Key < K -> [{Key,New},E|Dict]; @@ -91,7 +115,11 @@ store(Key, New, [{_K,_Old}|Dict]) -> %Key == K [{Key,New}|Dict]; store(Key, New, []) -> [{Key,New}]. --spec append(Key::term(), Value::term(), Dictionary::orddict()) -> orddict(). +-spec append(Key, Value, Orddict1) -> Orddict2 when + Key :: term(), + Value :: term(), + Orddict1 :: orddict(), + Orddict2 :: orddict(). append(Key, New, [{K,_}=E|Dict]) when Key < K -> [{Key,[New]},E|Dict]; @@ -101,7 +129,11 @@ append(Key, New, [{_K,Old}|Dict]) -> %Key == K [{Key,Old ++ [New]}|Dict]; append(Key, New, []) -> [{Key,[New]}]. --spec append_list(Key::term(), ValueList::[term()], orddict()) -> orddict(). +-spec append_list(Key, ValList, Orddict1) -> Orddict2 when + Key :: term(), + ValList :: [Value :: term()], + Orddict1 :: orddict(), + Orddict2 :: orddict(). append_list(Key, NewList, [{K,_}=E|Dict]) when Key < K -> [{Key,NewList},E|Dict]; @@ -112,14 +144,23 @@ append_list(Key, NewList, [{_K,Old}|Dict]) -> %Key == K append_list(Key, NewList, []) -> [{Key,NewList}]. --spec update(Key::term(), Fun::fun((term()) -> term()), orddict()) -> orddict(). +-spec update(Key, Fun, Orddict1) -> Orddict2 when + Key :: term(), + Fun :: fun((Value1 :: term()) -> Value2 :: term()), + Orddict1 :: orddict(), + Orddict2 :: orddict(). update(Key, Fun, [{K,_}=E|Dict]) when Key > K -> [E|update(Key, Fun, Dict)]; update(Key, Fun, [{K,Val}|Dict]) when Key == K -> [{Key,Fun(Val)}|Dict]. --spec update(term(), fun((term()) -> term()), term(), orddict()) -> orddict(). +-spec update(Key, Fun, Initial, Orddict1) -> Orddict2 when + Key :: term(), + Initial :: term(), + Fun :: fun((Value1 :: term()) -> Value2 :: term()), + Orddict1 :: orddict(), + Orddict2 :: orddict(). update(Key, _, Init, [{K,_}=E|Dict]) when Key < K -> [{Key,Init},E|Dict]; @@ -129,7 +170,11 @@ update(Key, Fun, _Init, [{_K,Val}|Dict]) -> %Key == K [{Key,Fun(Val)}|Dict]; update(Key, _, Init, []) -> [{Key,Init}]. --spec update_counter(Key::term(), Incr::number(), orddict()) -> orddict(). +-spec update_counter(Key, Increment, Orddict1) -> Orddict2 when + Key :: term(), + Increment :: number(), + Orddict1 :: orddict(), + Orddict2 :: orddict(). update_counter(Key, Incr, [{K,_}=E|Dict]) when Key < K -> [{Key,Incr},E|Dict]; @@ -139,19 +184,29 @@ update_counter(Key, Incr, [{_K,Val}|Dict]) -> %Key == K [{Key,Val+Incr}|Dict]; update_counter(Key, Incr, []) -> [{Key,Incr}]. --spec fold(fun((term(),term(),term()) -> term()), term(), orddict()) -> term(). +-spec fold(Fun, Acc0, Orddict) -> Acc1 when + Fun :: fun((Key :: term(), Value :: term(), AccIn :: term()) -> AccOut :: term()), + Acc0 :: term(), + Acc1 :: term(), + Orddict :: orddict(). 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((term(), term()) -> term()), orddict()) -> orddict(). +-spec map(Fun, Orddict1) -> Orddict2 when + Fun :: fun((Key :: term(), Value1 :: term()) -> Value2 :: term()), + Orddict1 :: orddict(), + Orddict2 :: orddict(). map(F, [{Key,Val}|D]) -> [{Key,F(Key, Val)}|map(F, D)]; map(F, []) when is_function(F, 2) -> []. --spec filter(fun((term(), term()) -> boolean()), orddict()) -> orddict(). +-spec filter(Pred, Orddict1) -> Orddict2 when + Pred :: fun((Key :: term(), Value :: term()) -> boolean()), + Orddict1 :: orddict(), + Orddict2 :: orddict(). filter(F, [{Key,Val}=E|D]) -> case F(Key, Val) of @@ -160,8 +215,11 @@ filter(F, [{Key,Val}=E|D]) -> end; filter(F, []) when is_function(F, 2) -> []. --spec merge(fun((term(), term(), term()) -> term()), orddict(), orddict()) -> - orddict(). +-spec merge(Fun, Orddict1, Orddict2) -> Orddict3 when + Fun :: fun((Key :: term(), Value1 :: term(), Value2 :: term()) -> Value :: term()), + Orddict1 :: orddict(), + Orddict2 :: orddict(), + Orddict3 :: orddict(). merge(F, [{K1,_}=E1|D1], [{K2,_}=E2|D2]) when K1 < K2 -> [E1|merge(F, D1, [E2|D2])]; diff --git a/lib/stdlib/src/ordsets.erl b/lib/stdlib/src/ordsets.erl index 5a1c260703..4a8b1275b2 100644 --- a/lib/stdlib/src/ordsets.erl +++ b/lib/stdlib/src/ordsets.erl @@ -40,7 +40,8 @@ new() -> []. %% is_set(Term) -> boolean(). %% Return 'true' if Set is an ordered set of elements, else 'false'. --spec is_set(term()) -> boolean(). +-spec is_set(Ordset) -> boolean() when + Ordset :: term(). is_set([E|Es]) -> is_set(Es, E); is_set([]) -> true; @@ -54,21 +55,26 @@ is_set([], _) -> true. %% size(OrdSet) -> int(). %% Return the number of elements in OrdSet. --spec size(ordset(_)) -> non_neg_integer(). +-spec size(Ordset) -> non_neg_integer() when + Ordset :: ordset(_). size(S) -> length(S). %% to_list(OrdSet) -> [Elem]. %% Return the elements in OrdSet as a list. --spec to_list(ordset(T)) -> [T]. +-spec to_list(Ordset) -> List when + Ordset :: ordset(T), + List :: [T]. to_list(S) -> S. %% from_list([Elem]) -> Set. %% Build an ordered set from the elements in List. --spec from_list([T]) -> ordset(T). +-spec from_list(List) -> Ordset when + List :: [T], + Ordset :: ordset(T). from_list(L) -> lists:usort(L). @@ -76,7 +82,9 @@ from_list(L) -> %% is_element(Element, OrdSet) -> boolean(). %% Return 'true' if Element is an element of OrdSet, else 'false'. --spec is_element(term(), ordset(_)) -> boolean(). +-spec is_element(Element, Ordset) -> boolean() when + Element :: term(), + Ordset :: ordset(_). is_element(E, [H|Es]) when E > H -> is_element(E, Es); is_element(E, [H|_]) when E < H -> false; @@ -86,7 +94,12 @@ is_element(_, []) -> false. %% add_element(Element, OrdSet) -> OrdSet. %% Return OrdSet with Element inserted in it. --spec add_element(E, ordset(T)) -> [T | E,...]. +-spec add_element(Element, Ordset1) -> Ordset2 when + Element :: E, + Ordset1 :: ordset(T), + Ordset2 :: ordset(T | E). + +%-spec add_element(E, ordset(T)) -> [T | E,...]. add_element(E, [H|Es]) when E > H -> [H|add_element(E, Es)]; add_element(E, [H|_]=Set) when E < H -> [E|Set]; @@ -96,7 +109,10 @@ add_element(E, []) -> [E]. %% del_element(Element, OrdSet) -> OrdSet. %% Return OrdSet but with Element removed. --spec del_element(term(), ordset(T)) -> ordset(T). +-spec del_element(Element, Ordset1) -> Ordset2 when + Element :: term(), + Ordset1 :: ordset(T), + Ordset2 :: ordset(T). del_element(E, [H|Es]) when E > H -> [H|del_element(E, Es)]; del_element(E, [H|_]=Set) when E < H -> Set; @@ -106,7 +122,10 @@ del_element(_, []) -> []. %% union(OrdSet1, OrdSet2) -> OrdSet %% Return the union of OrdSet1 and OrdSet2. --spec union(ordset(T1), ordset(T2)) -> ordset(T1 | T2). +-spec union(Ordset1, Ordset2) -> Ordset3 when + Ordset1 :: ordset(T1), + Ordset2 :: ordset(T2), + Ordset3 :: ordset(T1 | T2). union([E1|Es1], [E2|_]=Set2) when E1 < E2 -> [E1|union(Es1, Set2)]; @@ -120,7 +139,9 @@ union(Es1, []) -> Es1. %% union([OrdSet]) -> OrdSet %% Return the union of the list of ordered sets. --spec union([ordset(T)]) -> ordset(T). +-spec union(OrdsetList) -> Ordset when + OrdsetList :: [ordset(T)], + Ordset :: ordset(T). union([S1,S2|Ss]) -> union1(union(S1, S2), Ss); @@ -133,7 +154,10 @@ union1(S1, []) -> S1. %% intersection(OrdSet1, OrdSet2) -> OrdSet. %% Return the intersection of OrdSet1 and OrdSet2. --spec intersection(ordset(_), ordset(_)) -> ordset(_). +-spec intersection(Ordset1, Ordset2) -> Ordset3 when + Ordset1 :: ordset(_), + Ordset2 :: ordset(_), + Ordset3 :: ordset(_). intersection([E1|Es1], [E2|_]=Set2) when E1 < E2 -> intersection(Es1, Set2); @@ -149,7 +173,9 @@ intersection(_, []) -> %% intersection([OrdSet]) -> OrdSet. %% Return the intersection of the list of ordered sets. --spec intersection([ordset(_),...]) -> ordset(_). +-spec intersection(OrdsetList) -> Ordset when + OrdsetList :: [ordset(_),...], + Ordset :: ordset(_). intersection([S1,S2|Ss]) -> intersection1(intersection(S1, S2), Ss); @@ -162,7 +188,9 @@ intersection1(S1, []) -> S1. %% is_disjoint(OrdSet1, OrdSet2) -> boolean(). %% Check whether OrdSet1 and OrdSet2 are disjoint. --spec is_disjoint(ordset(_), ordset(_)) -> boolean(). +-spec is_disjoint(Ordset1, Ordset2) -> boolean() when + Ordset1 :: ordset(_), + Ordset2 :: ordset(_). is_disjoint([E1|Es1], [E2|_]=Set2) when E1 < E2 -> is_disjoint(Es1, Set2); @@ -179,7 +207,10 @@ is_disjoint(_, []) -> %% Return all and only the elements of OrdSet1 which are not also in %% OrdSet2. --spec subtract(ordset(_), ordset(_)) -> ordset(_). +-spec subtract(Ordset1, Ordset2) -> Ordset3 when + Ordset1 :: ordset(_), + Ordset2 :: ordset(_), + Ordset3 :: ordset(_). subtract([E1|Es1], [E2|_]=Set2) when E1 < E2 -> [E1|subtract(Es1, Set2)]; @@ -194,7 +225,9 @@ subtract(Es1, []) -> Es1. %% Return 'true' when every element of OrdSet1 is also a member of %% OrdSet2, else 'false'. --spec is_subset(ordset(_), ordset(_)) -> boolean(). +-spec is_subset(Ordset1, Ordset2) -> boolean() when + Ordset1 :: ordset(_), + Ordset2 :: ordset(_). is_subset([E1|_], [E2|_]) when E1 < E2 -> %E1 not in Set2 false; @@ -208,7 +241,11 @@ is_subset(_, []) -> false. %% fold(Fun, Accumulator, OrdSet) -> Accumulator. %% Fold function Fun over all elements in OrdSet and return Accumulator. --spec fold(fun((T, term()) -> term()), term(), ordset(T)) -> term(). +-spec fold(Function, Acc0, Ordset) -> Acc1 when + Function :: fun((Element :: T, AccIn :: term()) -> AccOut :: term()), + Ordset :: ordset(T), + Acc0 :: term(), + Acc1 :: term(). fold(F, Acc, Set) -> lists:foldl(F, Acc, Set). @@ -216,7 +253,10 @@ fold(F, Acc, Set) -> %% filter(Fun, OrdSet) -> OrdSet. %% Filter OrdSet with Fun. --spec filter(fun((T) -> boolean()), ordset(T)) -> ordset(T). +-spec filter(Pred, Ordset1) -> Ordset2 when + Pred :: fun((Element :: T) -> boolean()), + Ordset1 :: ordset(T), + Ordset2 :: ordset(T). filter(F, Set) -> lists:filter(F, Set). diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl index 5c52dfcbf0..c1285dab60 100644 --- a/lib/stdlib/src/otp_internal.erl +++ b/lib/stdlib/src/otp_internal.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2010. All Rights Reserved. +%% Copyright Ericsson AB 1999-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -180,6 +180,9 @@ obsolete_1(calendar, local_time_to_universal_time, 1) -> obsolete_1(rpc, safe_multi_server_call, A) when A =:= 2; A =:= 3 -> {deprecated, {rpc, multi_server_call, A}}; + +%% *** SNMP *** + obsolete_1(snmp, N, A) -> case is_snmp_agent_function(N, A) of false -> @@ -189,9 +192,100 @@ obsolete_1(snmp, N, A) -> integer_to_list(A)++" instead"} end; +obsolete_1(snmpm, agent_info, 3) -> + {deprecated, {snmpm, agent_info, 2}, "R16B"}; +obsolete_1(snmpm, update_agent_info, 5) -> + {deprecated, {snmpm, update_agent_info, 4}, "R16B"}; +obsolete_1(snmpm, g, 3) -> + {deprecated, {snmpm, sync_get, 3}, "R16B"}; +obsolete_1(snmpm, g, 4) -> + {deprecated, {snmpm, sync_get, [3,4]}, "R16B"}; +obsolete_1(snmpm, g, 5) -> + {deprecated, {snmpm, sync_get, [4,5]}, "R16B"}; +obsolete_1(snmpm, g, 6) -> + {deprecated, {snmpm, sync_get, [5,6]}, "R16B"}; +obsolete_1(snmpm, g, 7) -> + {deprecated, {snmpm, sync_get, 6}, "R16B"}; +obsolete_1(snmpm, ag, 3) -> + {deprecated, {snmpm, async_get, 3}, "R16B"}; +obsolete_1(snmpm, ag, 4) -> + {deprecated, {snmpm, async_get, [3,4]}, "R16B"}; +obsolete_1(snmpm, ag, 5) -> + {deprecated, {snmpm, async_get, [4,5]}, "R16B"}; +obsolete_1(snmpm, ag, 6) -> + {deprecated, {snmpm, async_get, [5,6]}, "R16B"}; +obsolete_1(snmpm, ag, 7) -> + {deprecated, {snmpm, async_get, 6}, "R16B"}; +obsolete_1(snmpm, gn, 3) -> + {deprecated, {snmpm, sync_get_next, 3}, "R16B"}; +obsolete_1(snmpm, gn, 4) -> + {deprecated, {snmpm, sync_get_next, [3,4]}, "R16B"}; +obsolete_1(snmpm, gn, 5) -> + {deprecated, {snmpm, sync_get_next, [4,5]}, "R16B"}; +obsolete_1(snmpm, gn, 6) -> + {deprecated, {snmpm, sync_get_next, [5,6]}, "R16B"}; +obsolete_1(snmpm, gn, 7) -> + {deprecated, {snmpm, sync_get_next, 6}, "R16B"}; +obsolete_1(snmpm, agn, 3) -> + {deprecated, {snmpm, async_get_next, 3}, "R16B"}; +obsolete_1(snmpm, agn, 4) -> + {deprecated, {snmpm, async_get_next, [3,4]}, "R16B"}; +obsolete_1(snmpm, agn, 5) -> + {deprecated, {snmpm, async_get_next, [4,5]}, "R16B"}; +obsolete_1(snmpm, agn, 6) -> + {deprecated, {snmpm, async_get_next, [5,6]}, "R16B"}; +obsolete_1(snmpm, agn, 7) -> + {deprecated, {snmpm, async_get_next, 6}, "R16B"}; +obsolete_1(snmpm, s, 3) -> + {deprecated, {snmpm, sync_set, 3}, "R16B"}; +obsolete_1(snmpm, s, 4) -> + {deprecated, {snmpm, sync_set, [3,4]}, "R16B"}; +obsolete_1(snmpm, s, 5) -> + {deprecated, {snmpm, sync_set, [4,5]}, "R16B"}; +obsolete_1(snmpm, s, 6) -> + {deprecated, {snmpm, sync_set, [5,6]}, "R16B"}; +obsolete_1(snmpm, s, 7) -> + {deprecated, {snmpm, sync_set, 6}, "R16B"}; +obsolete_1(snmpm, as, 3) -> + {deprecated, {snmpm, async_set, 3}, "R16B"}; +obsolete_1(snmpm, as, 4) -> + {deprecated, {snmpm, async_set, [3,4]}, "R16B"}; +obsolete_1(snmpm, as, 5) -> + {deprecated, {snmpm, async_set, [4,5]}, "R16B"}; +obsolete_1(snmpm, as, 6) -> + {deprecated, {snmpm, async_set, [5,6]}, "R16B"}; +obsolete_1(snmpm, as, 7) -> + {deprecated, {snmpm, async_set, 6}, "R16B"}; +obsolete_1(snmpm, gb, 5) -> + {deprecated, {snmpm, sync_get_bulk, 5}, "R16B"}; +obsolete_1(snmpm, gb, 6) -> + {deprecated, {snmpm, sync_get_bulk, [5,6]}, "R16B"}; +obsolete_1(snmpm, gb, 7) -> + {deprecated, {snmpm, sync_get_bulk, [6,7]}, "R16B"}; +obsolete_1(snmpm, gb, 8) -> + {deprecated, {snmpm, sync_get_bulk, [7,8]}, "R16B"}; +obsolete_1(snmpm, gb, 9) -> + {deprecated, {snmpm, sync_get_bulk, 8}, "R16B"}; +obsolete_1(snmpm, agb, 5) -> + {deprecated, {snmpm, async_get_bulk, 5}, "R16B"}; +obsolete_1(snmpm, agb, 6) -> + {deprecated, {snmpm, async_get_bulk, [5,6]}, "R16B"}; +obsolete_1(snmpm, agb, 7) -> + {deprecated, {snmpm, async_get_bulk, [6,7]}, "R16B"}; +obsolete_1(snmpm, agb, 8) -> + {deprecated, {snmpm, async_get_bulk, [7,8]}, "R16B"}; +obsolete_1(snmpm, agb, 9) -> + {deprecated, {snmpm, async_get_bulk, 8}, "R16B"}; + + +%% *** MEGACO *** + obsolete_1(megaco, format_versions, 1) -> {deprecated, "Deprecated; use megaco:print_version_info/0,1 instead"}; + +%% *** OS-MON-MIB *** + obsolete_1(os_mon_mib, init, 1) -> {deprecated, {os_mon_mib, load, 1}}; obsolete_1(os_mon_mib, stop, 1) -> @@ -236,22 +330,22 @@ obsolete_1(erlang, fault, 2) -> obsolete_1(file, rawopen, 2) -> {removed, "deprecated (will be removed in R13B); use file:open/2 with the raw option"}; -obsolete_1(http, request, 1) -> {deprecated,{httpc,request,1},"R15B"}; -obsolete_1(http, request, 2) -> {deprecated,{httpc,request,2},"R15B"}; -obsolete_1(http, request, 4) -> {deprecated,{httpc,request,4},"R15B"}; -obsolete_1(http, request, 5) -> {deprecated,{httpc,request,5},"R15B"}; -obsolete_1(http, cancel_request, 1) -> {deprecated,{httpc,cancel_request,1},"R15B"}; -obsolete_1(http, cancel_request, 2) -> {deprecated,{httpc,cancel_request,2},"R15B"}; -obsolete_1(http, set_option, 2) -> {deprecated,{httpc,set_option,2},"R15B"}; -obsolete_1(http, set_option, 3) -> {deprecated,{httpc,set_option,3},"R15B"}; -obsolete_1(http, set_options, 1) -> {deprecated,{httpc,set_options,1},"R15B"}; -obsolete_1(http, set_options, 2) -> {deprecated,{httpc,set_options,2},"R15B"}; -obsolete_1(http, verify_cookies, 2) -> {deprecated,{httpc,verify_cookies,2},"R15B"}; -obsolete_1(http, verify_cookies, 3) -> {deprecated,{httpc,verify_cookies,3},"R15B"}; -obsolete_1(http, cookie_header, 1) -> {deprecated,{httpc,cookie_header,1},"R15B"}; -obsolete_1(http, cookie_header, 2) -> {deprecated,{httpc,cookie_header,2},"R15B"}; -obsolete_1(http, stream_next, 1) -> {deprecated,{httpc,stream_next,1},"R15B"}; -obsolete_1(http, default_profile, 0) -> {deprecated,{httpc,default_profile,0},"R15B"}; +obsolete_1(http, request, 1) -> {removed,{httpc,request,1},"R15B"}; +obsolete_1(http, request, 2) -> {removed,{httpc,request,2},"R15B"}; +obsolete_1(http, request, 4) -> {removed,{httpc,request,4},"R15B"}; +obsolete_1(http, request, 5) -> {removed,{httpc,request,5},"R15B"}; +obsolete_1(http, cancel_request, 1) -> {removed,{httpc,cancel_request,1},"R15B"}; +obsolete_1(http, cancel_request, 2) -> {removed,{httpc,cancel_request,2},"R15B"}; +obsolete_1(http, set_option, 2) -> {removed,{httpc,set_option,2},"R15B"}; +obsolete_1(http, set_option, 3) -> {removed,{httpc,set_option,3},"R15B"}; +obsolete_1(http, set_options, 1) -> {removed,{httpc,set_options,1},"R15B"}; +obsolete_1(http, set_options, 2) -> {removed,{httpc,set_options,2},"R15B"}; +obsolete_1(http, verify_cookies, 2) -> {removed,{httpc,store_cookies,2},"R15B"}; +obsolete_1(http, verify_cookies, 3) -> {removed,{httpc,store_cookies,3},"R15B"}; +obsolete_1(http, cookie_header, 1) -> {removed,{httpc,cookie_header,1},"R15B"}; +obsolete_1(http, cookie_header, 2) -> {removed,{httpc,cookie_header,2},"R15B"}; +obsolete_1(http, stream_next, 1) -> {removed,{httpc,stream_next,1},"R15B"}; +obsolete_1(http, default_profile, 0) -> {removed,{httpc,default_profile,0},"R15B"}; obsolete_1(httpd, start, 0) -> {removed,{inets,start,[2,3]},"R14B"}; obsolete_1(httpd, start, 1) -> {removed,{inets,start,[2,3]},"R14B"}; @@ -355,7 +449,7 @@ obsolete_1(ssl_pkix, decode_cert, A) when A =:= 1; A =:= 2 -> %% Added in R13B04. obsolete_1(erlang, concat_binary, 1) -> - {deprecated,{erlang,list_to_binary,1},"R15B"}; + {removed,{erlang,list_to_binary,1},"R15B"}; %% Added in R14A. obsolete_1(ssl, peercert, 2) -> @@ -367,6 +461,18 @@ obsolete_1(public_key, pem_to_der, 1) -> obsolete_1(public_key, decode_private_key, A) when A =:= 1; A =:= 2 -> {deprecated,{public_key,pem_entry_decode,1},"R15A"}; +%% Added in R14B03. +obsolete_1(docb_gen, _, _) -> + {deprecated,"the DocBuilder application is deprecated (will be removed in R15B)"}; +obsolete_1(docb_transform, _, _) -> + {deprecated,"the DocBuilder application is deprecated (will be removed in R15B)"}; +obsolete_1(docb_xml_check, _, _) -> + {deprecated,"the DocBuilder application is deprecated (will be removed in R15B)"}; + +%% Added in R15B +obsolete_1(asn1rt, F, _) when F == load_driver; F == unload_driver -> + {deprecated,"deprecated (will be removed in R16A); has no effect as drivers are no longer used."}; + obsolete_1(_, _, _) -> no. diff --git a/lib/stdlib/src/pg.erl b/lib/stdlib/src/pg.erl index 503654e706..ee177e4e0b 100644 --- a/lib/stdlib/src/pg.erl +++ b/lib/stdlib/src/pg.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -35,7 +35,9 @@ %% Create a brand new empty process group with the master residing %% at the local node --spec create(term()) -> 'ok' | {'error', term()}. +-spec create(PgName) -> 'ok' | {'error', Reason} when + PgName :: term(), + Reason :: 'already_created' | term(). create(PgName) -> catch begin check(PgName), @@ -46,7 +48,10 @@ create(PgName) -> %% Create a brand new empty process group with the master %% residing at Node --spec create(term(), node()) -> 'ok' | {'error', term()}. +-spec create(PgName, Node) -> 'ok' | {'error', Reason} when + PgName :: term(), + Node :: node(), + Reason :: 'already_created' | term(). create(PgName, Node) -> catch begin check(PgName), @@ -66,7 +71,10 @@ standby(_PgName, _Node) -> %% Tell process group PgName that Pid is a new member of the group %% synchronously return a list of all old members in the group --spec join(atom(), pid()) -> [pid()]. +-spec join(PgName, Pid) -> Members when + PgName :: term(), + Pid :: pid(), + Members :: [pid()]. join(PgName, Pid) when is_atom(PgName) -> global:send(PgName, {join,self(),Pid}), @@ -77,7 +85,9 @@ join(PgName, Pid) when is_atom(PgName) -> %% Multi cast Mess to all members in the group --spec send(atom() | pid(), term()) -> 'ok'. +-spec send(PgName, Msg) -> 'ok' when + PgName :: term(), + Msg :: term(). send(PgName, Mess) when is_atom(PgName) -> global:send(PgName, {send, self(), Mess}), @@ -89,7 +99,9 @@ send(Pg, Mess) when is_pid(Pg) -> %% multi cast a message to all members in the group but ourselves %% If we are a member --spec esend(atom() | pid(), term()) -> 'ok'. +-spec esend(PgName, Msg) -> 'ok' when + PgName :: term(), + Msg :: term(). esend(PgName, Mess) when is_atom(PgName) -> global:send(PgName, {esend,self(),Mess}), @@ -100,7 +112,9 @@ esend(Pg, Mess) when is_pid(Pg) -> %% Return the members of the group --spec members(atom() | pid()) -> [pid()]. +-spec members(PgName) -> Members when + PgName :: term(), + Members :: [pid()]. members(PgName) when is_atom(PgName) -> global:send(PgName, {self() ,members}), diff --git a/lib/stdlib/src/pool.erl b/lib/stdlib/src/pool.erl index 7f5f23e26d..a5eb191ab2 100644 --- a/lib/stdlib/src/pool.erl +++ b/lib/stdlib/src/pool.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -52,11 +52,16 @@ %% Start up using the .hosts.erlang file --spec start(atom()) -> [node()]. +-spec start(Name) -> Nodes when + Name :: atom(), + Nodes :: [node()]. start(Name) -> start(Name,[]). --spec start(atom(), string()) -> [node()]. +-spec start(Name, Args) -> Nodes when + Name :: atom(), + Args :: string(), + Nodes :: [node()]. start(Name, Args) when is_atom(Name) -> gen_server:start({global, pool_master}, pool, [], []), Hosts = net_adm:host_file(), @@ -71,7 +76,8 @@ start(Name, Args) when is_atom(Name) -> get_nodes() -> get_elements(2, get_nodes_and_load()). --spec attach(node()) -> 'already_attached' | 'attached'. +-spec attach(Node) -> 'already_attached' | 'attached' when + Node :: node(). attach(Node) -> gen_server:call({global, pool_master}, {attach, Node}). @@ -82,11 +88,17 @@ get_nodes_and_load() -> get_node() -> gen_server:call({global, pool_master}, get_node). --spec pspawn(module(), atom(), [term()]) -> pid(). +-spec pspawn(Mod, Fun, Args) -> pid() when + Mod :: module(), + Fun :: atom(), + Args :: [term()]. pspawn(M, F, A) -> gen_server:call({global, pool_master}, {spawn, group_leader(), M, F, A}). --spec pspawn_link(module(), atom(), [term()]) -> pid(). +-spec pspawn_link(Mod, Fun, Args) -> pid() when + Mod :: module(), + Fun :: atom(), + Args :: [term()]. pspawn_link(M, F, A) -> P = pspawn(M, F, A), link(P), @@ -95,6 +107,9 @@ pspawn_link(M, F, A) -> start_nodes([], _, _) -> []; start_nodes([Host|Tail], Name, Args) -> case slave:start(Host, Name, Args) of + {error, {already_running, Node}} -> + io:format("Can't start node on host ~w due to ~w~n",[Host, {already_running, Node}]), + [Node | start_nodes(Tail, Name, Args)]; {error, R} -> io:format("Can't start node on host ~w due to ~w~n",[Host, R]), start_nodes(Tail, Name, Args); diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl index 4fb64a3353..02bcbb5a60 100644 --- a/lib/stdlib/src/proc_lib.erl +++ b/lib/stdlib/src/proc_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -40,77 +40,108 @@ -type priority_level() :: 'high' | 'low' | 'max' | 'normal'. -type spawn_option() :: 'link' + | 'monitor' | {'priority', priority_level()} | {'min_heap_size', non_neg_integer()} + | {'min_bin_vheap_size', non_neg_integer()} | {'fullsweep_after', non_neg_integer()}. --type dict_or_pid() :: pid() | [_] | {integer(), integer(), integer()}. +-type dict_or_pid() :: pid() + | (ProcInfo :: [_]) + | {X :: integer(), Y :: integer(), Z :: integer()}. %%----------------------------------------------------------------------------- --spec spawn(function()) -> pid(). +-spec spawn(Fun) -> pid() when + Fun :: function(). spawn(F) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn(?MODULE, init_p, [Parent,Ancestors,F]). --spec spawn(atom(), atom(), [term()]) -> pid(). +-spec spawn(Module, Function, Args) -> pid() when + Module :: module(), + Function :: atom(), + Args :: [term()]. spawn(M,F,A) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn(?MODULE, init_p, [Parent,Ancestors,M,F,A]). --spec spawn_link(function()) -> pid(). +-spec spawn_link(Fun) -> pid() when + Fun :: function(). spawn_link(F) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn_link(?MODULE, init_p, [Parent,Ancestors,F]). --spec spawn_link(atom(), atom(), [term()]) -> pid(). +-spec spawn_link(Module, Function, Args) -> pid() when + Module :: module(), + Function :: atom(), + Args :: [term()]. spawn_link(M,F,A) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn_link(?MODULE, init_p, [Parent,Ancestors,M,F,A]). --spec spawn(node(), function()) -> pid(). +-spec spawn(Node, Fun) -> pid() when + Node :: node(), + Fun :: function(). spawn(Node, F) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn(Node, ?MODULE, init_p, [Parent,Ancestors,F]). --spec spawn(node(), atom(), atom(), [term()]) -> pid(). +-spec spawn(Node, Module, Function, Args) -> pid() when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()]. + spawn(Node, M, F, A) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn(Node, ?MODULE, init_p, [Parent,Ancestors,M,F,A]). --spec spawn_link(node(), function()) -> pid(). +-spec spawn_link(Node, Fun) -> pid() when + Node :: node(), + Fun :: function(). spawn_link(Node, F) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn_link(Node, ?MODULE, init_p, [Parent,Ancestors,F]). --spec spawn_link(node(), atom(), atom(), [term()]) -> pid(). +-spec spawn_link(Node, Module, Function, Args) -> pid() when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()]. spawn_link(Node, M, F, A) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn_link(Node, ?MODULE, init_p, [Parent,Ancestors,M,F,A]). --spec spawn_opt(function(), [spawn_option()]) -> pid(). +-spec spawn_opt(Fun, SpawnOpts) -> pid() when + Fun :: function(), + SpawnOpts :: [spawn_option()]. + spawn_opt(F, Opts) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), check_for_monitor(Opts), erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,F],Opts). --spec spawn_opt(node(), function(), [spawn_option()]) -> pid(). +-spec spawn_opt(Node, Function, SpawnOpts) -> pid() when + Node :: node(), + Function :: function(), + SpawnOpts :: [spawn_option()]. spawn_opt(Node, F, Opts) when is_function(F) -> Parent = get_my_name(), @@ -118,7 +149,11 @@ spawn_opt(Node, F, Opts) when is_function(F) -> check_for_monitor(Opts), erlang:spawn_opt(Node, ?MODULE, init_p, [Parent,Ancestors,F], Opts). --spec spawn_opt(atom(), atom(), [term()], [spawn_option()]) -> pid(). +-spec spawn_opt(Module, Function, Args, SpawnOpts) -> pid() when + Module :: module(), + Function :: atom(), + Args :: [term()], + SpawnOpts :: [spawn_option()]. spawn_opt(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), @@ -126,7 +161,12 @@ spawn_opt(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) -> check_for_monitor(Opts), erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,M,F,A], Opts). --spec spawn_opt(node(), atom(), atom(), [term()], [spawn_option()]) -> pid(). +-spec spawn_opt(Node, Module, Function, Args, SpawnOpts) -> pid() when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()], + SpawnOpts :: [spawn_option()]. spawn_opt(Node, M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), @@ -144,7 +184,10 @@ check_for_monitor(SpawnOpts) -> false end. --spec hibernate(module(), atom(), [term()]) -> no_return(). +-spec hibernate(Module, Function, Args) -> no_return() when + Module :: module(), + Function :: atom(), + Args :: [term()]. hibernate(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> erlang:hibernate(?MODULE, wake_up, [M, F, A]). @@ -210,35 +253,65 @@ exit_p(Class, Reason) -> exit(Reason) end. --spec start(atom(), atom(), [term()]) -> term(). +-spec start(Module, Function, Args) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Ret :: term() | {error, Reason :: term()}. start(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> start(M, F, A, infinity). --spec start(atom(), atom(), [term()], timeout()) -> term(). +-spec start(Module, Function, Args, Time) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Time :: timeout(), + Ret :: term() | {error, Reason :: term()}. start(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) -> Pid = ?MODULE:spawn(M, F, A), sync_wait(Pid, Timeout). --spec start(atom(), atom(), [term()], timeout(), [spawn_option()]) -> term(). +-spec start(Module, Function, Args, Time, SpawnOpts) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Time :: timeout(), + SpawnOpts :: [spawn_option()], + Ret :: term() | {error, Reason :: term()}. start(M, F, A, Timeout, SpawnOpts) when is_atom(M), is_atom(F), is_list(A) -> Pid = ?MODULE:spawn_opt(M, F, A, SpawnOpts), sync_wait(Pid, Timeout). --spec start_link(atom(), atom(), [term()]) -> term(). +-spec start_link(Module, Function, Args) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Ret :: term() | {error, Reason :: term()}. start_link(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> start_link(M, F, A, infinity). --spec start_link(atom(), atom(), [term()], timeout()) -> term(). +-spec start_link(Module, Function, Args, Time) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Time :: timeout(), + Ret :: term() | {error, Reason :: term()}. start_link(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) -> Pid = ?MODULE:spawn_link(M, F, A), sync_wait(Pid, Timeout). --spec start_link(atom(),atom(),[term()],timeout(),[spawn_option()]) -> term(). +-spec start_link(Module, Function, Args, Time, SpawnOpts) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Time :: timeout(), + SpawnOpts :: [spawn_option()], + Ret :: term() | {error, Reason :: term()}. start_link(M,F,A,Timeout,SpawnOpts) when is_atom(M), is_atom(F), is_list(A) -> Pid = ?MODULE:spawn_opt(M, F, A, ensure_link(SpawnOpts)), @@ -267,13 +340,17 @@ flush(Pid) -> true end. --spec init_ack(pid(), term()) -> 'ok'. +-spec init_ack(Parent, Ret) -> 'ok' when + Parent :: pid(), + Ret :: term(). init_ack(Parent, Return) -> Parent ! {ack, self(), Return}, ok. --spec init_ack(term()) -> 'ok'. +-spec init_ack(Ret) -> 'ok' when + Ret :: term(). + init_ack(Return) -> [Parent|_] = get('$ancestors'), init_ack(Parent, Return). @@ -282,7 +359,11 @@ init_ack(Return) -> %% Fetch the initial call of a proc_lib spawned process. %% ----------------------------------------------------- --spec initial_call(dict_or_pid()) -> {atom(), atom(), [atom()]} | 'false'. +-spec initial_call(Process) -> {Module, Function, Args} | 'false' when + Process :: dict_or_pid(), + Module :: module(), + Function :: atom(), + Args :: [atom()]. initial_call(DictOrPid) -> case raw_initial_call(DictOrPid) of @@ -305,7 +386,11 @@ make_dummy_args(N, Acc) -> %% This function is typically called from c:i() and c:regs(). %% ----------------------------------------------------- --spec translate_initial_call(dict_or_pid()) -> mfa(). +-spec translate_initial_call(Process) -> {Module, Function, Arity} when + Process :: dict_or_pid(), + Module :: module(), + Function :: atom(), + Arity :: byte(). translate_initial_call(DictOrPid) -> case raw_initial_call(DictOrPid) of @@ -577,7 +662,8 @@ check(Res) -> Res. %%% Format (and write) a generated crash info structure. %%% ----------------------------------------------------------- --spec format([term()]) -> string(). +-spec format(CrashReport) -> string() when + CrashReport :: [term()]. format([OwnReport,LinkReport]) -> OwnFormat = format_report(OwnReport), diff --git a/lib/stdlib/src/proplists.erl b/lib/stdlib/src/proplists.erl index 6a45e0f868..e3eda5d932 100644 --- a/lib/stdlib/src/proplists.erl +++ b/lib/stdlib/src/proplists.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2010. All Rights Reserved. +%% Copyright Ericsson AB 2001-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -37,7 +37,7 @@ %% overriding the default settings, object properties, annotations, %% etc.</p> %% -%% @type property() = atom() | tuple() +%% % @type property() = atom() | tuple() -module(proplists). @@ -49,18 +49,13 @@ %% --------------------------------------------------------------------- --export_type([property/0]). +-export_type([property/0, proplist/0]). -type property() :: atom() | tuple(). - --type aliases() :: [{any(), any()}]. --type negations() :: [{any(), any()}]. --type expansions() :: [{property(), [any()]}]. +-type proplist() :: [property()]. %% --------------------------------------------------------------------- -%% @spec property(P::property()) -> property() -%% %% @doc Creates a normal form (minimal) representation of a property. If %% <code>P</code> is <code>{Key, true}</code> where <code>Key</code> is %% an atom, this returns <code>Key</code>, otherwise the whole term @@ -68,7 +63,8 @@ %% %% @see property/2 --spec property(property()) -> property(). +-spec property(Property) -> Property when + Property :: property(). property({Key, true}) when is_atom(Key) -> Key; @@ -76,8 +72,6 @@ property(Property) -> Property. -%% @spec property(Key::term(), Value::term()) -> property() -%% %% @doc Creates a normal form (minimal) representation of a simple %% key/value property. Returns <code>Key</code> if <code>Value</code> is %% <code>true</code> and <code>Key</code> is an atom, otherwise a tuple @@ -85,7 +79,10 @@ property(Property) -> %% %% @see property/1 --spec property(Key::term(), Value::term()) -> atom() | {term(), term()}. +-spec property(Key, Value) -> Property when + Key :: term(), + Value :: term(), + Property :: atom() | {term(), term()}. property(Key, true) when is_atom(Key) -> Key; @@ -95,14 +92,13 @@ property(Key, Value) -> %% --------------------------------------------------------------------- -%% @spec unfold(List::[term()]) -> [term()] -%% %% @doc Unfolds all occurences of atoms in <code>List</code> to tuples %% <code>{Atom, true}</code>. %% %% @see compact/1 --spec unfold(List::[term()]) -> [term()]. +-spec unfold(List) -> List when + List :: [term()]. unfold([P | Ps]) -> if is_atom(P) -> @@ -113,15 +109,14 @@ unfold([P | Ps]) -> unfold([]) -> []. -%% @spec compact(List::[term()]) -> [term()] -%% %% @doc Minimizes the representation of all entries in the list. This is %% equivalent to <code>[property(P) || P <- List]</code>. %% %% @see unfold/1 %% @see property/1 --spec compact(List::[property()]) -> [property()]. +-spec compact(List) -> List when + List :: [property()]. compact(List) -> [property(P) || P <- List]. @@ -129,8 +124,6 @@ compact(List) -> %% --------------------------------------------------------------------- -%% @spec lookup(Key::term(), List::[term()]) -> none | tuple() -%% %% @doc Returns the first entry associated with <code>Key</code> in %% <code>List</code>, if one exists, otherwise returns %% <code>none</code>. For an atom <code>A</code> in the list, the tuple @@ -140,7 +133,9 @@ compact(List) -> %% @see get_value/2 %% @see get_bool/2 --spec lookup(Key::term(), List::[term()]) -> 'none' | tuple(). +-spec lookup(Key, List) -> 'none' | tuple() when + Key :: term(), + List :: [term()]. lookup(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -154,15 +149,15 @@ lookup(Key, [P | Ps]) -> lookup(_Key, []) -> none. -%% @spec lookup_all(Key::term(), List::[term()]) -> [tuple()] -%% %% @doc Returns the list of all entries associated with <code>Key</code> %% in <code>List</code>. If no such entry exists, the result is the %% empty list. %% %% @see lookup/2 --spec lookup_all(Key::term(), List::[term()]) -> [tuple()]. +-spec lookup_all(Key, List) -> [tuple()] when + Key :: term(), + List :: [term()]. lookup_all(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -178,13 +173,13 @@ lookup_all(_Key, []) -> %% --------------------------------------------------------------------- -%% @spec is_defined(Key::term(), List::[term()]) -> boolean() -%% %% @doc Returns <code>true</code> if <code>List</code> contains at least %% one entry associated with <code>Key</code>, otherwise %% <code>false</code> is returned. --spec is_defined(Key::term(), List::[term()]) -> boolean(). +-spec is_defined(Key, List) -> boolean() when + Key :: term(), + List :: [term()]. is_defined(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -200,17 +195,15 @@ is_defined(_Key, []) -> %% --------------------------------------------------------------------- -%% @spec get_value(Key::term(), List::[term()]) -> term() %% @equiv get_value(Key, List, undefined) --spec get_value(Key::term(), List::[term()]) -> term(). +-spec get_value(Key, List) -> term() when + Key :: term(), + List :: List::[term()]. get_value(Key, List) -> get_value(Key, List, undefined). -%% @spec get_value(Key::term(), List::[term()], Default::term()) -> -%% term() -%% %% @doc Returns the value of a simple key/value property in %% <code>List</code>. If <code>lookup(Key, List)</code> would yield %% <code>{Key, Value}</code>, this function returns the corresponding @@ -221,7 +214,10 @@ get_value(Key, List) -> %% @see get_all_values/2 %% @see get_bool/2 --spec get_value(Key::term(), List::[term()], Default::term()) -> term(). +-spec get_value(Key, List, Default) -> term() when + Key :: term(), + List :: [term()], + Default :: term(). get_value(Key, [P | Ps], Default) -> if is_atom(P), P =:= Key -> @@ -240,8 +236,6 @@ get_value(Key, [P | Ps], Default) -> get_value(_Key, [], Default) -> Default. -%% @spec get_all_values(Key, List) -> [term()] -%% %% @doc Similar to <code>get_value/2</code>, but returns the list of %% values for <em>all</em> entries <code>{Key, Value}</code> in %% <code>List</code>. If no such entry exists, the result is the empty @@ -249,7 +243,9 @@ get_value(_Key, [], Default) -> %% %% @see get_value/2 --spec get_all_values(Key::term(), List::[term()]) -> [term()]. +-spec get_all_values(Key, List) -> [term()] when + Key :: term(), + List :: [term()]. get_all_values(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -267,8 +263,6 @@ get_all_values(Key, [P | Ps]) -> get_all_values(_Key, []) -> []. -%% @spec append_values(Key::term(), List::[term()]) -> [term()] -%% %% @doc Similar to <code>get_all_values/2</code>, but each value is %% wrapped in a list unless it is already itself a list, and the %% resulting list of lists is concatenated. This is often useful for @@ -278,7 +272,9 @@ get_all_values(_Key, []) -> %% %% @see get_all_values/2 --spec append_values(Key::term(), List::[term()]) -> [term()]. +-spec append_values(Key, List) -> List when + Key :: term(), + List :: [term()]. append_values(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -301,8 +297,6 @@ append_values(_Key, []) -> %% --------------------------------------------------------------------- -%% @spec get_bool(Key::term(), List::[term()]) -> boolean() -%% %% @doc Returns the value of a boolean key/value option. If %% <code>lookup(Key, List)</code> would yield <code>{Key, true}</code>, %% this function returns <code>true</code>; otherwise <code>false</code> @@ -311,7 +305,9 @@ append_values(_Key, []) -> %% @see lookup/2 %% @see get_value/2 --spec get_bool(Key::term(), List::[term()]) -> boolean(). +-spec get_bool(Key, List) -> boolean() when + Key :: term(), + List :: [term()]. get_bool(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -333,12 +329,11 @@ get_bool(_Key, []) -> %% --------------------------------------------------------------------- -%% @spec get_keys(List::[term()]) -> [term()] -%% %% @doc Returns an unordered list of the keys used in <code>List</code>, %% not containing duplicates. --spec get_keys(List::[term()]) -> [term()]. +-spec get_keys(List) -> [term()] when + List :: [term()]. get_keys(Ps) -> sets:to_list(get_keys(Ps, sets:new())). @@ -357,12 +352,12 @@ get_keys([], Keys) -> %% --------------------------------------------------------------------- -%% @spec delete(Key::term(), List::[term()]) -> [term()] -%% %% @doc Deletes all entries associated with <code>Key</code> from %% <code>List</code>. --spec delete(Key::term(), List::[term()]) -> [term()]. +-spec delete(Key, List) -> List when + Key :: term(), + List::[term()]. delete(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -378,11 +373,6 @@ delete(_, []) -> %% --------------------------------------------------------------------- -%% @spec substitute_aliases(Aliases, List::[term()]) -> [term()] -%% -%% Aliases = [{Key, Key}] -%% Key = term() -%% %% @doc Substitutes keys of properties. For each entry in %% <code>List</code>, if it is associated with some key <code>K1</code> %% such that <code>{K1, K2}</code> occurs in <code>Aliases</code>, the @@ -398,7 +388,10 @@ delete(_, []) -> %% @see substitute_negations/2 %% @see normalize/2 --spec substitute_aliases(aliases(), List::[term()]) -> [term()]. +-spec substitute_aliases(Aliases, List) -> List when + Aliases :: [{Key, Key}], + Key :: term(), + List::[term()]. substitute_aliases(As, Props) -> [substitute_aliases_1(As, P) || P <- Props]. @@ -417,11 +410,6 @@ substitute_aliases_1([], P) -> %% --------------------------------------------------------------------- -%% @spec substitute_negations(Negations, List::[term()]) -> [term()] -%% -%% Negations = [{Key, Key}] -%% Key = term() -%% %% @doc Substitutes keys of boolean-valued properties and simultaneously %% negates their values. For each entry in <code>List</code>, if it is %% associated with some key <code>K1</code> such that <code>{K1, @@ -443,7 +431,10 @@ substitute_aliases_1([], P) -> %% @see substitute_aliases/2 %% @see normalize/2 --spec substitute_negations(negations(), List::[term()]) -> [term()]. +-spec substitute_negations(Negations, List) -> List when + Negations :: [{Key, Key}], + Key :: term(), + List :: [term()]. substitute_negations(As, Props) -> [substitute_negations_1(As, P) || P <- Props]. @@ -472,10 +463,6 @@ substitute_negations_1([], P) -> %% --------------------------------------------------------------------- -%% @spec expand(Expansions, List::[term()]) -> [term()] -%% -%% Expansions = [{property(), [term()]}] -%% %% @doc Expands particular properties to corresponding sets of %% properties (or other terms). For each pair <code>{Property, %% Expansion}</code> in <code>Expansions</code>, if <code>E</code> is @@ -510,7 +497,9 @@ substitute_negations_1([], P) -> %% %% @see normalize/2 --spec expand(Expansions::expansions(), [term()]) -> [term()]. +-spec expand(Expansions, List) -> List when + Expansions :: [{Property :: property(), Expansion :: [term()]}], + List :: [term()]. expand(Es, Ps) when is_list(Ps) -> Es1 = [{property(P), V} || {P, V} <- Es], @@ -589,15 +578,6 @@ flatten([]) -> %% --------------------------------------------------------------------- -%% @spec normalize(List::[term()], Stages::[Operation]) -> [term()] -%% -%% Operation = {aliases, Aliases} | {negations, Negations} -%% | {expand, Expansions} -%% Aliases = [{Key, Key}] -%% Negations = [{Key, Key}] -%% Key = term() -%% Expansions = [{property(), [term()]}] -%% %% @doc Passes <code>List</code> through a sequence of %% substitution/expansion stages. For an <code>aliases</code> operation, %% the function <code>substitute_aliases/2</code> is applied using the @@ -619,11 +599,15 @@ flatten([]) -> %% @see expand/2 %% @see compact/1 --type operation() :: {'aliases', aliases()} - | {'negations', negations()} - | {'expand', expansions()}. - --spec normalize(List::[term()], Stages::[operation()]) -> [term()]. +-spec normalize(List, Stages) -> List when + List :: [term()], + Stages :: [Operation], + Operation :: {'aliases', Aliases} + | {'negations', Negations} + | {'expand', Expansions}, + Aliases :: [{Key, Key}], + Negations :: [{Key, Key}], + Expansions :: [{Property :: property(), Expansion :: [term()]}]. normalize(L, [{aliases, As} | Xs]) -> normalize(substitute_aliases(As, L), Xs); @@ -636,10 +620,6 @@ normalize(L, []) -> %% --------------------------------------------------------------------- -%% @spec split(List::[term()], Keys::[term()]) -> {Lists, Rest} -%% Lists = [[term()]] -%% Rest = [term()] -%% %% @doc Partitions <code>List</code> into a list of sublists and a %% remainder. <code>Lists</code> contains one sublist for each key in %% <code>Keys</code>, in the corresponding order. The relative order of @@ -654,7 +634,11 @@ normalize(L, []) -> %% {[[a], [{b, 5}, b],[{c, 2}, {c, 3, 4}]], [{e, 1}, d]}</pre> %% </p> --spec split(List::[term()], Keys::[term()]) -> {[[term()]], [term()]}. +-spec split(List, Keys) -> {Lists, Rest} when + List :: [term()], + Keys :: [term()], + Lists :: [[term()]], + Rest :: [term()]. split(List, Keys) -> {Store, Rest} = split(List, dict:from_list([{K, []} || K <- Keys]), []), diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl index bc6944e520..f5e180b4bd 100644 --- a/lib/stdlib/src/qlc.erl +++ b/lib/stdlib/src/qlc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -60,7 +60,7 @@ -record(qlc_table, % qlc:table/2 {trav_fun, % traverse fun - trav_MS, % bool(); true iff traverse fun takes a match spec + trav_MS, % boolean(); true iff traverse fun takes a match spec pre_fun, post_fun, info_fun, @@ -110,12 +110,12 @@ -record(qlc_cursor, {c}). -record(qlc_opt, - {unique = false, % bool() - cache = false, % bool() | list (true~ets, false~no) + {unique = false, % boolean() + cache = false, % boolean() | list (true~ets, false~no) max_lookup = -1, % int() >= 0 | -1 (represents infinity) join = any, % any | nested_loop | merge | lookup tmpdir = "", % global tmpdir - lookup = any, % any | bool() + lookup = any, % any | boolean() max_list = ?MAX_LIST_SIZE, % int() >= 0 tmpdir_usage = allowed % allowed | not_allowed % | warning_msg | error_msg | info_msg @@ -123,7 +123,9 @@ -record(setup, {parent}). --define(THROWN_ERROR, {?MODULE, throw_error, _}). +-define(THROWN_ERROR, {?MODULE, throw_error, _, _}). + +-export_type([query_handle/0]). %%% A query handle is a tuple {qlc_handle, Handle} where Handle is one %%% of #qlc_append, #qlc_table, #qlc_sort, and #qlc_lc. @@ -144,6 +146,35 @@ get_handle(_) -> %%% Exported functions %%% +-type(query_list_comprehension() :: term()). +-opaque(query_cursor() :: {qlc_cursor, term()}). +-opaque(query_handle() :: {qlc_handle, term()}). +-type(query_handle_or_list() :: query_handle() | list()). +-type(answers() :: [answer()]). +-type(answer() :: term()). +-type(abstract_expr() :: erl_parse:abstract_expr()). +-type(match_expression() :: ets:match_spec()). +-type(spawn_options() :: default | [proc_lib:spawn_option()]). +-type(sort_options() :: [sort_option()] | sort_option()). +-type(sort_option() :: {compressed, boolean()} + | {no_files, no_files()} + | {order, order()} + | {size, pos_integer()} + | {tmpdir, tmp_directory()} + | {unique, boolean()}). +-type(order() :: ascending | descending | order_fun()). +-type(order_fun() :: fun((term(), term()) -> boolean())). +-type(tmp_directory() :: [] | file:name()). +-type(no_files() :: pos_integer()). % > 1 +-type(key_pos() :: pos_integer() | [pos_integer()]). +-type(max_list_size() :: non_neg_integer()). +-type(cache() :: ets | list | no). +-type(tmp_file_usage() :: allowed | not_allowed | info_msg + | warning_msg | error_msg). + +-spec(append(QHL) -> QH when + QHL :: [query_handle_or_list()], + QH :: query_handle()). append(QHs) -> Hs = [case get_handle(QH) of badarg -> erlang:error(badarg, [QHs]); @@ -151,6 +182,10 @@ append(QHs) -> end || QH <- QHs], #qlc_handle{h = #qlc_append{hl = Hs}}. +-spec(append(QH1, QH2) -> QH3 when + QH1 :: query_handle_or_list(), + QH2 :: query_handle_or_list(), + QH3 :: query_handle()). append(QH1, QH2) -> Hs = [case get_handle(QH) of badarg -> erlang:error(badarg, [QH1, QH2]); @@ -158,9 +193,22 @@ append(QH1, QH2) -> end || QH <- [QH1, QH2]], #qlc_handle{h = #qlc_append{hl = Hs}}. +-spec(cursor(QH) -> Cursor when + QH :: query_handle_or_list(), + Cursor :: query_cursor()). cursor(QH) -> cursor(QH, []). +-spec(cursor(QH, Options) -> Cursor when + QH :: query_handle_or_list(), + Options :: [Option] | Option, + Option :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {spawn_options, spawn_options()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + Cursor :: query_cursor()). cursor(QH, Options) -> case {options(Options, [unique_all, cache_all, tmpdir, spawn_options, max_list_size, @@ -179,6 +227,8 @@ cursor(QH, Options) -> end end. +-spec(delete_cursor(QueryCursor) -> ok when + QueryCursor :: query_cursor()). delete_cursor(#qlc_cursor{c = {_, Owner}}=C) when Owner =/= self() -> erlang:error(not_cursor_owner, [C]); delete_cursor(#qlc_cursor{c = {Pid, _}}) -> @@ -186,15 +236,47 @@ delete_cursor(#qlc_cursor{c = {Pid, _}}) -> delete_cursor(T) -> erlang:error(badarg, [T]). +-spec(e(QH) -> Answers | Error when + QH :: query_handle_or_list(), + Answers :: answers(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). e(QH) -> eval(QH, []). +-spec(e(QH, Options) -> Answers | Error when + QH :: query_handle_or_list(), + Options :: [Option] | Option, + Option :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + Answers :: answers(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). e(QH, Options) -> eval(QH, Options). +-spec(eval(QH) -> Answers | Error when + QH :: query_handle_or_list(), + Answers :: answers(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). eval(QH) -> eval(QH, []). +-spec(eval(QH, Options) -> Answers | Error when + QH :: query_handle_or_list(), + Answers :: answers(), + Options :: [Option] | Option, + Option :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). eval(QH, Options) -> case {options(Options, [unique_all, cache_all, tmpdir, max_list_size, tmpdir_usage]), @@ -226,9 +308,35 @@ eval(QH, Options) -> end end. +-spec(fold(Function, Acc0, QH) -> + Acc1 | Error when + QH :: query_handle_or_list(), + Function :: fun((answer(), AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). fold(Fun, Acc0, QH) -> fold(Fun, Acc0, QH, []). +-spec(fold(Function, Acc0, QH, Options) -> + Acc1 | Error when + QH :: query_handle_or_list(), + Function :: fun((answer(), AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Options :: [Option] | Option, + Option :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). fold(Fun, Acc0, QH, Options) -> case {options(Options, [unique_all, cache_all, tmpdir, max_list_size, tmpdir_usage]), @@ -258,6 +366,9 @@ fold(Fun, Acc0, QH, Options) -> end end. +-spec(format_error(Error) -> Chars when + Error :: {error, module(), term()}, + Chars :: io_lib:chars()). format_error(not_a_query_list_comprehension) -> io_lib:format("argument is not a query list comprehension", []); format_error({used_generator_variable, V}) -> @@ -295,9 +406,29 @@ format_error({error, Module, Reason}) -> format_error(E) -> io_lib:format("~p~n", [E]). +-spec(info(QH) -> Info when + QH :: query_handle_or_list(), + Info :: abstract_expr() | string()). info(QH) -> info(QH, []). +-spec(info(QH, Options) -> Info when + QH :: query_handle_or_list(), + Options :: [Option] | Option, + Option :: EvalOption | ReturnOption, + EvalOption :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + ReturnOption :: {depth, Depth} + | {flat, boolean()} + | {format, Format} + | {n_elements, NElements}, + Depth :: infinity | non_neg_integer(), + Format :: abstract_code | string, + NElements :: infinity | pos_integer(), + Info :: abstract_expr() | string()). info(QH, Options) -> case {options(Options, [unique_all, cache_all, flat, format, n_elements, depth, tmpdir, max_list_size, tmpdir_usage]), @@ -333,9 +464,18 @@ info(QH, Options) -> end end. +-spec(keysort(KeyPos, QH1) -> QH2 when + KeyPos :: key_pos(), + QH1 :: query_handle_or_list(), + QH2 :: query_handle()). keysort(KeyPos, QH) -> keysort(KeyPos, QH, []). +-spec(keysort(KeyPos, QH1, SortOptions) -> QH2 when + KeyPos :: key_pos(), + SortOptions :: sort_options(), + QH1 :: query_handle_or_list(), + QH2 :: query_handle()). keysort(KeyPos, QH, Options) -> case {is_keypos(KeyPos), options(Options, [tmpdir, order, unique, compressed, @@ -354,9 +494,22 @@ keysort(KeyPos, QH, Options) -> -define(DEFAULT_NUM_OF_ANSWERS, 10). +-spec(next_answers(QueryCursor) -> + Answers | Error when + QueryCursor :: query_cursor(), + Answers :: answers(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). next_answers(C) -> next_answers(C, ?DEFAULT_NUM_OF_ANSWERS). +-spec(next_answers(QueryCursor, NumberOfAnswers) -> + Answers | Error when + QueryCursor :: query_cursor(), + Answers :: answers(), + NumberOfAnswers :: all_remaining | pos_integer(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). next_answers(#qlc_cursor{c = {_, Owner}}=C, NumOfAnswers) when Owner =/= self() -> erlang:error(not_cursor_owner, [C, NumOfAnswers]); @@ -370,14 +523,35 @@ next_answers(#qlc_cursor{c = {Pid, _}}=C, NumOfAnswers) -> next_answers(T1, T2) -> erlang:error(badarg, [T1, T2]). +-spec(parse_transform(Forms, Options) -> Forms2 when + Forms :: [erl_parse:abstract_form()], + Forms2 :: [erl_parse:abstract_form()], + Options :: [Option], + Option :: type_checker | compile:option()). + parse_transform(Forms, Options) -> qlc_pt:parse_transform(Forms, Options). %% The funcspecs qlc:q/1 and qlc:q/2 are known by erl_eval.erl and %% erl_lint.erl. +-spec(q(QLC) -> QH when + QLC :: query_list_comprehension(), + QH :: query_handle()). q(QLC_lc) -> q(QLC_lc, []). +-spec(q(QLC, Options) -> QH when + QH :: query_handle(), + Options :: [Option] | Option, + Option :: {max_lookup, MaxLookup} + | {cache, cache()} | cache + | {join, Join} + | {lookup, Lookup} + | {unique, boolean()} | unique, + MaxLookup :: non_neg_integer() | infinity, + Join :: any | lookup | merge | nested_loop, + Lookup :: boolean() | any, + QLC :: query_list_comprehension()). q(#qlc_lc{}=QLC_lc, Options) -> case options(Options, [unique, cache, max_lookup, join, lookup]) of [Unique, Cache, Max, Join, Lookup] -> @@ -390,9 +564,16 @@ q(#qlc_lc{}=QLC_lc, Options) -> q(T1, T2) -> erlang:error(badarg, [T1, T2]). +-spec(sort(QH1) -> QH2 when + QH1 :: query_handle_or_list(), + QH2 :: query_handle()). sort(QH) -> sort(QH, []). +-spec(sort(QH1, SortOptions) -> QH2 when + SortOptions :: sort_options(), + QH1 :: query_handle_or_list(), + QH2 :: query_handle()). sort(QH, Options) -> case {options(Options, [tmpdir, order, unique, compressed, size, no_files]), get_handle(QH)} of @@ -406,14 +587,47 @@ sort(QH, Options) -> end. %% Note that the generated code is evaluated by (the slow) erl_eval. +-spec(string_to_handle(QueryString) -> QH | Error when + QueryString :: string(), + QH :: query_handle(), + Error :: {error, module(), Reason}, + Reason :: erl_parse:error_info() | erl_scan:error_info()). string_to_handle(Str) -> string_to_handle(Str, []). +-spec(string_to_handle(QueryString, Options) -> QH | Error when + QueryString :: string(), + Options :: [Option] | Option, + Option :: {max_lookup, MaxLookup} + | {cache, cache()} | cache + | {join, Join} + | {lookup, Lookup} + | {unique, boolean()} | unique, + MaxLookup :: non_neg_integer() | infinity, + Join :: any | lookup | merge | nested_loop, + Lookup :: boolean() | any, + QH :: query_handle(), + Error :: {error, module(), Reason}, + Reason :: erl_parse:error_info() | erl_scan:error_info()). string_to_handle(Str, Options) -> - string_to_handle(Str, Options, []). - -string_to_handle(Str, Options, Bindings) when is_list(Str), - is_list(Bindings) -> + string_to_handle(Str, Options, erl_eval:new_bindings()). + +-spec(string_to_handle(QueryString, Options, Bindings) -> QH | Error when + QueryString :: string(), + Options :: [Option] | Option, + Option :: {max_lookup, MaxLookup} + | {cache, cache()} | cache + | {join, Join} + | {lookup, Lookup} + | {unique, boolean()} | unique, + MaxLookup :: non_neg_integer() | infinity, + Join :: any | lookup | merge | nested_loop, + Lookup :: boolean() | any, + Bindings :: erl_eval:binding_struct(), + QH :: query_handle(), + Error :: {error, module(), Reason}, + Reason :: erl_parse:error_info() | erl_scan:error_info()). +string_to_handle(Str, Options, Bindings) when is_list(Str) -> case options(Options, [unique, cache, max_lookup, join, lookup]) of badarg -> erlang:error(badarg, [Str, Options, Bindings]); @@ -447,6 +661,51 @@ string_to_handle(Str, Options, Bindings) when is_list(Str), string_to_handle(T1, T2, T3) -> erlang:error(badarg, [T1, T2, T3]). +-spec(table(TraverseFun, Options) -> QH when + TraverseFun :: TraverseFun0 | TraverseFun1, + TraverseFun0 :: fun(() -> TraverseResult), + TraverseFun1 :: fun((match_expression()) -> TraverseResult), + TraverseResult :: Objects | term(), + Objects :: [] | [term() | ObjectList], + ObjectList :: TraverseFun0 | Objects, + Options :: [Option] | Option, + Option :: {format_fun, FormatFun} + | {info_fun, InfoFun} + | {lookup_fun, LookupFun} + | {parent_fun, ParentFun} + | {post_fun, PostFun} + | {pre_fun, PreFun} + | {key_equality, KeyComparison}, + FormatFun :: undefined | fun((SelectedObjects) -> FormatedTable), + SelectedObjects :: all + | {all, NElements, DepthFun} + | {match_spec, match_expression()} + | {lookup, Position, Keys} + | {lookup, Position, Keys, NElements, DepthFun}, + NElements :: infinity | pos_integer(), + DepthFun :: fun((term()) -> term()), + FormatedTable :: {Mod, Fun, Args} + | abstract_expr() + | string(), + InfoFun :: undefined | fun((InfoTag) -> InfoValue), + InfoTag :: indices | is_unique_objects | keypos | num_of_objects, + InfoValue :: undefined | term(), + LookupFun :: undefined | fun((Position, Keys) -> LookupResult), + LookupResult :: [term()] | term(), + ParentFun :: undefined | fun(() -> ParentFunValue), + PostFun :: undefined | fun(() -> term()), + PreFun :: undefined | fun((PreArgs) -> term()), + PreArgs :: [PreArg], + PreArg :: {parent_value, ParentFunValue} | {stop_fun, StopFun}, + ParentFunValue :: undefined | term(), + StopFun :: undefined | fun(() -> term()), + KeyComparison :: '=:=' | '==', + Position :: pos_integer(), + Keys :: [term()], + Mod :: atom(), + Fun :: atom(), + Args :: [term()], + QH :: query_handle()). table(TraverseFun, Options) when is_function(TraverseFun) -> case {is_function(TraverseFun, 0), IsFun1 = is_function(TraverseFun, 1)} of @@ -472,6 +731,11 @@ table(TraverseFun, Options) when is_function(TraverseFun) -> table(T1, T2) -> erlang:error(badarg, [T1, T2]). +-spec(transform_from_evaluator(LC, Bs) -> Expr when + LC :: abstract_expr(), + Expr :: abstract_expr(), + Bs :: erl_eval:binding_struct()). + transform_from_evaluator(LC, Bs0) -> qlc_pt:transform_from_evaluator(LC, Bs0). @@ -722,8 +986,8 @@ listify(T) -> %% Optimizations to be carried out. -record(optz, - {unique = false, % bool() - cache = false, % bool() | list + {unique = false, % boolean() + cache = false, % boolean() | list join_option = any, % constraint set by the 'join' option fast_join = no, % no | #qlc_join. 'no' means nested loop. opt % #qlc_opt @@ -756,8 +1020,8 @@ listify(T) -> lu_skip_quals = [], % qualifiers to skip due to lookup join = {[],[]}, % {Lookup, Merge} n_objs = undefined, % for join (not used yet) - is_unique_objects = false, % bool() - is_cached = false % bool() (true means 'ets' or 'list') + is_unique_objects = false, % boolean() + is_cached = false % boolean() (true means 'ets' or 'list') }). %%% Cursor process functions. @@ -1143,20 +1407,20 @@ monitor_request(Pid, Req) -> %% QueryDesc = {qlc, TemplateDesc, [QualDesc], [QueryOpt]} %% | {table, TableDesc} %% | {append, [QueryDesc]} -%% | {sort, QueryDesc, [SortOption]} -%% | {keysort, KeyPos, QueryDesc, [SortOption]} +%% | {sort, QueryDesc, [sort_option()]} +%% | {keysort, key_pos(), QueryDesc, [sort_option()]} %% | {list, list()} -%% | {list, QueryDesc, MatchExpression} +%% | {list, QueryDesc, match_expression()} %% TableDesc = {Mod, Fun, Args} -%% | AbstractExpression -%% | character_list() +%% | erl_parse:abstract_expr() +%% | string() %% Mod = module() %% Fun = atom() %% Args = [term()] %% QualDesc = FilterDesc %% | {generate, PatternDesc, QueryDesc} -%% QueryOpt = {cache, bool()} | cache -%% | {unique, bool()} | unique +%% QueryOpt = {cache, boolean()} | cache +%% | {unique, boolean()} | unique %% FilterDesc = PatternDesc = TemplateDesc = binary() le_info(#prepared{qh = #simple_qlc{le = LE, p = P, line = L, optz = Optz}}, @@ -3437,7 +3701,8 @@ lookup_join(F1, C1, LuF, C2, Rev) -> maybe_error_logger(allowed, _) -> ok; maybe_error_logger(Name, Why) -> - [_, _, {?MODULE,maybe_error_logger,_} | Stacktrace] = expand_stacktrace(), + [_, _, {?MODULE,maybe_error_logger,_,_} | Stacktrace] = + expand_stacktrace(), Trimmer = fun(M, _F, _A) -> M =:= erl_eval end, Formater = fun(Term, I) -> io_lib:print(Term, I, 80, -1) end, X = lib:format_stacktrace(1, Stacktrace, Trimmer, Formater), @@ -3456,7 +3721,7 @@ expand_stacktrace() -> expand_stacktrace(D) -> _ = erlang:system_flag(backtrace_depth, D), {'EXIT', {foo, Stacktrace}} = (catch erlang:error(foo)), - L = lists:takewhile(fun({M,_,_}) -> M =/= ?MODULE + L = lists:takewhile(fun({M,_,_,_}) -> M =/= ?MODULE end, lists:reverse(Stacktrace)), if length(L) < 3 andalso length(Stacktrace) =:= D -> diff --git a/lib/stdlib/src/qlc_pt.erl b/lib/stdlib/src/qlc_pt.erl index 24378a0698..21504d707b 100644 --- a/lib/stdlib/src/qlc_pt.erl +++ b/lib/stdlib/src/qlc_pt.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -63,6 +63,12 @@ %%% Exported functions %%% +-spec(parse_transform(Forms, Options) -> Forms2 when + Forms :: [erl_parse:abstract_form()], + Forms2 :: [erl_parse:abstract_form()], + Options :: [Option], + Option :: type_checker | compile:option()). + parse_transform(Forms, Options) -> ?DEBUG("qlc Parse Transform~n", []), State = #state{imp = is_qlc_q_imported(Forms), @@ -96,10 +102,20 @@ parse_transform(Forms, Options) -> end end. +-spec(transform_from_evaluator(LC, Bs) -> Expr when + LC :: erl_parse:abstract_expr(), + Expr :: erl_parse:abstract_expr(), + Bs :: erl_eval:binding_struct()). + transform_from_evaluator(LC, Bindings) -> ?DEBUG("qlc Parse Transform (Evaluator Version)~n", []), transform_expression(LC, Bindings, false). +-spec(transform_expression(LC, Bs) -> Expr when + LC :: erl_parse:abstract_expr(), + Expr :: erl_parse:abstract_expr(), + Bs :: erl_eval:binding_struct()). + transform_expression(LC, Bindings) -> transform_expression(LC, Bindings, true). diff --git a/lib/stdlib/src/queue.erl b/lib/stdlib/src/queue.erl index c09079e8d2..afe917b151 100644 --- a/lib/stdlib/src/queue.erl +++ b/lib/stdlib/src/queue.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -56,14 +56,14 @@ new() -> {[],[]}. %{RearList,FrontList} %% O(1) --spec is_queue(term()) -> boolean(). +-spec is_queue(Term :: term()) -> boolean(). is_queue({R,F}) when is_list(R), is_list(F) -> true; is_queue(_) -> false. %% O(1) --spec is_empty(queue()) -> boolean(). +-spec is_empty(Q :: queue()) -> boolean(). is_empty({[],[]}) -> true; is_empty({In,Out}) when is_list(In), is_list(Out) -> @@ -72,14 +72,14 @@ is_empty(Q) -> erlang:error(badarg, [Q]). %% O(len(Q)) --spec len(queue()) -> non_neg_integer(). +-spec len(Q :: queue()) -> non_neg_integer(). len({R,F}) when is_list(R), is_list(F) -> length(R)+length(F); len(Q) -> erlang:error(badarg, [Q]). %% O(len(Q)) --spec to_list(queue()) -> list(). +-spec to_list(Q :: queue()) -> list(). to_list({In,Out}) when is_list(In), is_list(Out) -> Out++lists:reverse(In, []); to_list(Q) -> @@ -88,7 +88,7 @@ to_list(Q) -> %% Create queue from list %% %% O(length(L)) --spec from_list(list()) -> queue(). +-spec from_list(L :: list()) -> queue(). from_list(L) when is_list(L) -> f2r(L); from_list(L) -> @@ -97,7 +97,7 @@ from_list(L) -> %% Return true or false depending on if element is in queue %% %% O(length(Q)) worst case --spec member(term(), queue()) -> boolean(). +-spec member(Item :: term(), Q :: queue()) -> boolean(). member(X, {R,F}) when is_list(R), is_list(F) -> lists:member(X, R) orelse lists:member(X, F); member(X, Q) -> @@ -110,7 +110,7 @@ member(X, Q) -> %% Put at least one element in each list, if it is cheap %% %% O(1) --spec in(term(), queue()) -> queue(). +-spec in(Item :: term(), Q1 :: queue()) -> Q2 :: queue(). in(X, {[_]=In,[]}) -> {[X], In}; in(X, {In,Out}) when is_list(In), is_list(Out) -> @@ -122,7 +122,7 @@ in(X, Q) -> %% Put at least one element in each list, if it is cheap %% %% O(1) --spec in_r(term(), queue()) -> queue(). +-spec in_r(Item :: term(), Q1 :: queue()) -> Q2 :: queue(). in_r(X, {[],[_]=F}) -> {F,[X]}; in_r(X, {R,F}) when is_list(R), is_list(F) -> @@ -133,7 +133,9 @@ in_r(X, Q) -> %% Take from head/front %% %% O(1) amortized, O(len(Q)) worst case --spec out(queue()) -> {'empty' | {'value',term()}, queue()}. +-spec out(Q1 :: queue()) -> + {{value, Item :: term()}, Q2 :: queue()} | + {empty, Q1 :: queue()}. out({[],[]}=Q) -> {empty,Q}; out({[V],[]}) -> @@ -151,7 +153,9 @@ out(Q) -> %% Take from tail/rear %% %% O(1) amortized, O(len(Q)) worst case --spec out_r(queue()) -> {'empty' | {'value',term()}, queue()}. +-spec out_r(Q1 :: queue()) -> + {{value, Item :: term()}, Q2 :: queue()} | + {empty, Q1 :: queue()}. out_r({[],[]}=Q) -> {empty,Q}; out_r({[],[V]}) -> @@ -172,7 +176,7 @@ out_r(Q) -> %% Return the first element in the queue %% %% O(1) since the queue is supposed to be well formed --spec get(queue()) -> term(). +-spec get(Q :: queue()) -> Item :: term(). get({[],[]}=Q) -> erlang:error(empty, [Q]); get({R,F}) when is_list(R), is_list(F) -> @@ -191,7 +195,7 @@ get([_|R], []) -> % malformed queue -> O(len(Q)) %% Return the last element in the queue %% %% O(1) since the queue is supposed to be well formed --spec get_r(queue()) -> term(). +-spec get_r(Q :: queue()) -> Item :: term(). get_r({[],[]}=Q) -> erlang:error(empty, [Q]); get_r({[H|_],F}) when is_list(F) -> @@ -206,7 +210,7 @@ get_r(Q) -> %% Return the first element in the queue %% %% O(1) since the queue is supposed to be well formed --spec peek(queue()) -> 'empty' | {'value',term()}. +-spec peek(Q :: queue()) -> empty | {value,Item :: term()}. peek({[],[]}) -> empty; peek({R,[H|_]}) when is_list(R) -> @@ -221,7 +225,7 @@ peek(Q) -> %% Return the last element in the queue %% %% O(1) since the queue is supposed to be well formed --spec peek_r(queue()) -> 'empty' | {'value',term()}. +-spec peek_r(Q :: queue()) -> empty | {value,Item :: term()}. peek_r({[],[]}) -> empty; peek_r({[H|_],F}) when is_list(F) -> @@ -236,7 +240,7 @@ peek_r(Q) -> %% Remove the first element and return resulting queue %% %% O(1) amortized --spec drop(queue()) -> queue(). +-spec drop(Q1 :: queue()) -> Q2 :: queue(). drop({[],[]}=Q) -> erlang:error(empty, [Q]); drop({[_],[]}) -> @@ -254,7 +258,7 @@ drop(Q) -> %% Remove the last element and return resulting queue %% %% O(1) amortized --spec drop_r(queue()) -> queue(). +-spec drop_r(Q1 :: queue()) -> Q2 :: queue(). drop_r({[],[]}=Q) -> erlang:error(empty, [Q]); drop_r({[],[_]}) -> @@ -275,7 +279,7 @@ drop_r(Q) -> %% Return reversed queue %% %% O(1) --spec reverse(queue()) -> queue(). +-spec reverse(Q1 :: queue()) -> Q2 :: queue(). reverse({R,F}) when is_list(R), is_list(F) -> {F,R}; reverse(Q) -> @@ -285,7 +289,7 @@ reverse(Q) -> %% %% Q2 empty: O(1) %% else: O(len(Q1)) --spec join(queue(), queue()) -> queue(). +-spec join(Q1 :: queue(), Q2 :: queue()) -> Q3 :: queue(). join({R,F}=Q, {[],[]}) when is_list(R), is_list(F) -> Q; join({[],[]}, {R,F}=Q) when is_list(R), is_list(F) -> @@ -299,7 +303,8 @@ join(Q1, Q2) -> %% %% N = 0..len(Q) %% O(max(N, len(Q))) --spec split(non_neg_integer(), queue()) -> {queue(),queue()}. +-spec split(N :: non_neg_integer(), Q1 :: queue()) -> + {Q2 :: queue(),Q3 :: queue()}. split(0, {R,F}=Q) when is_list(R), is_list(F) -> {{[],[]},Q}; split(N, {R,F}=Q) when is_integer(N), N >= 1, is_list(R), is_list(F) -> @@ -340,7 +345,8 @@ split_r1_to_f2(N, [X|R1], F1, R2, F2) -> %% %% Fun(_) -> List: O(length(List) * len(Q)) %% else: O(len(Q) --spec filter(fun((term()) -> boolean() | list()), queue()) -> queue(). +-spec filter(Fun, Q1 :: queue()) -> Q2 :: queue() when + Fun :: fun((Item :: term()) -> boolean() | list()). filter(Fun, {R0,F0}) when is_function(Fun, 1), is_list(R0), is_list(F0) -> F = filter_f(Fun, F0), R = filter_r(Fun, R0), @@ -416,7 +422,7 @@ filter_r(Fun, [X|R0]) -> %% Cons to head %% --spec cons(term(), queue()) -> queue(). +-spec cons(Item :: term(), Q1 :: queue()) -> Q2 :: queue(). cons(X, Q) -> in_r(X, Q). @@ -425,7 +431,7 @@ cons(X, Q) -> %% Return the first element in the queue %% %% O(1) since the queue is supposed to be well formed --spec head(queue()) -> term(). +-spec head(Q :: queue()) -> Item :: term(). head({[],[]}=Q) -> erlang:error(empty, [Q]); head({R,F}) when is_list(R), is_list(F) -> @@ -435,7 +441,7 @@ head(Q) -> %% Remove head element and return resulting queue %% --spec tail(queue()) -> queue(). +-spec tail(Q1 :: queue()) -> Q2 :: queue(). tail(Q) -> drop(Q). @@ -443,22 +449,22 @@ tail(Q) -> %% Cons to tail %% --spec snoc(queue(), term()) -> queue(). +-spec snoc(Q1 :: queue(), Item :: term()) -> Q2 :: queue(). snoc(Q, X) -> in(X, Q). %% Return last element --spec daeh(queue()) -> term(). +-spec daeh(Q :: queue()) -> Item :: term(). daeh(Q) -> get_r(Q). --spec last(queue()) -> term(). +-spec last(Q :: queue()) -> Item :: term(). last(Q) -> get_r(Q). %% Remove last element and return resulting queue --spec liat(queue()) -> queue(). +-spec liat(Q1 :: queue()) -> Q2 :: queue(). liat(Q) -> drop_r(Q). --spec lait(queue()) -> queue(). +-spec lait(Q1 :: queue()) -> Q2 :: queue(). lait(Q) -> drop_r(Q). %% Oops, mis-spelled 'tail' reversed. Forget this one. --spec init(queue()) -> queue(). +-spec init(Q1 :: queue()) -> Q2 :: queue(). init(Q) -> drop_r(Q). %%-------------------------------------------------------------------------- diff --git a/lib/stdlib/src/random.erl b/lib/stdlib/src/random.erl index 606edbb1fb..d7b51a151c 100644 --- a/lib/stdlib/src/random.erl +++ b/lib/stdlib/src/random.erl @@ -57,7 +57,10 @@ seed() -> %% seed({A1, A2, A3}) %% Seed random number generation --spec seed({integer(), integer(), integer()}) -> 'undefined' | ran(). +-spec seed({A1, A2, A3}) -> 'undefined' | ran() when + A1 :: integer(), + A2 :: integer(), + A3 :: integer(). seed({A1, A2, A3}) -> seed(A1, A2, A3). @@ -65,7 +68,10 @@ seed({A1, A2, A3}) -> %% seed(A1, A2, A3) %% Seed random number generation --spec seed(integer(), integer(), integer()) -> 'undefined' | ran(). +-spec seed(A1, A2, A3) -> 'undefined' | ran() when + A1 :: integer(), + A2 :: integer(), + A3 :: integer(). seed(A1, A2, A3) -> seed_put({(abs(A1) rem (?PRIME1-1)) + 1, % Avoid seed numbers that are @@ -99,7 +105,8 @@ uniform() -> %% Given an integer N >= 1, uniform(N) returns a random integer %% between 1 and N. --spec uniform(pos_integer()) -> pos_integer(). +-spec uniform(N) -> pos_integer() when + N :: pos_integer(). uniform(N) when is_integer(N), N >= 1 -> trunc(uniform() * N) + 1. @@ -110,7 +117,9 @@ uniform(N) when is_integer(N), N >= 1 -> %% uniform_s(State) -> {F, NewState} %% Returns a random float between 0 and 1. --spec uniform_s(ran()) -> {float(), ran()}. +-spec uniform_s(State0) -> {float(), State1} when + State0 :: ran(), + State1 :: ran(). uniform_s({A1, A2, A3}) -> B1 = (A1*171) rem ?PRIME1, @@ -123,7 +132,10 @@ uniform_s({A1, A2, A3}) -> %% Given an integer N >= 1, uniform(N) returns a random integer %% between 1 and N. --spec uniform_s(pos_integer(), ran()) -> {integer(), ran()}. +-spec uniform_s(N, State0) -> {integer(), State1} when + N :: pos_integer(), + State0 :: ran(), + State1 :: ran(). uniform_s(N, State0) when is_integer(N), N >= 1 -> {F, State1} = uniform_s(State0), diff --git a/lib/stdlib/src/re.erl b/lib/stdlib/src/re.erl index e2cc9f57ce..99bcbd722e 100644 --- a/lib/stdlib/src/re.erl +++ b/lib/stdlib/src/re.erl @@ -19,15 +19,46 @@ -module(re). -export([grun/3,urun/3,ucompile/2,replace/3,replace/4,split/2,split/3]). +%-opaque mp() :: {re_pattern, _, _, _}. +-type mp() :: {re_pattern, _, _, _}. + +-type nl_spec() :: cr | crlf | lf | anycrlf | any. + +-type compile_option() :: unicode | anchored | caseless | dollar_endonly + | dotall | extended | firstline | multiline + | no_auto_capture | dupnames | ungreedy + | {newline, nl_spec()}| bsr_anycrlf + | bsr_unicode. + %% Emulator builtins in this module: %% re:compile/1 %% re:compile/2 %% re:run/2 %% re:run/3 +-spec split(Subject, RE) -> SplitList when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata(), + SplitList :: [iodata() | unicode:charlist()]. + split(Subject,RE) -> split(Subject,RE,[]). +-spec split(Subject, RE, Options) -> SplitList when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata() | unicode:charlist(), + Options :: [ Option ], + Option :: anchored | global | notbol | noteol | notempty + | {offset, non_neg_integer()} | {newline, nl_spec()} + | bsr_anycrlf | bsr_unicode | {return, ReturnType} + | {parts, NumParts} | group | trim | CompileOpt, + NumParts :: non_neg_integer() | infinity, + ReturnType :: iodata | list | binary, + CompileOpt :: compile_option(), + SplitList :: [RetData] | [GroupedRetData], + GroupedRetData :: [RetData], + RetData :: iodata() | unicode:charlist() | binary() | list(). + split(Subject,RE,Options) -> try {NewOpt,Convert,Unicode,Limit,Strip,Group} = @@ -197,10 +228,26 @@ compile_split(Pat,Options0) when not is_tuple(Pat) -> compile_split(_,_) -> throw(badre). +-spec replace(Subject, RE, Replacement) -> iodata() | unicode:charlist() when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata(), + Replacement :: iodata() | unicode:charlist(). replace(Subject,RE,Replacement) -> replace(Subject,RE,Replacement,[]). +-spec replace(Subject, RE, Replacement, Options) -> iodata() | unicode:charlist() when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata() | unicode:charlist(), + Replacement :: iodata() | unicode:charlist(), + Options :: [Option], + Option :: anchored | global | notbol | noteol | notempty + | {offset, non_neg_integer()} | {newline, NLSpec} | bsr_anycrlf + | bsr_unicode | {return, ReturnType} | CompileOpt, + ReturnType :: iodata | list | binary, + CompileOpt :: compile_option(), + NLSpec :: cr | crlf | lf | anycrlf | any. + replace(Subject,RE,Replacement,Options) -> try {NewOpt,Convert,Unicode} = @@ -526,10 +573,10 @@ ucompile(RE,Options) -> re:compile(unicode:characters_to_binary(RE,unicode),Options) catch error:AnyError -> - {'EXIT',{new_stacktrace,[{Mod,_,L}|Rest]}} = + {'EXIT',{new_stacktrace,[{Mod,_,L,Loc}|Rest]}} = (catch erlang:error(new_stacktrace, [RE,Options])), - erlang:raise(error,AnyError,[{Mod,compile,L}|Rest]) + erlang:raise(error,AnyError,[{Mod,compile,L,Loc}|Rest]) end. @@ -538,10 +585,10 @@ urun(Subject,RE,Options) -> urun2(Subject,RE,Options) catch error:AnyError -> - {'EXIT',{new_stacktrace,[{Mod,_,L}|Rest]}} = + {'EXIT',{new_stacktrace,[{Mod,_,L,Loc}|Rest]}} = (catch erlang:error(new_stacktrace, [Subject,RE,Options])), - erlang:raise(error,AnyError,[{Mod,run,L}|Rest]) + erlang:raise(error,AnyError,[{Mod,run,L,Loc}|Rest]) end. urun2(Subject0,RE0,Options0) -> @@ -578,20 +625,20 @@ grun(Subject,RE,{Options,NeedClean}) -> grun2(Subject,RE,{Options,NeedClean}) catch error:AnyError -> - {'EXIT',{new_stacktrace,[{Mod,_,L}|Rest]}} = + {'EXIT',{new_stacktrace,[{Mod,_,L,Loc}|Rest]}} = (catch erlang:error(new_stacktrace, [Subject,RE,Options])), - erlang:raise(error,AnyError,[{Mod,run,L}|Rest]) + erlang:raise(error,AnyError,[{Mod,run,L,Loc}|Rest]) end; grun(Subject,RE,{Options,NeedClean,OrigRE}) -> try grun2(Subject,RE,{Options,NeedClean}) catch error:AnyError -> - {'EXIT',{new_stacktrace,[{Mod,_,L}|Rest]}} = + {'EXIT',{new_stacktrace,[{Mod,_,L,Loc}|Rest]}} = (catch erlang:error(new_stacktrace, [Subject,OrigRE,Options])), - erlang:raise(error,AnyError,[{Mod,run,L}|Rest]) + erlang:raise(error,AnyError,[{Mod,run,L,Loc}|Rest]) end. grun2(Subject,RE,{Options,NeedClean}) -> diff --git a/lib/stdlib/src/regexp.erl b/lib/stdlib/src/regexp.erl index 8f5994bbee..65f9ca247d 100644 --- a/lib/stdlib/src/regexp.erl +++ b/lib/stdlib/src/regexp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -37,6 +37,9 @@ -import(string, [substr/2,substr/3]). -import(lists, [reverse/1]). +-type errordesc() :: term(). +-opaque regexp() :: term(). + %% -type matchres() = {match,Start,Length} | nomatch | {error,E}. %% -type subres() = {ok,RepString,RepCount} | {error,E}. %% -type splitres() = {ok,[SubString]} | {error,E}. @@ -287,6 +290,10 @@ re_apply_or(R1, nomatch) -> R1. %% Convert a sh style regexp into a full AWK one. The main difficulty is %% getting character sets right as the conventions are different. +-spec sh_to_awk(ShRegExp) -> AwkRegExp when + ShRegExp :: string(), + AwkRegExp :: string(). + sh_to_awk(Sh) -> "^(" ++ sh_to_awk_1(Sh). %Fix the beginning sh_to_awk_1([$*|Sh]) -> %This matches any string @@ -336,6 +343,12 @@ special_char(_C) -> false. %% parse(RegExp) -> {ok,RE} | {error,E}. %% Parse the regexp described in the string RegExp. +-spec parse(RegExp) -> ParseRes when + RegExp :: string(), + ParseRes :: {ok, RE} | {error, Error}, + RE :: regexp(), + Error :: errordesc(). + parse(S) -> case catch reg(S) of {R,[]} -> {ok,R}; @@ -345,6 +358,10 @@ parse(S) -> %% format_error(Error) -> String. +-spec format_error(ErrorDescriptor) -> Chars when + ErrorDescriptor :: errordesc(), + Chars :: io_lib:chars(). + format_error({illegal,What}) -> ["illegal character `",What,"'"]; format_error({unterminated,What}) -> ["unterminated `",What,"'"]; format_error({char_class,What}) -> @@ -353,6 +370,14 @@ format_error({char_class,What}) -> %% -type match(String, RegExp) -> matchres(). %% Find the longest match of RegExp in String. +-spec match(String, RegExp) -> MatchRes when + String :: string(), + RegExp :: string() | regexp(), + MatchRes :: {match, Start, Length} | nomatch | {error, Error}, + Start :: pos_integer(), + Length :: pos_integer(), + Error :: errordesc(). + match(S, RegExp) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> match(S, RE); @@ -378,6 +403,14 @@ match(RE, S, St, Pos, L) -> %% -type first_match(String, RegExp) -> matchres(). %% Find the first match of RegExp in String. +-spec first_match(String, RegExp) -> MatchRes when + String :: string(), + RegExp :: string() | regexp(), + MatchRes :: {match, Start, Length} | nomatch | {error, Error}, + Start :: pos_integer(), + Length :: pos_integer(), + Error :: errordesc(). + first_match(S, RegExp) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> first_match(S, RE); @@ -400,6 +433,15 @@ first_match(_RE, [], _St) -> nomatch. %% -type matches(String, RegExp) -> {match,[{Start,Length}]} | {error,E}. %% Return the all the non-overlapping matches of RegExp in String. +-spec matches(String, RegExp) -> MatchRes when + String :: string(), + RegExp :: string() | regexp(), + MatchRes :: {match, Matches} | {error, Error}, + Matches :: [{Start, Length}], + Start :: pos_integer(), + Length :: pos_integer(), + Error :: errordesc(). + matches(S, RegExp) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> matches(S, RE); @@ -420,6 +462,15 @@ matches(S, RE, St) -> %% the string Replace in String. Accept pre-parsed regular %% expressions. +-spec sub(String, RegExp, New) -> SubRes when + String :: string(), + RegExp :: string() | regexp(), + New :: string(), + NewString :: string(), + SubRes :: {ok, NewString, RepCount} | {error, Error}, + RepCount :: 0 | 1, + Error :: errordesc(). + sub(String, RegExp, Rep) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> sub(String, RE, Rep); @@ -449,6 +500,15 @@ sub_repl([], _M, Rest) -> Rest. %% Substitute every match of the regular expression RegExp with the %% string New in String. Accept pre-parsed regular expressions. +-spec gsub(String, RegExp, New) -> SubRes when + String :: string(), + RegExp :: string() | regexp(), + New :: string(), + NewString :: string(), + SubRes :: {ok, NewString, RepCount} | {error, Error}, + RepCount :: non_neg_integer(), + Error :: errordesc(). + gsub(String, RegExp, Rep) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> gsub(String, RE, Rep); @@ -462,6 +522,13 @@ gsub(String, RE, Rep) -> %% Split a string into substrings where the RegExp describes the %% field seperator. The RegExp " " is specially treated. +-spec split(String, RegExp) -> SplitRes when + String :: string(), + RegExp :: string() | regexp(), + SplitRes :: {ok, FieldList} | {error, Error}, + FieldList :: [string()], + Error :: errordesc(). + split(String, " ") -> %This is really special {ok,RE} = parse("[ \t]+"), case split_apply(String, RE, true) of diff --git a/lib/stdlib/src/sets.erl b/lib/stdlib/src/sets.erl index bcddca2567..3fd6c81e5f 100644 --- a/lib/stdlib/src/sets.erl +++ b/lib/stdlib/src/sets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. +%% Copyright Ericsson AB 2000-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -84,30 +84,38 @@ new() -> %% is_set(Set) -> boolean(). %% Return 'true' if Set is a set of elements, else 'false'. --spec is_set(term()) -> boolean(). +-spec is_set(Set) -> boolean() when + Set :: term(). is_set(#set{}) -> true; is_set(_) -> false. %% size(Set) -> int(). %% Return the number of elements in Set. --spec size(set()) -> non_neg_integer(). +-spec size(Set) -> non_neg_integer() when + Set :: set(). size(S) -> S#set.size. %% to_list(Set) -> [Elem]. %% Return the elements in Set as a list. --spec to_list(set()) -> [term()]. +-spec to_list(Set) -> List when + Set :: set(), + List :: [term()]. to_list(S) -> fold(fun (Elem, List) -> [Elem|List] end, [], S). %% from_list([Elem]) -> Set. %% Build a set from the elements in List. --spec from_list([term()]) -> set(). +-spec from_list(List) -> Set when + List :: [term()], + Set :: set(). from_list(L) -> lists:foldl(fun (E, S) -> add_element(E, S) end, new(), L). %% is_element(Element, Set) -> boolean(). %% Return 'true' if Element is an element of Set, else 'false'. --spec is_element(term(), set()) -> boolean(). +-spec is_element(Element, Set) -> boolean() when + Element :: term(), + Set :: set(). is_element(E, S) -> Slot = get_slot(S, E), Bkt = get_bucket(S, Slot), @@ -115,7 +123,10 @@ is_element(E, S) -> %% add_element(Element, Set) -> Set. %% Return Set with Element inserted in it. --spec add_element(term(), set()) -> set(). +-spec add_element(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: set(), + Set2 :: set(). add_element(E, S0) -> Slot = get_slot(S0, E), {S1,Ic} = on_bucket(fun (B0) -> add_bkt_el(E, B0, B0) end, S0, Slot), @@ -129,7 +140,10 @@ add_bkt_el(E, [], Bkt) -> {[E|Bkt],1}. %% del_element(Element, Set) -> Set. %% Return Set but with Element removed. --spec del_element(term(), set()) -> set(). +-spec del_element(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: set(), + Set2 :: set(). del_element(E, S0) -> Slot = get_slot(S0, E), {S1,Dc} = on_bucket(fun (B0) -> del_bkt_el(E, B0) end, S0, Slot), @@ -144,7 +158,10 @@ del_bkt_el(_, []) -> {[],0}. %% union(Set1, Set2) -> Set %% Return the union of Set1 and Set2. --spec union(set(), set()) -> set(). +-spec union(Set1, Set2) -> Set3 when + Set1 :: set(), + Set2 :: set(), + Set3 :: set(). union(S1, S2) when S1#set.size < S2#set.size -> fold(fun (E, S) -> add_element(E, S) end, S2, S1); union(S1, S2) -> @@ -152,7 +169,9 @@ union(S1, S2) -> %% union([Set]) -> Set %% Return the union of the list of sets. --spec union([set()]) -> set(). +-spec union(SetList) -> Set when + SetList :: [set()], + Set :: set(). union([S1,S2|Ss]) -> union1(union(S1, S2), Ss); union([S]) -> S; @@ -165,7 +184,10 @@ union1(S1, []) -> S1. %% intersection(Set1, Set2) -> Set. %% Return the intersection of Set1 and Set2. --spec intersection(set(), set()) -> set(). +-spec intersection(Set1, Set2) -> Set3 when + Set1 :: set(), + Set2 :: set(), + Set3 :: set(). intersection(S1, S2) when S1#set.size < S2#set.size -> filter(fun (E) -> is_element(E, S2) end, S1); intersection(S1, S2) -> @@ -173,7 +195,9 @@ intersection(S1, S2) -> %% intersection([Set]) -> Set. %% Return the intersection of the list of sets. --spec intersection([set(),...]) -> set(). +-spec intersection(SetList) -> Set when + SetList :: [set(),...], + Set :: set(). intersection([S1,S2|Ss]) -> intersection1(intersection(S1, S2), Ss); intersection([S]) -> S. @@ -185,7 +209,9 @@ intersection1(S1, []) -> S1. %% is_disjoint(Set1, Set2) -> boolean(). %% Check whether Set1 and Set2 are disjoint. --spec is_disjoint(set(), set()) -> boolean(). +-spec is_disjoint(Set1, Set2) -> boolean() when + Set1 :: set(), + Set2 :: set(). is_disjoint(S1, S2) when S1#set.size < S2#set.size -> fold(fun (_, false) -> false; (E, true) -> not is_element(E, S2) @@ -198,25 +224,39 @@ is_disjoint(S1, S2) -> %% subtract(Set1, Set2) -> Set. %% Return all and only the elements of Set1 which are not also in %% Set2. --spec subtract(set(), set()) -> set(). +-spec subtract(Set1, Set2) -> Set3 when + Set1 :: set(), + Set2 :: set(), + Set3 :: set(). 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(set(), set()) -> boolean(). +-spec is_subset(Set1, Set2) -> boolean() when + Set1 :: set(), + Set2 :: set(). 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(fun((_,_) -> _), T, set()) -> T. +-spec fold(Function, Acc0, Set) -> Acc1 when + Function :: fun((E :: term(),AccIn) -> AccOut), + Set :: set(), + Acc0 :: T, + Acc1 :: T, + AccIn :: T, + AccOut :: T. fold(F, Acc, D) -> fold_set(F, Acc, D). %% filter(Fun, Set) -> Set. %% Filter Set with Fun. --spec filter(fun((_) -> boolean()), set()) -> set(). +-spec filter(Pred, Set1) -> Set2 when + Pred :: fun((E :: term()) -> boolean()), + Set1 :: set(), + Set2 :: set(). filter(F, D) -> filter_set(F, D). %% get_slot(Hashdb, Key) -> Slot. diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl index ebb221c151..964697cae6 100644 --- a/lib/stdlib/src/shell.erl +++ b/lib/stdlib/src/shell.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -115,7 +115,9 @@ whereis_evaluator(Shell) -> %% Call this function to start a user restricted shell %% from a normal shell session. --spec start_restricted(module()) -> {'error', code:load_error_rsn()}. +-spec start_restricted(Module) -> {'error', Reason} when + Module :: module(), + Reason :: code:load_error_rsn(). start_restricted(RShMod) when is_atom(RShMod) -> case code:ensure_loaded(RShMod) of @@ -465,7 +467,7 @@ getc(N) -> {get({command,N}), get({result,N}), N}. get_cmd(Num, C) -> - case catch erl_eval:expr(Num, []) of + case catch erl_eval:expr(Num, erl_eval:new_bindings()) of {value,N,_} when N < 0 -> getc(C+N); {value,N,_} -> getc(N); _Other -> {undefined,undefined,undefined} @@ -1086,7 +1088,7 @@ shell_default(F,As,Bs) -> end. shell_undef(F,A) -> - erlang:error({shell_undef,F,A}). + erlang:error({shell_undef,F,A,[]}). local_func_handler(Shell, RT, Ef) -> H = fun(Lf) -> @@ -1184,6 +1186,8 @@ expr(E, Bs, Lf, Ef) -> expr_list(Es, Bs, Lf, Ef) -> erl_eval:expr_list(Es, strip_bindings(Bs), Lf, Ef). +-spec strip_bindings(erl_eval:binding_struct()) -> erl_eval:binding_struct(). + strip_bindings(Bs) -> Bs -- [B || {{module,_},_}=B <- Bs]. @@ -1468,23 +1472,26 @@ set_env(App, Name, Val, Default) -> application_controller:set_env(App, Name, Val), Prev. --spec history(non_neg_integer()) -> non_neg_integer(). +-spec history(N) -> non_neg_integer() when + N :: non_neg_integer(). history(L) when is_integer(L), L >= 0 -> set_env(stdlib, shell_history_length, L, ?DEF_HISTORY). --spec results(non_neg_integer()) -> non_neg_integer(). +-spec results(N) -> non_neg_integer() when + N :: non_neg_integer(). results(L) when is_integer(L), L >= 0 -> set_env(stdlib, shell_saved_results, L, ?DEF_RESULTS). --spec catch_exception(boolean()) -> boolean(). +-spec catch_exception(Bool) -> Bool when + Bool :: boolean(). catch_exception(Bool) -> set_env(stdlib, shell_catch_exception, Bool, ?DEF_CATCH_EXCEPTION). --type prompt_func() :: 'default' | {module(),atom()}. --spec prompt_func(prompt_func()) -> prompt_func(). +-spec prompt_func(PromptFunc) -> PromptFunc when + PromptFunc :: 'default' | {module(),atom()}. prompt_func(String) -> set_env(stdlib, shell_prompt_func, String, ?DEF_PROMPT_FUNC). diff --git a/lib/stdlib/src/slave.erl b/lib/stdlib/src/slave.erl index 196b659938..de0179da59 100644 --- a/lib/stdlib/src/slave.erl +++ b/lib/stdlib/src/slave.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -54,6 +54,10 @@ pseudo([Master | ServerList]) -> pseudo(_) -> error_msg("No master node given to slave:pseudo/1~n",[]). +-spec pseudo(Master, ServerList) -> ok when + Master :: node(), + ServerList :: [atom()]. + pseudo(_, []) -> ok; pseudo(Master, [S|Tail]) -> start_pseudo(S, whereis(S), Master), @@ -68,6 +72,9 @@ start_pseudo(_,_,_) -> ok. %% It's already there %% This relay can be used to relay all messages directed to a process. +-spec relay(Pid) -> no_return() when + Pid :: pid(). + relay({badrpc,Reason}) -> error_msg(" ** exiting relay server ~w :~w **~n", [self(),Reason]), exit(Reason); @@ -120,25 +127,61 @@ relay1(Pid) -> %% {error, no_rsh} | %% {error, {already_running, Name@Host}} +-spec start(Host) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. + start(Host) -> L = atom_to_list(node()), Name = upto($@, L), - start(Host, Name). + start(Host, Name, [], no_link). + +-spec start(Host, Name) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Name :: atom(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. start(Host, Name) -> start(Host, Name, []). +-spec start(Host, Name, Args) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Name :: atom(), + Args :: string(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. + start(Host, Name, Args) -> start(Host, Name, Args, no_link). +-spec start_link(Host) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. + start_link(Host) -> L = atom_to_list(node()), Name = upto($@, L), - start_link(Host, Name). + start(Host, Name, [], self()). + +-spec start_link(Host, Name) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Name :: atom(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. start_link(Host, Name) -> start_link(Host, Name, []). +-spec start_link(Host, Name, Args) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Name :: atom(), + Args :: string(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. + start_link(Host, Name, Args) -> start(Host, Name, Args, self()). @@ -163,6 +206,9 @@ start(Host0, Name, Args, LinkTo, Prog) -> %% Stops a running node. +-spec stop(Node) -> ok when + Node :: node(). + stop(Node) -> % io:format("stop(~p)~n", [Node]), rpc:call(Node, erlang, halt, []), diff --git a/lib/stdlib/src/sofs.erl b/lib/stdlib/src/sofs.erl index a83f803330..34eb224647 100644 --- a/lib/stdlib/src/sofs.erl +++ b/lib/stdlib/src/sofs.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% 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(sofs). @@ -40,11 +40,11 @@ substitution/2, projection/2, partition/1, partition/2, partition/3, multiple_relative_product/2, join/4]). --export([family_to_relation/1, family_specification/2, +-export([family_to_relation/1, family_specification/2, union_of_family/1, intersection_of_family/1, family_union/1, family_intersection/1, family_domain/1, family_range/1, family_field/1, - family_union/2, family_intersection/2, family_difference/2, + family_union/2, family_intersection/2, family_difference/2, partition_family/2, family_projection/2]). -export([family_to_digraph/1, family_to_digraph/2, @@ -64,9 +64,9 @@ -compile({inline, [{external_fun,1},{element_type,1}]}). --compile({inline, +-compile({inline, [{unify_types,2}, {match_types,2}, - {test_rel,3}, {symdiff,3}, + {test_rel,3}, {symdiff,3}, {subst,3}]}). -compile({inline, [{fam_binop,3}]}). @@ -80,13 +80,14 @@ -define(TAG, 'Set'). -define(ORDTAG, 'OrdSet'). --record(?TAG, {data = [], type = type}). --record(?ORDTAG, {orddata = {}, ordtype = type}). +-record(?TAG, {data = [] :: list(), type = type :: term()}). +-record(?ORDTAG, {orddata = {} :: tuple() | atom(), + ordtype = type :: term()}). -define(LIST(S), (S)#?TAG.data). -define(TYPE(S), (S)#?TAG.type). -%%-define(SET(L, T), -%% case is_type(T) of +%%-define(SET(L, T), +%% case is_type(T) of %% true -> #?TAG{data = L, type = T}; %% false -> erlang:error(badtype, [T]) %% end @@ -113,14 +114,40 @@ -define(IS_SET_OF(X), is_list(X)). -define(FAMILY(X, Y), ?BINREL(X, ?SET_OF(Y))). +-export_type([anyset/0, binary_relation/0, external_set/0, a_function/0, + family/0, relation/0, set_of_sets/0, set_fun/0, spec_fun/0, + type/0]). +-export_type([ordset/0, a_set/0]). + +-type(anyset() :: ordset() | a_set()). +-type(binary_relation() :: relation()). +-type(external_set() :: term()). +-type(a_function() :: relation()). +-type(family() :: a_function()). +-opaque(ordset() :: #?ORDTAG{}). +-type(relation() :: a_set()). +-opaque(a_set() :: #?TAG{}). +-type(set_of_sets() :: a_set()). +-type(set_fun() :: pos_integer() + | {external, fun((external_set()) -> external_set())} + | fun((anyset()) -> anyset())). +-type(spec_fun() :: {external, fun((external_set()) -> boolean())} + | fun((anyset()) -> boolean())). +-type(type() :: term()). + +-type(tuple_of(_T) :: tuple()). + %% %% Exported functions %% -%%% +%%% %%% Create sets -%%% +%%% +-spec(from_term(Term) -> AnySet when + AnySet :: anyset(), + Term :: term()). from_term(T) -> Type = case T of _ when is_list(T) -> [?ANYTYPE]; @@ -133,6 +160,10 @@ from_term(T) -> Set end. +-spec(from_term(Term, Type) -> AnySet when + AnySet :: anyset(), + Term :: term(), + Type :: type()). from_term(L, T) -> case is_type(T) of true -> @@ -146,14 +177,23 @@ from_term(L, T) -> erlang:error(badarg, [L, T]) end. +-spec(from_external(ExternalSet, Type) -> AnySet when + ExternalSet :: external_set(), + AnySet :: anyset(), + Type :: type()). from_external(L, ?SET_OF(Type)) -> ?SET(L, Type); from_external(T, Type) -> ?ORDSET(T, Type). +-spec(empty_set() -> Set when + Set :: a_set()). empty_set() -> ?SET([], ?ANYTYPE). +-spec(is_type(Term) -> Bool when + Bool :: boolean(), + Term :: term()). is_type(Atom) when ?IS_ATOM_TYPE(Atom), Atom =/= ?ANYTYPE -> true; is_type(?SET_OF(T)) -> @@ -163,19 +203,26 @@ is_type(T) when tuple_size(T) > 0 -> is_type(_T) -> false. +-spec(set(Terms) -> Set when + Set :: a_set(), + Terms :: [term()]). set(L) -> case catch usort(L) of {'EXIT', _} -> erlang:error(badarg, [L]); - SL -> + SL -> ?SET(SL, ?ATOM_TYPE) end. +-spec(set(Terms, Type) -> Set when + Set :: a_set(), + Terms :: [term()], + Type :: type()). set(L, ?SET_OF(Type) = T) when ?IS_ATOM_TYPE(Type), Type =/= ?ANYTYPE -> case catch usort(L) of {'EXIT', _} -> erlang:error(badarg, [L, T]); - SL -> + SL -> ?SET(SL, Type) end; set(L, ?SET_OF(_) = T) -> @@ -188,6 +235,12 @@ set(L, ?SET_OF(_) = T) -> set(L, T) -> erlang:error(badarg, [L, T]). +-spec(from_sets(ListOfSets) -> Set when + Set :: a_set(), + ListOfSets :: [anyset()]; + (TupleOfSets) -> Ordset when + Ordset :: ordset(), + TupleOfSets :: tuple_of(anyset())). from_sets(Ss) when is_list(Ss) -> case set_of_sets(Ss, [], ?ANYTYPE) of {error, Error} -> @@ -205,6 +258,9 @@ from_sets(Tuple) when is_tuple(Tuple) -> from_sets(T) -> erlang:error(badarg, [T]). +-spec(relation(Tuples) -> Relation when + Relation :: relation(), + Tuples :: [tuple()]). relation([]) -> ?SET([], ?BINREL(?ATOM_TYPE, ?ATOM_TYPE)); relation(Ts = [T | _]) when is_tuple(T) -> @@ -217,6 +273,11 @@ relation(Ts = [T | _]) when is_tuple(T) -> relation(E) -> erlang:error(badarg, [E]). +-spec(relation(Tuples, Type) -> Relation when + N :: integer(), + Type :: N | type(), + Relation :: relation(), + Tuples :: [tuple()]). relation(Ts, TS) -> case catch rel(Ts, TS) of {'EXIT', _} -> @@ -225,6 +286,9 @@ relation(Ts, TS) -> Set end. +-spec(a_function(Tuples) -> Function when + Function :: a_function(), + Tuples :: [tuple()]). a_function(Ts) -> case catch func(Ts, ?BINREL(?ATOM_TYPE, ?ATOM_TYPE)) of {'EXIT', _} -> @@ -235,6 +299,10 @@ a_function(Ts) -> Set end. +-spec(a_function(Tuples, Type) -> Function when + Function :: a_function(), + Tuples :: [tuple()], + Type :: type()). a_function(Ts, T) -> case catch a_func(Ts, T) of {'EXIT', _} -> @@ -245,6 +313,9 @@ a_function(Ts, T) -> Set end. +-spec(family(Tuples) -> Family when + Family :: family(), + Tuples :: [tuple()]). family(Ts) -> case catch fam2(Ts, ?FAMILY(?ATOM_TYPE, ?ATOM_TYPE)) of {'EXIT', _} -> @@ -255,6 +326,10 @@ family(Ts) -> Set end. +-spec(family(Tuples, Type) -> Family when + Family :: family(), + Tuples :: [tuple()], + Type :: type()). family(Ts, T) -> case catch fam(Ts, T) of {'EXIT', _} -> @@ -265,20 +340,30 @@ family(Ts, T) -> Set end. -%%% +%%% %%% Functions on sets. -%%% +%%% +-spec(to_external(AnySet) -> ExternalSet when + ExternalSet :: external_set(), + AnySet :: anyset()). to_external(S) when ?IS_SET(S) -> ?LIST(S); to_external(S) when ?IS_ORDSET(S) -> ?ORDDATA(S). +-spec(type(AnySet) -> Type when + AnySet :: anyset(), + Type :: type()). type(S) when ?IS_SET(S) -> ?SET_OF(?TYPE(S)); type(S) when ?IS_ORDSET(S) -> ?ORDTYPE(S). +-spec(to_sets(ASet) -> Sets when + ASet :: a_set() | ordset(), + Sets :: tuple_of(AnySet) | [AnySet], + AnySet :: anyset()). to_sets(S) when ?IS_SET(S) -> case ?TYPE(S) of ?SET_OF(Type) -> list_of_sets(?LIST(S), Type, []); @@ -289,6 +374,9 @@ to_sets(S) when ?IS_ORDSET(S), is_tuple(?ORDTYPE(S)) -> to_sets(S) when ?IS_ORDSET(S) -> erlang:error(badarg, [S]). +-spec(no_elements(ASet) -> NoElements when + ASet :: a_set() | ordset(), + NoElements :: non_neg_integer()). no_elements(S) when ?IS_SET(S) -> length(?LIST(S)); no_elements(S) when ?IS_ORDSET(S), is_tuple(?ORDTYPE(S)) -> @@ -296,6 +384,10 @@ no_elements(S) when ?IS_ORDSET(S), is_tuple(?ORDTYPE(S)) -> no_elements(S) when ?IS_ORDSET(S) -> erlang:error(badarg, [S]). +-spec(specification(Fun, Set1) -> Set2 when + Fun :: spec_fun(), + Set1 :: a_set(), + Set2 :: a_set()). specification(Fun, S) when ?IS_SET(S) -> Type = ?TYPE(S), R = case external_fun(Fun) of @@ -311,36 +403,62 @@ specification(Fun, S) when ?IS_SET(S) -> erlang:error(Bad, [Fun, S]) end. +-spec(union(Set1, Set2) -> Set3 when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). union(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> ?SET(umerge(?LIST(S1), ?LIST(S2)), Type) end. +-spec(intersection(Set1, Set2) -> Set3 when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). intersection(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> ?SET(intersection(?LIST(S1), ?LIST(S2), []), Type) end. +-spec(difference(Set1, Set2) -> Set3 when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). difference(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> ?SET(difference(?LIST(S1), ?LIST(S2), []), Type) end. +-spec(symdiff(Set1, Set2) -> Set3 when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). symdiff(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> ?SET(symdiff(?LIST(S1), ?LIST(S2), []), Type) end. +-spec(symmetric_partition(Set1, Set2) -> {Set3, Set4, Set5} when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set(), + Set4 :: a_set(), + Set5 :: a_set()). symmetric_partition(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> sympart(?LIST(S1), ?LIST(S2), [], [], [], Type) end. +-spec(product(Set1, Set2) -> BinRel when + BinRel :: binary_relation(), + Set1 :: a_set(), + Set2 :: a_set()). product(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> if ?TYPE(S1) =:= ?ANYTYPE -> S1; @@ -351,6 +469,9 @@ product(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> ?SET(relprod(map(F, ?LIST(S1)), map(F, ?LIST(S2))), T) end. +-spec(product(TupleOfSets) -> Relation when + Relation :: relation(), + TupleOfSets :: tuple_of(a_set())). product({S1, S2}) -> product(S1, S2); product(T) when is_tuple(T) -> @@ -365,11 +486,15 @@ product(T) when is_tuple(T) -> case member([], L) of true -> empty_set(); - false -> + false -> ?SET(reverse(prod(L, [], [])), Type) end end. +-spec(constant_function(Set, AnySet) -> Function when + AnySet :: anyset(), + Function :: a_function(), + Set :: a_set()). constant_function(S, E) when ?IS_SET(S) -> case {?TYPE(S), is_sofs_set(E)} of {?ANYTYPE, true} -> S; @@ -381,6 +506,10 @@ constant_function(S, E) when ?IS_SET(S) -> constant_function(S, E) when ?IS_ORDSET(S) -> erlang:error(badarg, [S, E]). +-spec(is_equal(AnySet1, AnySet2) -> Bool when + AnySet1 :: anyset(), + AnySet2 :: anyset(), + Bool :: boolean()). is_equal(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case match_types(?TYPE(S1), ?TYPE(S2)) of true -> ?LIST(S1) == ?LIST(S2); @@ -396,12 +525,19 @@ is_equal(S1, S2) when ?IS_SET(S1), ?IS_ORDSET(S2) -> is_equal(S1, S2) when ?IS_ORDSET(S1), ?IS_SET(S2) -> erlang:error(type_mismatch, [S1, S2]). +-spec(is_subset(Set1, Set2) -> Bool when + Bool :: boolean(), + Set1 :: a_set(), + Set2 :: a_set()). is_subset(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case match_types(?TYPE(S1), ?TYPE(S2)) of true -> subset(?LIST(S1), ?LIST(S2)); false -> erlang:error(type_mismatch, [S1, S2]) end. +-spec(is_sofs_set(Term) -> Bool when + Bool :: boolean(), + Term :: term()). is_sofs_set(S) when ?IS_SET(S) -> true; is_sofs_set(S) when ?IS_ORDSET(S) -> @@ -409,16 +545,26 @@ is_sofs_set(S) when ?IS_ORDSET(S) -> is_sofs_set(_S) -> false. +-spec(is_set(AnySet) -> Bool when + AnySet :: anyset(), + Bool :: boolean()). is_set(S) when ?IS_SET(S) -> true; is_set(S) when ?IS_ORDSET(S) -> false. -is_empty_set(S) when ?IS_SET(S) -> +-spec(is_empty_set(AnySet) -> Bool when + AnySet :: anyset(), + Bool :: boolean()). +is_empty_set(S) when ?IS_SET(S) -> ?LIST(S) =:= []; is_empty_set(S) when ?IS_ORDSET(S) -> false. +-spec(is_disjoint(Set1, Set2) -> Bool when + Bool :: boolean(), + Set1 :: a_set(), + Set2 :: a_set()). is_disjoint(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case match_types(?TYPE(S1), ?TYPE(S2)) of true -> @@ -433,6 +579,9 @@ is_disjoint(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> %%% Functions on set-of-sets. %%% +-spec(union(SetOfSets) -> Set when + Set :: a_set(), + SetOfSets :: set_of_sets()). union(Sets) when ?IS_SET(Sets) -> case ?TYPE(Sets) of ?SET_OF(Type) -> ?SET(lunion(?LIST(Sets)), Type); @@ -440,6 +589,9 @@ union(Sets) when ?IS_SET(Sets) -> _ -> erlang:error(badarg, [Sets]) end. +-spec(intersection(SetOfSets) -> Set when + Set :: a_set(), + SetOfSets :: set_of_sets()). intersection(Sets) when ?IS_SET(Sets) -> case ?LIST(Sets) of [] -> erlang:error(badarg, [Sets]); @@ -451,32 +603,41 @@ intersection(Sets) when ?IS_SET(Sets) -> end end. +-spec(canonical_relation(SetOfSets) -> BinRel when + BinRel :: binary_relation(), + SetOfSets :: set_of_sets()). canonical_relation(Sets) when ?IS_SET(Sets) -> ST = ?TYPE(Sets), case ST of ?SET_OF(?ANYTYPE) -> empty_set(); - ?SET_OF(Type) -> + ?SET_OF(Type) -> ?SET(can_rel(?LIST(Sets), []), ?BINREL(Type, ST)); ?ANYTYPE -> Sets; _ -> erlang:error(badarg, [Sets]) end. -%%% +%%% %%% Functions on binary relations only. -%%% +%%% rel2fam(R) -> relation_to_family(R). +-spec(relation_to_family(BinRel) -> Family when + Family :: family(), + BinRel :: binary_relation()). %% Inlined. relation_to_family(R) when ?IS_SET(R) -> case ?TYPE(R) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> ?SET(rel2family(?LIST(R)), ?FAMILY(DT, RT)); ?ANYTYPE -> R; _Else -> erlang:error(badarg, [R]) end. +-spec(domain(BinRel) -> Set when + BinRel :: binary_relation(), + Set :: a_set()). domain(R) when ?IS_SET(R) -> case ?TYPE(R) of ?BINREL(DT, _) -> ?SET(dom(?LIST(R)), DT); @@ -484,6 +645,9 @@ domain(R) when ?IS_SET(R) -> _Else -> erlang:error(badarg, [R]) end. +-spec(range(BinRel) -> Set when + BinRel :: binary_relation(), + Set :: a_set()). range(R) when ?IS_SET(R) -> case ?TYPE(R) of ?BINREL(_, RT) -> ?SET(ran(?LIST(R), []), RT); @@ -491,35 +655,63 @@ range(R) when ?IS_SET(R) -> _ -> erlang:error(badarg, [R]) end. +-spec(field(BinRel) -> Set when + BinRel :: binary_relation(), + Set :: a_set()). %% In "Introduction to LOGIC", Suppes defines the field of a binary %% relation to be the union of the domain and the range (or %% counterdomain). field(R) -> union(domain(R), range(R)). +-spec(relative_product(ListOfBinRels) -> BinRel2 when + ListOfBinRels :: [BinRel, ...], + BinRel :: binary_relation(), + BinRel2 :: binary_relation()). +%% The following clause is kept for backward compatibility. +%% The list is due to Dialyzer's specs. relative_product(RT) when is_tuple(RT) -> - case relprod_n(RT, foo, false, false) of - {error, Reason} -> - erlang:error(Reason, [RT]); + relative_product(tuple_to_list(RT)); +relative_product(RL) when is_list(RL) -> + case relprod_n(RL, foo, false, false) of + {error, Reason} -> + erlang:error(Reason, [RL]); Reply -> Reply end. +-spec(relative_product(ListOfBinRels, BinRel1) -> BinRel2 when + ListOfBinRels :: [BinRel, ...], + BinRel :: binary_relation(), + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(); + (BinRel1, BinRel2) -> BinRel3 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + BinRel3 :: binary_relation()). relative_product(R1, R2) when ?IS_SET(R1), ?IS_SET(R2) -> relative_product1(converse(R1), R2); +%% The following clause is kept for backward compatibility. +%% The list is due to Dialyzer's specs. relative_product(RT, R) when is_tuple(RT), ?IS_SET(R) -> + relative_product(tuple_to_list(RT), R); +relative_product(RL, R) when is_list(RL), ?IS_SET(R) -> EmptyR = case ?TYPE(R) of ?BINREL(_, _) -> ?LIST(R) =:= []; ?ANYTYPE -> true; - _ -> erlang:error(badarg, [RT, R]) + _ -> erlang:error(badarg, [RL, R]) end, - case relprod_n(RT, R, EmptyR, true) of - {error, Reason} -> - erlang:error(Reason, [RT, R]); + case relprod_n(RL, R, EmptyR, true) of + {error, Reason} -> + erlang:error(Reason, [RL, R]); Reply -> Reply end. +-spec(relative_product1(BinRel1, BinRel2) -> BinRel3 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + BinRel3 :: binary_relation()). relative_product1(R1, R2) when ?IS_SET(R1), ?IS_SET(R2) -> {DTR1, RTR1} = case ?TYPE(R1) of ?BINREL(_, _) = R1T -> R1T; @@ -538,16 +730,23 @@ relative_product1(R1, R2) when ?IS_SET(R1), ?IS_SET(R2) -> false -> erlang:error(type_mismatch, [R1, R2]) end. +-spec(converse(BinRel1) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation()). converse(R) when ?IS_SET(R) -> case ?TYPE(R) of ?BINREL(DT, RT) -> ?SET(converse(?LIST(R), []), ?BINREL(RT, DT)); ?ANYTYPE -> R; _ -> erlang:error(badarg, [R]) end. - + +-spec(image(BinRel, Set1) -> Set2 when + BinRel :: binary_relation(), + Set1 :: a_set(), + Set2 :: a_set()). image(R, S) when ?IS_SET(R), ?IS_SET(S) -> case ?TYPE(R) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> case match_types(DT, ?TYPE(S)) of true -> ?SET(usort(restrict(?LIST(S), ?LIST(R))), RT); @@ -558,9 +757,13 @@ image(R, S) when ?IS_SET(R), ?IS_SET(S) -> _ -> erlang:error(badarg, [R, S]) end. +-spec(inverse_image(BinRel, Set1) -> Set2 when + BinRel :: binary_relation(), + Set1 :: a_set(), + Set2 :: a_set()). inverse_image(R, S) when ?IS_SET(R), ?IS_SET(S) -> case ?TYPE(R) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> case match_types(RT, ?TYPE(S)) of true -> NL = restrict(?LIST(S), converse(?LIST(R), [])), @@ -572,17 +775,23 @@ inverse_image(R, S) when ?IS_SET(R), ?IS_SET(S) -> _ -> erlang:error(badarg, [R, S]) end. +-spec(strict_relation(BinRel1) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation()). strict_relation(R) when ?IS_SET(R) -> case ?TYPE(R) of - Type = ?BINREL(_, _) -> + Type = ?BINREL(_, _) -> ?SET(strict(?LIST(R), []), Type); ?ANYTYPE -> R; _ -> erlang:error(badarg, [R]) end. - + +-spec(weak_relation(BinRel1) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation()). weak_relation(R) when ?IS_SET(R) -> case ?TYPE(R) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> case unify_types(DT, RT) of [] -> erlang:error(badarg, [R]); @@ -592,7 +801,12 @@ weak_relation(R) when ?IS_SET(R) -> ?ANYTYPE -> R; _ -> erlang:error(badarg, [R]) end. - + +-spec(extension(BinRel1, Set, AnySet) -> BinRel2 when + AnySet :: anyset(), + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + Set :: a_set()). extension(R, S, E) when ?IS_SET(R), ?IS_SET(S) -> case {?TYPE(R), ?TYPE(S), is_sofs_set(E)} of {T=?BINREL(DT, RT), ST, true} -> @@ -621,9 +835,12 @@ extension(R, S, E) when ?IS_SET(R), ?IS_SET(S) -> erlang:error(badarg, [R, S, E]) end. +-spec(is_a_function(BinRel) -> Bool when + Bool :: boolean(), + BinRel :: binary_relation()). is_a_function(R) when ?IS_SET(R) -> case ?TYPE(R) of - ?BINREL(_, _) -> + ?BINREL(_, _) -> case ?LIST(R) of [] -> true; [{V,_} | Es] -> is_a_func(Es, V) @@ -632,16 +849,28 @@ is_a_function(R) when ?IS_SET(R) -> _ -> erlang:error(badarg, [R]) end. +-spec(restriction(BinRel1, Set) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + Set :: a_set()). restriction(Relation, Set) -> restriction(1, Relation, Set). +-spec(drestriction(BinRel1, Set) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + Set :: a_set()). drestriction(Relation, Set) -> drestriction(1, Relation, Set). -%%% +%%% %%% Functions on functions only. -%%% +%%% +-spec(composite(Function1, Function2) -> Function3 when + Function1 :: a_function(), + Function2 :: a_function(), + Function3 :: a_function()). composite(Fn1, Fn2) when ?IS_SET(Fn1), ?IS_SET(Fn2) -> ?BINREL(DTF1, RTF1) = case ?TYPE(Fn1)of ?BINREL(_, _) = F1T -> F1T; @@ -656,7 +885,7 @@ composite(Fn1, Fn2) when ?IS_SET(Fn1), ?IS_SET(Fn2) -> case match_types(RTF1, DTF2) of true when DTF1 =:= ?ANYTYPE -> Fn1; true when DTF2 =:= ?ANYTYPE -> Fn2; - true -> + true -> case comp(?LIST(Fn1), ?LIST(Fn2)) of SL when is_list(SL) -> ?SET(sort(SL), ?BINREL(DTF1, RTF2)); @@ -666,9 +895,12 @@ composite(Fn1, Fn2) when ?IS_SET(Fn1), ?IS_SET(Fn2) -> false -> erlang:error(type_mismatch, [Fn1, Fn2]) end. +-spec(inverse(Function1) -> Function2 when + Function1 :: a_function(), + Function2 :: a_function()). inverse(Fn) when ?IS_SET(Fn) -> case ?TYPE(Fn) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> case inverse1(?LIST(Fn)) of SL when is_list(SL) -> ?SET(SL, ?BINREL(RT, DT)); @@ -678,11 +910,16 @@ inverse(Fn) when ?IS_SET(Fn) -> ?ANYTYPE -> Fn; _ -> erlang:error(badarg, [Fn]) end. - -%%% + +%%% %%% Functions on relations (binary or other). -%%% +%%% +-spec(restriction(SetFun, Set1, Set2) -> Set3 when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). %% Equivalent to range(restriction(inverse(substitution(Fun, S1)), S2)). restriction(I, R, S) when is_integer(I), ?IS_SET(R), ?IS_SET(S) -> RT = ?TYPE(R), @@ -747,6 +984,11 @@ restriction(SetFun, S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> end end. +-spec(drestriction(SetFun, Set1, Set2) -> Set3 when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). drestriction(I, R, S) when is_integer(I), ?IS_SET(R), ?IS_SET(S) -> RT = ?TYPE(R), ST = ?TYPE(S), @@ -812,6 +1054,10 @@ drestriction(SetFun, S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> end end. +-spec(projection(SetFun, Set1) -> Set2 when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set()). projection(I, Set) when is_integer(I), ?IS_SET(Set) -> Type = ?TYPE(Set), case check_for_sort(Type, I) of @@ -827,6 +1073,10 @@ projection(I, Set) when is_integer(I), ?IS_SET(Set) -> projection(Fun, Set) -> range(substitution(Fun, Set)). +-spec(substitution(SetFun, Set1) -> Set2 when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set()). substitution(I, Set) when is_integer(I), ?IS_SET(Set) -> Type = ?TYPE(Set), case check_for_sort(Type, I) of @@ -867,11 +1117,18 @@ substitution(SetFun, Set) when ?IS_SET(Set) -> end end. +-spec(partition(SetOfSets) -> Partition when + SetOfSets :: set_of_sets(), + Partition :: a_set()). partition(Sets) -> F1 = relation_to_family(canonical_relation(Sets)), F2 = relation_to_family(converse(F1)), range(F2). +-spec(partition(SetFun, Set) -> Partition when + SetFun :: set_fun(), + Partition :: a_set(), + Set :: a_set()). partition(I, Set) when is_integer(I), ?IS_SET(Set) -> Type = ?TYPE(Set), case check_for_sort(Type, I) of @@ -887,6 +1144,12 @@ partition(I, Set) when is_integer(I), ?IS_SET(Set) -> partition(Fun, Set) -> range(partition_family(Fun, Set)). +-spec(partition(SetFun, Set1, Set2) -> {Set3, Set4} when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set(), + Set4 :: a_set()). partition(I, R, S) when is_integer(I), ?IS_SET(R), ?IS_SET(S) -> RT = ?TYPE(R), ST = ?TYPE(S), @@ -954,21 +1217,32 @@ partition(SetFun, S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> end end. +-spec(multiple_relative_product(TupleOfBinRels, BinRel1) -> BinRel2 when + TupleOfBinRels :: tuple_of(BinRel), + BinRel :: binary_relation(), + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation()). multiple_relative_product(T, R) when is_tuple(T), ?IS_SET(R) -> case test_rel(R, tuple_size(T), eq) of true when ?TYPE(R) =:= ?ANYTYPE -> empty_set(); - true -> + true -> MProd = mul_relprod(tuple_to_list(T), 1, R), - relative_product(list_to_tuple(MProd)); - false -> + relative_product(MProd); + false -> erlang:error(badarg, [T, R]) end. -join(R1, I1, R2, I2) +-spec(join(Relation1, I, Relation2, J) -> Relation3 when + Relation1 :: relation(), + Relation2 :: relation(), + Relation3 :: relation(), + I :: pos_integer(), + J :: pos_integer()). +join(R1, I1, R2, I2) when ?IS_SET(R1), ?IS_SET(R2), is_integer(I1), is_integer(I2) -> case test_rel(R1, I1, lte) and test_rel(R2, I2, lte) of - false -> + false -> erlang:error(badarg, [R1, I1, R2, I2]); true when ?TYPE(R1) =:= ?ANYTYPE -> R1; true when ?TYPE(R2) =:= ?ANYTYPE -> R2; @@ -980,8 +1254,8 @@ join(R1, I1, R2, I2) true -> fun({X,Y}) -> join_element(X, Y) end; false -> - fun({X,Y}) -> - list_to_tuple(join_element(X, Y, I2)) + fun({X,Y}) -> + list_to_tuple(join_element(X, Y, I2)) end end, ?SET(replace(T, F, []), F({?TYPE(R1), ?TYPE(R2)})) @@ -1001,9 +1275,15 @@ test_rel(R, I, C) -> %%% Family functions %%% +-spec(fam2rel(Family) -> BinRel when + Family :: family(), + BinRel :: binary_relation()). fam2rel(F) -> family_to_relation(F). +-spec(family_to_relation(Family) -> BinRel when + Family :: family(), + BinRel :: binary_relation()). %% Inlined. family_to_relation(F) when ?IS_SET(F) -> case ?TYPE(F) of @@ -1013,6 +1293,10 @@ family_to_relation(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_specification(Fun, Family1) -> Family2 when + Fun :: spec_fun(), + Family1 :: family(), + Family2 :: family()). family_specification(Fun, F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_DT, Type) = FType -> @@ -1032,6 +1316,9 @@ family_specification(Fun, F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [Fun, F]) end. +-spec(union_of_family(Family) -> Set when + Family :: family(), + Set :: a_set()). union_of_family(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_DT, Type) -> @@ -1040,6 +1327,9 @@ union_of_family(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(intersection_of_family(Family) -> Set when + Family :: family(), + Set :: a_set()). intersection_of_family(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_DT, Type) -> @@ -1052,6 +1342,9 @@ intersection_of_family(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_union(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_union(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(DT, ?SET_OF(Type)) -> @@ -1060,6 +1353,9 @@ family_union(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_intersection(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_intersection(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(DT, ?SET_OF(Type)) -> @@ -1073,6 +1369,9 @@ family_intersection(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_domain(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_domain(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(FDT, ?BINREL(DT, _)) -> @@ -1082,6 +1381,9 @@ family_domain(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_range(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_range(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(DT, ?BINREL(_, RT)) -> @@ -1091,15 +1393,30 @@ family_range(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_field(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_field(F) -> family_union(family_domain(F), family_range(F)). +-spec(family_union(Family1, Family2) -> Family3 when + Family1 :: family(), + Family2 :: family(), + Family3 :: family()). family_union(F1, F2) -> fam_binop(F1, F2, fun fam_union/3). +-spec(family_intersection(Family1, Family2) -> Family3 when + Family1 :: family(), + Family2 :: family(), + Family3 :: family()). family_intersection(F1, F2) -> fam_binop(F1, F2, fun fam_intersect/3). +-spec(family_difference(Family1, Family2) -> Family3 when + Family1 :: family(), + Family2 :: family(), + Family3 :: family()). family_difference(F1, F2) -> fam_binop(F1, F2, fun fam_difference/3). @@ -1108,13 +1425,17 @@ fam_binop(F1, F2, FF) when ?IS_SET(F1), ?IS_SET(F2) -> case unify_types(?TYPE(F1), ?TYPE(F2)) of [] -> erlang:error(type_mismatch, [F1, F2]); - ?ANYTYPE -> + ?ANYTYPE -> F1; - Type = ?FAMILY(_, _) -> + Type = ?FAMILY(_, _) -> ?SET(FF(?LIST(F1), ?LIST(F2), []), Type); _ -> erlang:error(badarg, [F1, F2]) end. +-spec(partition_family(SetFun, Set) -> Family when + Family :: family(), + SetFun :: set_fun(), + Set :: a_set()). partition_family(I, Set) when is_integer(I), ?IS_SET(Set) -> Type = ?TYPE(Set), case check_for_sort(Type, I) of @@ -1159,8 +1480,12 @@ partition_family(SetFun, Set) when ?IS_SET(Set) -> end end. +-spec(family_projection(SetFun, Family1) -> Family2 when + SetFun :: set_fun(), + Family1 :: family(), + Family2 :: family()). family_projection(SetFun, F) when ?IS_SET(F) -> - case ?TYPE(F) of + case ?TYPE(F) of ?FAMILY(_, _) when [] =:= ?LIST(F) -> empty_set(); ?FAMILY(DT, Type) -> @@ -1172,7 +1497,7 @@ family_projection(SetFun, F) when ?IS_SET(F) -> Bad -> erlang:error(Bad, [SetFun, F]) end; - _ -> + _ -> erlang:error(badarg, [SetFun, F]) end; ?ANYTYPE -> F; @@ -1183,6 +1508,9 @@ family_projection(SetFun, F) when ?IS_SET(F) -> %%% Digraph functions %%% +-spec(family_to_digraph(Family) -> Graph when + Graph :: digraph(), + Family :: family()). family_to_digraph(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_, _) -> fam2digraph(F, digraph:new()); @@ -1190,6 +1518,10 @@ family_to_digraph(F) when ?IS_SET(F) -> _Else -> erlang:error(badarg, [F]) end. +-spec(family_to_digraph(Family, GraphType) -> Graph when + Graph :: digraph(), + Family :: family(), + GraphType :: [digraph:d_type()]). family_to_digraph(F, Type) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_, _) -> ok; @@ -1208,12 +1540,19 @@ family_to_digraph(F, Type) when ?IS_SET(F) -> error:badarg -> erlang:error(badarg, [F, Type]) end. +-spec(digraph_to_family(Graph) -> Family when + Graph :: digraph(), + Family :: family()). digraph_to_family(G) -> case catch digraph_family(G) of {'EXIT', _} -> erlang:error(badarg, [G]); L -> ?SET(L, ?FAMILY(?ATOM_TYPE, ?ATOM_TYPE)) end. +-spec(digraph_to_family(Graph, Type) -> Family when + Graph :: digraph(), + Family :: family(), + Type :: type()). digraph_to_family(G, T) -> case {is_type(T), T} of {true, ?SET_OF(?FAMILY(_,_) = Type)} -> @@ -1284,7 +1623,7 @@ rel(Ts, [Type]) -> end; rel(Ts, Sz) -> rel(Ts, Sz, erlang:make_tuple(Sz, ?ATOM_TYPE)). - + atoms_only(Type, I) when ?IS_ATOM_TYPE(?REL_TYPE(I, Type)) -> atoms_only(Type, I+1); atoms_only(Type, I) when I > tuple_size(Type), ?IS_RELATION(Type) -> @@ -1312,7 +1651,7 @@ rel_type([], SL, Type) when ?IS_RELATION(Type) -> %% Inlined. a_func(Ts, T) -> case {T, is_type(T)} of - {[?BINREL(DT, RT) = Type], true} when ?IS_ATOM_TYPE(DT), + {[?BINREL(DT, RT) = Type], true} when ?IS_ATOM_TYPE(DT), ?IS_ATOM_TYPE(RT) -> func(Ts, Type); {[Type], true} -> @@ -1333,16 +1672,16 @@ func([], _X0, L, Type) -> %% Inlined. fam(Ts, T) -> case {T, is_type(T)} of - {[?FAMILY(DT, RT) = Type], true} when ?IS_ATOM_TYPE(DT), + {[?FAMILY(DT, RT) = Type], true} when ?IS_ATOM_TYPE(DT), ?IS_ATOM_TYPE(RT) -> fam2(Ts, Type); {[Type], true} -> func_type(Ts, [], Type, fun(?FAMILY(_,_)) -> true end) end. -fam2([], Type) -> +fam2([], Type) -> ?SET([], Type); -fam2(Ts, Type) -> +fam2(Ts, Type) -> fam2(sort(Ts), Ts, [], Type). fam2([{I,L} | T], I0, SL, Type) when I /= I0 -> @@ -1383,7 +1722,7 @@ setify(E, Type0) -> {Type, OrdSet} = make_element(E, Type0, Type0), ?ORDSET(OrdSet, Type). -is_no_lists(T) when is_tuple(T) -> +is_no_lists(T) when is_tuple(T) -> Sz = tuple_size(T), is_no_lists(T, Sz, Sz, []). @@ -1404,7 +1743,7 @@ create([], T, _T0, L) -> make_element(C, ?ANYTYPE, _T0) -> make_element(C); -make_element(C, Atom, ?ANYTYPE) when ?IS_ATOM_TYPE(Atom), +make_element(C, Atom, ?ANYTYPE) when ?IS_ATOM_TYPE(Atom), not is_list(C), not is_tuple(C) -> {Atom, C}; make_element(C, Atom, Atom) when ?IS_ATOM_TYPE(Atom) -> @@ -1585,12 +1924,12 @@ sympart([H1 | T1], [H2 | T2], L1, L12, L2, T) when H1 == H2 -> sympart([H1 | T1], [H2 | T2], L1, L12, L2, T) -> sympart2(T1, T2, L1, L12, [H2 | L2], T, H1); sympart(S1, [], L1, L12, L2, T) -> - {?SET(reverse(L1, S1), T), - ?SET(reverse(L12), T), + {?SET(reverse(L1, S1), T), + ?SET(reverse(L12), T), ?SET(reverse(L2), T)}; sympart(_, S2, L1, L12, L2, T) -> - {?SET(reverse(L1), T), - ?SET(reverse(L12), T), + {?SET(reverse(L1), T), + ?SET(reverse(L12), T), ?SET(reverse(L2, S2), T)}. sympart1([H1 | T1], T2, L1, L12, L2, T, H2) when H1 < H2 -> @@ -1600,8 +1939,8 @@ sympart1([H1 | T1], T2, L1, L12, L2, T, H2) when H1 == H2 -> sympart1([H1 | T1], T2, L1, L12, L2, T, H2) -> sympart2(T1, T2, L1, L12, [H2 | L2], T, H1); sympart1(_, T2, L1, L12, L2, T, H2) -> - {?SET(reverse(L1), T), - ?SET(reverse(L12), T), + {?SET(reverse(L1), T), + ?SET(reverse(L12), T), ?SET(reverse(L2, [H2 | T2]), T)}. sympart2(T1, [H2 | T2], L1, L12, L2, T, H1) when H1 > H2 -> @@ -1611,8 +1950,8 @@ sympart2(T1, [H2 | T2], L1, L12, L2, T, H1) when H1 == H2 -> sympart2(T1, [H2 | T2], L1, L12, L2, T, H1) -> sympart1(T1, T2, [H1 | L1], L12, L2, T, H2); sympart2(T1, _, L1, L12, L2, T, H1) -> - {?SET(reverse(L1, [H1 | T1]), T), - ?SET(reverse(L12), T), + {?SET(reverse(L1, [H1 | T1]), T), + ?SET(reverse(L12), T), ?SET(reverse(L2), T)}. prod([[E | Es] | Xs], T, L) -> @@ -1660,7 +1999,7 @@ lunion([[] | Ls]) -> lunion(Ls); lunion([S | Ss]) -> umerge(lunion(Ss, last(S), [S], [])); -lunion([]) -> +lunion([]) -> []. lunion([[E] = S | Ss], Last, SL, Ls) when E > Last -> % optimization @@ -1669,7 +2008,7 @@ lunion([S | Ss], Last, SL, Ls) when hd(S) > Last -> lunion(Ss, last(S), [S | SL], Ls); lunion([S | Ss], _Last, SL, Ls) -> lunion(Ss, last(S), [S], [append(reverse(SL)) | Ls]); -lunion([], _Last, SL, Ls) -> +lunion([], _Last, SL, Ls) -> [append(reverse(SL)) | Ls]. %% The empty list is always the first list, if present. @@ -1752,18 +2091,17 @@ relprod(B0, Bx0, By0, A0, L, Ax, [{Bx,By} | B], Ay) when Ay == Bx -> relprod(B0, Bx0, By0, A0, L, _Ax, _B, _Ay) -> relprod2(B0, Bx0, By0, A0, L). -relprod_n({}, _R, _EmptyG, _IsR) -> +relprod_n([], _R, _EmptyG, _IsR) -> {error, badarg}; -relprod_n(RT, R, EmptyR, IsR) -> - RL = tuple_to_list(RT), +relprod_n(RL, R, EmptyR, IsR) -> case domain_type(RL, ?ANYTYPE) of - Error = {error, _Reason} -> + Error = {error, _Reason} -> Error; DType -> Empty = any(fun is_empty_set/1, RL) or EmptyR, RType = range_type(RL, []), Type = ?BINREL(DType, RType), - Prod = + Prod = case Empty of true when DType =:= ?ANYTYPE; RType =:= ?ANYTYPE -> empty_set(); @@ -1771,7 +2109,7 @@ relprod_n(RT, R, EmptyR, IsR) -> ?SET([], Type); false -> TL = ?LIST((relprod_n(RL))), - Sz = tuple_size(RT), + Sz = length(RL), Fun = fun({X,A}) -> {X, flat(Sz, A, [])} end, ?SET(map(Fun, TL), Type) end, @@ -1799,12 +2137,12 @@ flat(N, {T,A}, L) -> domain_type([T | Ts], T0) when ?IS_SET(T) -> case ?TYPE(T) of - ?BINREL(DT, _RT) -> + ?BINREL(DT, _RT) -> case unify_types(DT, T0) of [] -> {error, type_mismatch}; T1 -> domain_type(Ts, T1) end; - ?ANYTYPE -> + ?ANYTYPE -> domain_type(Ts, T0); _ -> {error, badarg} end; @@ -1813,12 +2151,12 @@ domain_type([], T0) -> range_type([T | Ts], L) -> case ?TYPE(T) of - ?BINREL(_DT, RT) -> + ?BINREL(_DT, RT) -> range_type(Ts, [RT | L]); - ?ANYTYPE -> + ?ANYTYPE -> ?ANYTYPE end; -range_type([], L) -> +range_type([], L) -> list_to_tuple(reverse(L)). converse([{A,B} | X], L) -> @@ -1861,7 +2199,7 @@ weak1([E={X,_Y} | Es], Ys, L, X0) when X == X0 -> % when X < Y weak1(Es, Ys, L, X) -> weak(Es, Ys, [{X,X} | L]). -weak2([E={X,_Y} | Es], Ys, L, X0) when X == X0 -> % when X < _Y +weak2([E={X,_Y} | Es], Ys, L, X0) when X == X0 -> % when X < _Y weak2(Es, Ys, [E | L], X); weak2(Es, Ys, L, _X) -> weak(Es, Ys, L). @@ -1910,7 +2248,7 @@ restrict_n(I, [T | Ts], Key, Keys, L) -> end; restrict_n(_I, _Ts, _Key, _Keys, L) -> L. - + restrict_n(I, K, Ts, [Key | Keys], L, E) when K > Key -> restrict_n(I, K, Ts, Keys, L, E); restrict_n(I, K, Ts, [Key | Keys], L, E) when K == Key -> @@ -1933,7 +2271,7 @@ restrict([{K,E} | Ts], _Key, Keys, L) -> restrict(Ts, K, Keys, L, E); restrict(_Ts, _Key, _Keys, L) -> L. - + restrict(Ts, K, [Key | Keys], L, E) when K > Key -> restrict(Ts, K, Keys, L, E); restrict(Ts, K, [Key | Keys], L, E) when K == Key -> @@ -1956,7 +2294,7 @@ diff_restrict_n(I, _Ts, _Key, _Keys, L) when I =:= 1 -> reverse(L); diff_restrict_n(_I, _Ts, _Key, _Keys, L) -> sort(L). - + diff_restrict_n(I, K, Ts, [Key | Keys], L, T) when K > Key -> diff_restrict_n(I, K, Ts, Keys, L, T); diff_restrict_n(I, K, Ts, [Key | Keys], L, _T) when K == Key -> @@ -1981,7 +2319,7 @@ diff_restrict([{K,E} | Ts], _Key, Keys, L) -> diff_restrict(Ts, K, Keys, L, E); diff_restrict(_Ts, _Key, _Keys, L) -> L. - + diff_restrict(Ts, K, [Key | Keys], L, E) when K > Key -> diff_restrict(Ts, K, Keys, L, E); diff_restrict(Ts, K, [Key | Keys], L, _E) when K == Key -> @@ -2041,7 +2379,7 @@ external_fun({external, Function}) when is_atom(Function) -> false; external_fun({external, Fun}) -> Fun; -external_fun(_) -> +external_fun(_) -> false. %% Inlined. @@ -2121,7 +2459,7 @@ partition3_n(I, _Ts, _Key, _Keys, L1, L2) when I =:= 1 -> [reverse(L1) | reverse(L2)]; partition3_n(_I, _Ts, _Key, _Keys, L1, L2) -> [sort(L1) | sort(L2)]. - + partition3_n(I, K, Ts, [Key | Keys], L1, L2, T) when K > Key -> partition3_n(I, K, Ts, Keys, L1, L2, T); partition3_n(I, K, Ts, [Key | Keys], L1, L2, T) when K == Key -> @@ -2146,7 +2484,7 @@ partition3([{K,E} | Ts], _Key, Keys, L1, L2) -> partition3(Ts, K, Keys, L1, L2, E); partition3(_Ts, _Key, _Keys, L1, L2) -> [L1 | L2]. - + partition3(Ts, K, [Key | Keys], L1, L2, E) when K > Key -> partition3(Ts, K, Keys, L1, L2, E); partition3(Ts, K, [Key | Keys], L1, L2, E) when K == Key -> @@ -2192,7 +2530,7 @@ join_element(E1, E2, I2) -> join_element2([B | Bs], C, I2) when C =/= I2 -> [B | join_element2(Bs, C+1, I2)]; -join_element2([_ | Bs], _C, _I2) -> +join_element2([_ | Bs], _C, _I2) -> Bs. family2rel([{X,S} | F], L) -> @@ -2297,7 +2635,7 @@ check_function([{X,_} | XL], R) -> check_function(X, XL, R); check_function([], R) -> R. - + check_function(X0, [{X,_} | XL], R) when X0 /= X -> check_function(X, XL, R); check_function(X0, [{X,_} | _XL], _R) when X0 == X -> @@ -2371,14 +2709,14 @@ term2set(T, Type) -> ?ORDSET(T, Type). fam2digraph(F, G) -> - Fun = fun({From, ToL}) -> + Fun = fun({From, ToL}) -> digraph:add_vertex(G, From), Fun2 = fun(To) -> digraph:add_vertex(G, To), case digraph:add_edge(G, From, To) of - {error, {bad_edge, _}} -> + {error, {bad_edge, _}} -> throw({error, cyclic}); - _ -> + _ -> true end end, @@ -2397,7 +2735,7 @@ digraph_fam([V | Vs], V0, G, L) when V /= V0 -> digraph_fam([], _V0, _G, L) -> reverse(L). -%% -> bool() +%% -> boolean() check_fun(T, F, FunT) -> true = is_type(FunT), {NT, _MaxI} = number_tuples(T, 1), @@ -2424,7 +2762,7 @@ check_for_sort(T, _I) when T =:= ?ANYTYPE -> check_for_sort(T, I) when ?IS_RELATION(T), I =< ?REL_ARITY(T), I >= 1 -> I > 1; check_for_sort(_T, _I) -> - error. + error. inverse_substitution(L, Fun, Sort) -> %% One easily sees that the inverse of the tuples created by @@ -2477,11 +2815,11 @@ match_types(Type1, Type2) -> match_types1(Type1, Type2). match_types1(Atom, Atom) when ?IS_ATOM_TYPE(Atom) -> true; -match_types1(?ANYTYPE, _) -> +match_types1(?ANYTYPE, _) -> true; -match_types1(_, ?ANYTYPE) -> +match_types1(_, ?ANYTYPE) -> true; -match_types1(?SET_OF(Type1), ?SET_OF(Type2)) -> +match_types1(?SET_OF(Type1), ?SET_OF(Type2)) -> match_types1(Type1, Type2); match_types1(T1, T2) when tuple_size(T1) =:= tuple_size(T2) -> match_typesl(tuple_size(T1), T1, T2); diff --git a/lib/stdlib/src/string.erl b/lib/stdlib/src/string.erl index 264348180f..30eac4f07d 100644 --- a/lib/stdlib/src/string.erl +++ b/lib/stdlib/src/string.erl @@ -29,23 +29,23 @@ %%--------------------------------------------------------------------------- --type direction() :: 'left' | 'right' | 'both'. - -%%--------------------------------------------------------------------------- - %% Robert's bit %% len(String) %% Return the length of a string. --spec len(string()) -> non_neg_integer(). +-spec len(String) -> Length when + String :: string(), + Length :: non_neg_integer(). len(S) -> length(S). %% equal(String1, String2) %% Test if 2 strings are equal. --spec equal(string(), string()) -> boolean(). +-spec equal(String1, String2) -> boolean() when + String1 :: string(), + String2 :: string(). equal(S, S) -> true; equal(_, _) -> false. @@ -53,7 +53,10 @@ equal(_, _) -> false. %% concat(String1, String2) %% Concatenate 2 strings. --spec concat(string(), string()) -> string(). +-spec concat(String1, String2) -> String3 when + String1 :: string(), + String2 :: string(), + String3 :: string(). concat(S1, S2) -> S1 ++ S2. @@ -61,7 +64,10 @@ concat(S1, S2) -> S1 ++ S2. %% rchr(String, Char) %% Return the first/last index of the character in a string. --spec chr(string(), char()) -> non_neg_integer(). +-spec chr(String, Character) -> Index when + String :: string(), + Character :: char(), + Index :: non_neg_integer(). chr(S, C) when is_integer(C) -> chr(S, C, 1). @@ -69,7 +75,10 @@ chr([C|_Cs], C, I) -> I; chr([_|Cs], C, I) -> chr(Cs, C, I+1); chr([], _C, _I) -> 0. --spec rchr(string(), char()) -> non_neg_integer(). +-spec rchr(String, Character) -> Index when + String :: string(), + Character :: char(), + Index :: non_neg_integer(). rchr(S, C) when is_integer(C) -> rchr(S, C, 1, 0). @@ -85,7 +94,10 @@ rchr([], _C, _I, L) -> L. %% Return the first/last index of the sub-string in a string. %% index/2 is kept for backwards compatibility. --spec str(string(), string()) -> non_neg_integer(). +-spec str(String, SubString) -> Index when + String :: string(), + SubString :: string(), + Index :: non_neg_integer(). str(S, Sub) when is_list(Sub) -> str(S, Sub, 1). @@ -97,7 +109,10 @@ str([C|S], [C|Sub], I) -> str([_|S], Sub, I) -> str(S, Sub, I+1); str([], _Sub, _I) -> 0. --spec rstr(string(), string()) -> non_neg_integer(). +-spec rstr(String, SubString) -> Index when + String :: string(), + SubString :: string(), + Index :: non_neg_integer(). rstr(S, Sub) when is_list(Sub) -> rstr(S, Sub, 1, 0). @@ -116,7 +131,10 @@ prefix(Pre, String) when is_list(Pre), is_list(String) -> false. %% span(String, Chars) -> Length. %% cspan(String, Chars) -> Length. --spec span(string(), string()) -> non_neg_integer(). +-spec span(String, Chars) -> Length when + String :: string(), + Chars :: string(), + Length :: non_neg_integer(). span(S, Cs) when is_list(Cs) -> span(S, Cs, 0). @@ -127,7 +145,10 @@ span([C|S], Cs, I) -> end; span([], _Cs, I) -> I. --spec cspan(string(), string()) -> non_neg_integer(). +-spec cspan(String, Chars) -> Length when + String :: string(), + Chars :: string(), + Length :: non_neg_integer(). cspan(S, Cs) when is_list(Cs) -> cspan(S, Cs, 0). @@ -142,14 +163,21 @@ cspan([], _Cs, I) -> I. %% substr(String, Start, Length) %% Extract a sub-string from String. --spec substr(string(), pos_integer()) -> string(). +-spec substr(String, Start) -> SubString when + String :: string(), + SubString :: string(), + Start :: pos_integer(). substr(String, 1) when is_list(String) -> String; substr(String, S) when is_integer(S), S > 1 -> substr2(String, S). --spec substr(string(), pos_integer(), non_neg_integer()) -> string(). +-spec substr(String, Start, Length) -> SubString when + String :: string(), + SubString :: string(), + Start :: pos_integer(), + Length :: non_neg_integer(). substr(String, S, L) when is_integer(S), S >= 1, is_integer(L), L >= 0 -> substr1(substr2(String, S), L). @@ -163,7 +191,10 @@ substr2([_|String], S) -> substr2(String, S-1). %% tokens(String, Seperators). %% Return a list of tokens seperated by characters in Seperators. --spec tokens(string(), string()) -> [[char(),...]]. +-spec tokens(String, SeparatorList) -> Tokens when + String :: string(), + SeparatorList :: string(), + Tokens :: [Token :: nonempty_string()]. tokens(S, Seps) -> tokens1(S, Seps, []). @@ -184,11 +215,18 @@ tokens2([C|S], Seps, Toks, Cs) -> tokens2([], _Seps, Toks, Cs) -> reverse([reverse(Cs)|Toks]). --spec chars(char(), non_neg_integer()) -> string(). +-spec chars(Character, Number) -> String when + Character :: char(), + Number :: non_neg_integer(), + String :: string(). chars(C, N) -> chars(C, N, []). --spec chars(char(), non_neg_integer(), string()) -> string(). +-spec chars(Character, Number, Tail) -> String when + Character :: char(), + Number :: non_neg_integer(), + Tail :: string(), + String :: string(). chars(C, N, Tail) when N > 0 -> chars(C, N-1, [C|Tail]); @@ -199,7 +237,10 @@ chars(C, 0, Tail) when is_integer(C) -> %%% COPIES %%% --spec copies(string(), non_neg_integer()) -> string(). +-spec copies(String, Number) -> Copies when + String :: string(), + Copies :: string(), + Number :: non_neg_integer(). copies(CharList, Num) when is_list(CharList), is_integer(Num), Num >= 0 -> copies(CharList, Num, []). @@ -211,11 +252,16 @@ copies(CharList, Num, R) -> %%% WORDS %%% --spec words(string()) -> pos_integer(). +-spec words(String) -> Count when + String :: string(), + Count :: pos_integer(). words(String) -> words(String, $\s). --spec words(string(), char()) -> pos_integer(). +-spec words(String, Character) -> Count when + String :: string(), + Character :: char(), + Count :: pos_integer(). words(String, Char) when is_integer(Char) -> w_count(strip(String, both, Char), Char, 0). @@ -226,11 +272,18 @@ w_count([_H|T], Char, Num) -> w_count(T, Char, Num). %%% SUB_WORDS %%% --spec sub_word(string(), integer()) -> string(). +-spec sub_word(String, Number) -> Word when + String :: string(), + Word :: string(), + Number :: integer(). sub_word(String, Index) -> sub_word(String, Index, $\s). --spec sub_word(string(), integer(), char()) -> string(). +-spec sub_word(String, Number, Character) -> Word when + String :: string(), + Word :: string(), + Number :: integer(), + Character :: char(). sub_word(String, Index, Char) when is_integer(Index), is_integer(Char) -> case words(String, Char) of @@ -254,14 +307,21 @@ s_word([_|T],Stop,Char,Index,Res) when Index < Stop -> strip(String) -> strip(String, both). --spec strip(string(), direction()) -> string(). +-spec strip(String, Direction) -> Stripped when + String :: string(), + Stripped :: string(), + Direction :: left | right | both. strip(String, left) -> strip_left(String, $\s); strip(String, right) -> strip_right(String, $\s); strip(String, both) -> strip_right(strip_left(String, $\s), $\s). --spec strip(string(), direction(), char()) -> string(). +-spec strip(String, Direction, Character) -> Stripped when + String :: string(), + Stripped :: string(), + Direction :: left | right | both, + Character :: char(). strip(String, right, Char) -> strip_right(String, Char); strip(String, left, Char) -> strip_left(String, Char); @@ -285,11 +345,18 @@ strip_right([], Sc) when is_integer(Sc) -> %%% LEFT %%% --spec left(string(), non_neg_integer()) -> string(). +-spec left(String, Number) -> Left when + String :: string(), + Left :: string(), + Number :: non_neg_integer(). left(String, Len) when is_integer(Len) -> left(String, Len, $\s). --spec left(string(), non_neg_integer(), char()) -> string(). +-spec left(String, Number, Character) -> Left when + String :: string(), + Left :: string(), + Number :: non_neg_integer(), + Character :: char(). left(String, Len, Char) when is_integer(Char) -> Slen = length(String), @@ -303,11 +370,18 @@ l_pad(String, Num, Char) -> String ++ chars(Char, Num). %%% RIGHT %%% --spec right(string(), non_neg_integer()) -> string(). +-spec right(String, Number) -> Right when + String :: string(), + Right :: string(), + Number :: non_neg_integer(). right(String, Len) when is_integer(Len) -> right(String, Len, $\s). --spec right(string(), non_neg_integer(), char()) -> string(). +-spec right(String, Number, Character) -> Right when + String :: string(), + Right :: string(), + Number :: non_neg_integer(), + Character :: char(). right(String, Len, Char) when is_integer(Char) -> Slen = length(String), @@ -321,11 +395,18 @@ r_pad(String, Num, Char) -> chars(Char, Num, String). %%% CENTRE %%% --spec centre(string(), non_neg_integer()) -> string(). +-spec centre(String, Number) -> Centered when + String :: string(), + Centered :: string(), + Number :: non_neg_integer(). centre(String, Len) when is_integer(Len) -> centre(String, Len, $\s). --spec centre(string(), non_neg_integer(), char()) -> string(). +-spec centre(String, Number, Character) -> Centered when + String :: string(), + Centered :: string(), + Number :: non_neg_integer(), + Character :: char(). centre(String, 0, Char) when is_list(String), is_integer(Char) -> []; % Strange cases to centre string @@ -341,11 +422,18 @@ centre(String, Len, Char) when is_integer(Char) -> %%% SUB_STRING %%% --spec sub_string(string(), pos_integer()) -> string(). +-spec sub_string(String, Start) -> SubString when + String :: string(), + SubString :: string(), + Start :: pos_integer(). sub_string(String, Start) -> substr(String, Start). --spec sub_string(string(), pos_integer(), pos_integer()) -> string(). +-spec sub_string(String, Start, Stop) -> SubString when + String :: string(), + SubString :: string(), + Start :: pos_integer(), + Stop :: pos_integer(). sub_string(String, Start, Stop) -> substr(String, Start, Stop - Start + 1). @@ -370,23 +458,34 @@ to_upper_char(C) when is_integer(C), 16#F8 =< C, C =< 16#FE -> to_upper_char(C) -> C. --spec to_lower(string()) -> string() - ; (char()) -> char(). +-spec to_lower(String) -> Result when + String :: string(), + Result :: string() + ; (Char) -> CharResult when + Char :: char(), + CharResult :: char(). to_lower(S) when is_list(S) -> [to_lower_char(C) || C <- S]; to_lower(C) when is_integer(C) -> to_lower_char(C). --spec to_upper(string()) -> string() - ; (char()) -> char(). +-spec to_upper(String) -> Result when + String :: string(), + Result :: string() + ; (Char) -> CharResult when + Char :: char(), + CharResult :: char(). to_upper(S) when is_list(S) -> [to_upper_char(C) || C <- S]; to_upper(C) when is_integer(C) -> to_upper_char(C). --spec join([string()], string()) -> string(). +-spec join(StringList, Separator) -> String when + StringList :: [string()], + Separator :: string(), + String :: string(). join([], Sep) when is_list(Sep) -> []; diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl index 3c5800effa..9da0d52f8c 100644 --- a/lib/stdlib/src/supervisor.erl +++ b/lib/stdlib/src/supervisor.erl @@ -27,27 +27,34 @@ which_children/1, count_children/1, check_childspecs/1]). --export([behaviour_info/1]). - %% Internal exports -export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3]). -export([handle_cast/2]). %%-------------------------------------------------------------------------- --export_type([child_spec/0, del_err/0, startchild_ret/0, strategy/0]). +-export_type([child_spec/0, startchild_ret/0, strategy/0]). %%-------------------------------------------------------------------------- --type child_id() :: pid() | 'undefined'. --type mfargs() :: {module(), atom(), [term()] | undefined}. +-type child() :: pid() | 'undefined'. +-type child_id() :: term(). +-type mfargs() :: {M :: module(), F :: atom(), A :: [term()] | undefined}. -type modules() :: [module()] | 'dynamic'. -type restart() :: 'permanent' | 'transient' | 'temporary'. -type shutdown() :: 'brutal_kill' | timeout(). -type worker() :: 'worker' | 'supervisor'. --type sup_name() :: {'local', atom()} | {'global', atom()}. --type sup_ref() :: atom() | {atom(), atom()} | {'global', atom()} | pid(). --type child_spec() :: {term(),mfargs(),restart(),shutdown(),worker(),modules()}. +-type sup_name() :: {'local', Name :: atom()} | {'global', Name :: atom()}. +-type sup_ref() :: (Name :: atom()) + | {Name :: atom(), Node :: node()} + | {'global', Name :: atom()} + | pid(). +-type child_spec() :: {Id :: child_id(), + StartFunc :: mfargs(), + Restart :: restart(), + Shutdown :: shutdown(), + Type :: worker(), + Modules :: modules()}. -type strategy() :: 'one_for_all' | 'one_for_one' | 'rest_for_one' | 'simple_one_for_one'. @@ -55,21 +62,23 @@ %%-------------------------------------------------------------------------- -record(child, {% pid is undefined when child is not running - pid = undefined :: child_id(), + pid = undefined :: child(), name, mfargs :: mfargs(), restart_type :: restart(), shutdown :: shutdown(), child_type :: worker(), modules = [] :: modules()}). --type child() :: #child{}. +-type child_rec() :: #child{}. -define(DICT, dict). +-define(SETS, sets). +-define(SET, set). -record(state, {name, strategy :: strategy(), - children = [] :: [child()], - dynamics :: ?DICT() | list(), + children = [] :: [child_rec()], + dynamics :: ?DICT() | ?SET(), intensity :: non_neg_integer(), period :: pos_integer(), restarts = [], @@ -79,14 +88,12 @@ -define(is_simple(State), State#state.strategy =:= simple_one_for_one). -%%-------------------------------------------------------------------------- - --spec behaviour_info(atom()) -> 'undefined' | [{atom(), arity()}]. - -behaviour_info(callbacks) -> - [{init,1}]; -behaviour_info(_Other) -> - undefined. +-callback init(Args :: term()) -> + {ok, {{RestartStrategy :: strategy(), + MaxR :: non_neg_integer(), + MaxT :: non_neg_integer()}, + [ChildSpec :: child_spec()]}} + | ignore. %%% --------------------------------------------------- %%% This is a general process supervisor built upon gen_server.erl. @@ -97,11 +104,16 @@ behaviour_info(_Other) -> -type startlink_err() :: {'already_started', pid()} | 'shutdown' | term(). -type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}. --spec start_link(module(), term()) -> startlink_ret(). +-spec start_link(Module, Args) -> startlink_ret() when + Module :: module(), + Args :: term(). start_link(Mod, Args) -> gen_server:start_link(supervisor, {self, Mod, Args}, []). --spec start_link(sup_name(), module(), term()) -> startlink_ret(). +-spec start_link(SupName, Module, Args) -> startlink_ret() when + SupName :: sup_name(), + Module :: module(), + Args :: term(). start_link(SupName, Mod, Args) -> gen_server:start_link(SupName, supervisor, {SupName, Mod, Args}, []). @@ -109,24 +121,33 @@ start_link(SupName, Mod, Args) -> %%% Interface functions. %%% --------------------------------------------------- --type info() :: term(). -type startchild_err() :: 'already_present' - | {'already_started', child_id()} | term(). --type startchild_ret() :: {'ok', child_id()} | {'ok', child_id(), info()} + | {'already_started', Child :: child()} | term(). +-type startchild_ret() :: {'ok', Child :: child()} + | {'ok', Child :: child(), Info :: term()} | {'error', startchild_err()}. --spec start_child(sup_ref(), child_spec() | [term()]) -> startchild_ret(). +-spec start_child(SupRef, ChildSpec) -> startchild_ret() when + SupRef :: sup_ref(), + ChildSpec :: child_spec() | (List :: [term()]). start_child(Supervisor, ChildSpec) -> call(Supervisor, {start_child, ChildSpec}). --type restart_err() :: 'running' | 'not_found' | 'simple_one_for_one' | term(). --spec restart_child(sup_ref(), term()) -> - {'ok', child_id()} | {'ok', child_id(), info()} | {'error', restart_err()}. +-spec restart_child(SupRef, Id) -> Result when + SupRef :: sup_ref(), + Id :: child_id(), + Result :: {'ok', Child :: child()} + | {'ok', Child :: child(), Info :: term()} + | {'error', Error}, + Error :: 'running' | 'not_found' | 'simple_one_for_one' | term(). restart_child(Supervisor, Name) -> call(Supervisor, {restart_child, Name}). --type del_err() :: 'running' | 'not_found' | 'simple_one_for_one'. --spec delete_child(sup_ref(), term()) -> 'ok' | {'error', del_err()}. +-spec delete_child(SupRef, Id) -> Result when + SupRef :: sup_ref(), + Id :: child_id(), + Result :: 'ok' | {'error', Error}, + Error :: 'running' | 'not_found' | 'simple_one_for_one'. delete_child(Supervisor, Name) -> call(Supervisor, {delete_child, Name}). @@ -137,22 +158,39 @@ delete_child(Supervisor, Name) -> %% way (maybe killed). %%----------------------------------------------------------------- --type term_err() :: 'not_found' | 'simple_one_for_one'. --spec terminate_child(sup_ref(), term()) -> 'ok' | {'error', term_err()}. +-spec terminate_child(SupRef, Id) -> Result when + SupRef :: sup_ref(), + Id :: pid() | child_id(), + Result :: 'ok' | {'error', Error}, + Error :: 'not_found' | 'simple_one_for_one'. terminate_child(Supervisor, Name) -> call(Supervisor, {terminate_child, Name}). --spec which_children(sup_ref()) -> [{term(), child_id(), worker(), modules()}]. +-spec which_children(SupRef) -> [{Id,Child,Type,Modules}] when + SupRef :: sup_ref(), + Id :: child_id() | undefined, + Child :: child(), + Type :: worker(), + Modules :: modules(). which_children(Supervisor) -> call(Supervisor, which_children). +-spec count_children(SupRef) -> PropListOfCounts when + SupRef :: sup_ref(), + PropListOfCounts :: [Count], + Count :: {specs, ChildSpecCount :: non_neg_integer()} + | {active, ActiveProcessCount :: non_neg_integer()} + | {supervisors, ChildSupervisorCount :: non_neg_integer()} + |{workers, ChildWorkerCount :: non_neg_integer()}. count_children(Supervisor) -> call(Supervisor, count_children). call(Supervisor, Req) -> gen_server:call(Supervisor, Req, infinity). --spec check_childspecs([child_spec()]) -> 'ok' | {'error', term()}. +-spec check_childspecs(ChildSpecs) -> Result when + ChildSpecs :: [child_spec()], + Result :: 'ok' | {'error', Error :: term()}. check_childspecs(ChildSpecs) when is_list(ChildSpecs) -> case check_startspec(ChildSpecs) of {ok, _} -> ok; @@ -220,12 +258,12 @@ init_dynamic(_State, StartSpec) -> %%----------------------------------------------------------------- %% Func: start_children/2 -%% Args: Children = [child()] in start order +%% Args: Children = [child_rec()] in start order %% SupName = {local, atom()} | {global, atom()} | {pid(), Mod} %% Purpose: Start all children. The new list contains #child's %% with pids. %% Returns: {ok, NChildren} | {error, NChildren} -%% NChildren = [child()] in termination order (reversed +%% NChildren = [child_rec()] in termination order (reversed %% start order) %%----------------------------------------------------------------- start_children(Children, SupName) -> start_children(Children, [], SupName). @@ -297,8 +335,26 @@ handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) -> {reply, What, State} end; -%%% The requests terminate_child, delete_child and restart_child are -%%% invalid for simple_one_for_one supervisors. +%% terminate_child for simple_one_for_one can only be done with pid +handle_call({terminate_child, Name}, _From, State) when not is_pid(Name), + ?is_simple(State) -> + {reply, {error, simple_one_for_one}, State}; + +handle_call({terminate_child, Name}, _From, State) -> + case get_child(Name, State, ?is_simple(State)) of + {value, Child} -> + case do_terminate(Child, State#state.name) of + #child{restart_type=RT} when RT=:=temporary; ?is_simple(State) -> + {reply, ok, state_del_child(Child, State)}; + NChild -> + {reply, ok, replace_child(NChild, State)} + end; + false -> + {reply, {error, not_found}, State} + end; + +%%% The requests delete_child and restart_child are invalid for +%%% simple_one_for_one supervisors. handle_call({_Req, _Data}, _From, State) when ?is_simple(State) -> {reply, {error, simple_one_for_one}, State}; @@ -341,21 +397,12 @@ handle_call({delete_child, Name}, _From, State) -> {reply, {error, not_found}, State} end; -handle_call({terminate_child, Name}, _From, State) -> - case get_child(Name, State) of - {value, Child} -> - NChild = do_terminate(Child, State#state.name), - {reply, ok, replace_child(NChild, State)}; - _ -> - {reply, {error, not_found}, State} - end; - handle_call(which_children, _From, #state{children = [#child{restart_type = temporary, child_type = CT, modules = Mods}]} = State) when ?is_simple(State) -> - Reply = lists:map(fun(Pid) -> {undefined, Pid, CT, Mods} end, dynamics_db(temporary, - State#state.dynamics)), + Reply = lists:map(fun(Pid) -> {undefined, Pid, CT, Mods} end, + ?SETS:to_list(dynamics_db(temporary, State#state.dynamics))), {reply, Reply, State}; handle_call(which_children, _From, #state{children = [#child{restart_type = RType, @@ -380,7 +427,7 @@ handle_call(count_children, _From, #state{children = [#child{restart_type = temp child_type = CT}]} = State) when ?is_simple(State) -> {Active, Count} = - lists:foldl(fun(Pid, {Alive, Tot}) -> + ?SETS:fold(fun(Pid, {Alive, Tot}) -> if is_pid(Pid) -> {Alive+1, Tot +1}; true -> {Alive, Tot + 1} end end, {0, 0}, dynamics_db(temporary, State#state.dynamics)), @@ -610,6 +657,9 @@ do_restart(_, normal, Child, State) -> do_restart(_, shutdown, Child, State) -> NState = state_del_child(Child, State), {ok, NState}; +do_restart(_, {shutdown, _Term}, Child, State) -> + NState = state_del_child(Child, State), + {ok, NState}; do_restart(transient, Reason, Child, State) -> report_error(child_terminated, Reason, Child, State#state.name), restart(Child, State); @@ -676,14 +726,21 @@ restart(one_for_all, Child, State) -> %%----------------------------------------------------------------- %% Func: terminate_children/2 -%% Args: Children = [child()] in termination order +%% Args: Children = [child_rec()] in termination order %% SupName = {local, atom()} | {global, atom()} | {pid(),Mod} -%% Returns: NChildren = [child()] in +%% Returns: NChildren = [child_rec()] in %% startup order (reversed termination order) %%----------------------------------------------------------------- terminate_children(Children, SupName) -> terminate_children(Children, SupName, []). +%% Temporary children should not be restarted and thus should +%% be skipped when building the list of terminated children, although +%% we do want them to be shut down as many functions from this module +%% use this function to just clear everything. +terminate_children([Child = #child{restart_type=temporary} | Children], SupName, Res) -> + do_terminate(Child, SupName), + terminate_children(Children, SupName, Res); terminate_children([Child | Children], SupName, Res) -> NChild = do_terminate(Child, SupName), terminate_children(Children, SupName, [NChild | Res]); @@ -791,24 +848,27 @@ save_child(Child, #state{children = Children} = State) -> State#state{children = [Child |Children]}. save_dynamic_child(temporary, Pid, _, #state{dynamics = Dynamics} = State) -> - State#state{dynamics = [Pid | dynamics_db(temporary, Dynamics)]}; + State#state{dynamics = ?SETS:add_element(Pid, dynamics_db(temporary, Dynamics))}; save_dynamic_child(RestartType, Pid, Args, #state{dynamics = Dynamics} = State) -> State#state{dynamics = ?DICT:store(Pid, Args, dynamics_db(RestartType, Dynamics))}. dynamics_db(temporary, undefined) -> - []; + ?SETS:new(); dynamics_db(_, undefined) -> ?DICT:new(); dynamics_db(_,Dynamics) -> Dynamics. -dynamic_child_args(_, Dynamics) when is_list(Dynamics)-> - {ok, undefined}; dynamic_child_args(Pid, Dynamics) -> - ?DICT:find(Pid, Dynamics). + case ?SETS:is_set(Dynamics) of + true -> + {ok, undefined}; + false -> + ?DICT:find(Pid, Dynamics) + end. state_del_child(#child{pid = Pid, restart_type = temporary}, State) when ?is_simple(State) -> - NDynamics = lists:delete(Pid, dynamics_db(temporary, State#state.dynamics)), + NDynamics = ?SETS:del_element(Pid, dynamics_db(temporary, State#state.dynamics)), State#state{dynamics = NDynamics}; state_del_child(#child{pid = Pid, restart_type = RType}, State) when ?is_simple(State) -> NDynamics = ?DICT:erase(Pid, dynamics_db(RType, State#state.dynamics)), @@ -817,8 +877,12 @@ state_del_child(Child, State) -> NChildren = del_child(Child#child.name, State#state.children), State#state{children = NChildren}. +del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name, Ch#child.restart_type =:= temporary -> + Chs; del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name -> [Ch#child{pid = undefined} | Chs]; +del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid, Ch#child.restart_type =:= temporary -> + Chs; del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid -> [Ch#child{pid = undefined} | Chs]; del_child(Name, [Ch|Chs]) -> @@ -841,7 +905,31 @@ split_child(_, [], After) -> {lists:reverse(After), []}. get_child(Name, State) -> + get_child(Name, State, false). +get_child(Pid, State, AllowPid) when AllowPid, is_pid(Pid) -> + get_dynamic_child(Pid, State); +get_child(Name, State, _) -> lists:keysearch(Name, #child.name, State#state.children). + +get_dynamic_child(Pid, #state{children=[Child], dynamics=Dynamics}) -> + case is_dynamic_pid(Pid, dynamics_db(Child#child.restart_type, Dynamics)) of + true -> + {value, Child#child{pid=Pid}}; + false -> + case erlang:is_process_alive(Pid) of + true -> false; + false -> {value, Child} + end + end. + +is_dynamic_pid(Pid, Dynamics) -> + case ?SETS:is_set(Dynamics) of + true -> + ?SETS:is_element(Pid, Dynamics); + false -> + ?DICT:is_key(Pid, Dynamics) + end. + replace_child(Child, State) -> Chs = do_replace_child(Child, State#state.children), State#state{children = Chs}. @@ -916,7 +1004,7 @@ supname(N, _) -> N. %%% Shutdown = integer() | infinity | brutal_kill %%% ChildType = supervisor | worker %%% Modules = [atom()] | dynamic -%%% Returns: {ok, [child()]} | Error +%%% Returns: {ok, [child_rec()]} | Error %%% ------------------------------------------------------ check_startspec(Children) -> check_startspec(Children, []). diff --git a/lib/stdlib/src/supervisor_bridge.erl b/lib/stdlib/src/supervisor_bridge.erl index 3d2bd2c9a5..e8405ab9a4 100644 --- a/lib/stdlib/src/supervisor_bridge.erl +++ b/lib/stdlib/src/supervisor_bridge.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -22,15 +22,14 @@ %% External exports -export([start_link/2, start_link/3]). --export([behaviour_info/1]). %% Internal exports -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]). -export([code_change/3]). -behaviour_info(callbacks) -> - [{init,1},{terminate,2}]; -behaviour_info(_Other) -> - undefined. +-callback init(Args :: term()) -> + {ok, Pid :: pid(), State :: term()} | ignore | {error, Error :: term()}. +-callback terminate(Reason :: (shutdown | term()), State :: term()) -> + Ignored :: term(). %%%----------------------------------------------------------------- %%% This is a rewrite of supervisor_bridge from BS.3. @@ -49,9 +48,25 @@ behaviour_info(_Other) -> %%%----------------------------------------------------------------- -record(state, {mod, pid, child_state, name}). +-spec start_link(Module, Args) -> Result when + Module :: module(), + Args :: term(), + Result :: {ok, Pid} | ignore | {error, Error}, + Error :: {already_started, Pid} | term(), + Pid :: pid(). + start_link(Mod, StartArgs) -> gen_server:start_link(supervisor_bridge, [Mod, StartArgs, self], []). +-spec start_link(SupBridgeName, Module, Args) -> Result when + SupBridgeName :: {local, Name} | {global, Name}, + Name :: atom(), + Module :: module(), + Args :: term(), + Result :: {ok, Pid} | ignore | {error, Error}, + Error :: {already_started, Pid} | term(), + Pid :: pid(). + start_link(Name, Mod, StartArgs) -> gen_server:start_link(Name, supervisor_bridge, [Mod, StartArgs, Name], []). diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl index 12209c16d7..f34201604c 100644 --- a/lib/stdlib/src/sys.erl +++ b/lib/stdlib/src/sys.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -33,22 +33,74 @@ %%----------------------------------------------------------------- -type name() :: pid() | atom() | {'global', atom()}. --type system_event() :: {'in', _Msg} | {'in', _Msg, _From} | {'out', _Msg, _To}. +-type system_event() :: {'in', Msg :: _} + | {'in', Msg :: _, From :: _} + | {'out', Msg :: _, To :: _} + | term(). +-opaque dbg_opt() :: list(). +-type dbg_fun() :: fun((FuncState :: _, + Event :: system_event(), + ProcState :: _) -> 'done' | (NewFuncState :: _)). %%----------------------------------------------------------------- %% System messages %%----------------------------------------------------------------- +-spec suspend(Name) -> Void when + Name :: name(), + Void :: term(). suspend(Name) -> send_system_msg(Name, suspend). +-spec suspend(Name, Timeout) -> Void when + Name :: name(), + Timeout :: timeout(), + Void :: term(). suspend(Name, Timeout) -> send_system_msg(Name, suspend, Timeout). +-spec resume(Name) -> Void when + Name :: name(), + Void :: term(). resume(Name) -> send_system_msg(Name, resume). +-spec resume(Name, Timeout) -> Void when + Name :: name(), + Timeout :: timeout(), + Void :: term(). resume(Name, Timeout) -> send_system_msg(Name, resume, Timeout). +-spec get_status(Name) -> Status when + Name :: name(), + Status :: {status, Pid :: pid(), {module, Module :: module()}, [SItem]}, + SItem :: (PDict :: [{Key :: term(), Value :: term()}]) + | (SysState :: 'running' | 'suspended') + | (Parent :: pid()) + | (Dbg :: dbg_opt()) + | (Misc :: term()). get_status(Name) -> send_system_msg(Name, get_status). +-spec get_status(Name, Timeout) -> Status when + Name :: name(), + Timeout :: timeout(), + Status :: {status, Pid :: pid(), {module, Module :: module()}, [SItem]}, + SItem :: (PDict :: [{Key :: term(), Value :: term()}]) + | (SysState :: 'running' | 'suspended') + | (Parent :: pid()) + | (Dbg :: dbg_opt()) + | (Misc :: term()). get_status(Name, Timeout) -> send_system_msg(Name, get_status, Timeout). +-spec change_code(Name, Module, OldVsn, Extra) -> 'ok' | {error, Reason} when + Name :: name(), + Module :: module(), + OldVsn :: 'undefined' | term(), + Extra :: term(), + Reason :: term(). change_code(Name, Mod, Vsn, Extra) -> send_system_msg(Name, {change_code, Mod, Vsn, Extra}). +-spec change_code(Name, Module, OldVsn, Extra, Timeout) -> + 'ok' | {error, Reason} when + Name :: name(), + Module :: module(), + OldVsn :: 'undefined' | term(), + Extra :: term(), + Timeout :: timeout(), + Reason :: term(). change_code(Name, Mod, Vsn, Extra, Timeout) -> send_system_msg(Name, {change_code, Mod, Vsn, Extra}, Timeout). @@ -56,52 +108,116 @@ change_code(Name, Mod, Vsn, Extra, Timeout) -> %% Debug commands %%----------------------------------------------------------------- --type log_flag() :: 'true' | {'true',pos_integer()} | 'false' | 'get' | 'print'. - --spec log(name(), log_flag()) -> 'ok' | {'ok', [system_event()]}. +-spec log(Name, Flag) -> 'ok' | {'ok', [system_event()]} when + Name :: name(), + Flag :: 'true' | + {'true', N :: pos_integer()} + | 'false' | 'get' | 'print'. log(Name, Flag) -> send_system_msg(Name, {debug, {log, Flag}}). --spec log(name(), log_flag(), timeout()) -> 'ok' | {'ok', [system_event()]}. +-spec log(Name, Flag, Timeout) -> 'ok' | {'ok', [system_event()]} when + Name :: name(), + Flag :: 'true' | + {'true', N :: pos_integer()} + | 'false' | 'get' | 'print', + Timeout :: timeout(). log(Name, Flag, Timeout) -> send_system_msg(Name, {debug, {log, Flag}}, Timeout). --spec trace(name(), boolean()) -> 'ok'. +-spec trace(Name, Flag) -> 'ok' when + Name :: name(), + Flag :: boolean(). trace(Name, Flag) -> send_system_msg(Name, {debug, {trace, Flag}}). --spec trace(name(), boolean(), timeout()) -> 'ok'. +-spec trace(Name, Flag, Timeout) -> 'ok' when + Name :: name(), + Flag :: boolean(), + Timeout :: timeout(). trace(Name, Flag, Timeout) -> send_system_msg(Name, {debug, {trace, Flag}}, Timeout). --type l2f_fname() :: string() | 'false'. - --spec log_to_file(name(), l2f_fname()) -> 'ok' | {'error','open_file'}. +-spec log_to_file(Name, Flag) -> 'ok' | {'error','open_file'} when + Name :: name(), + Flag :: (FileName :: string()) | 'false'. log_to_file(Name, FileName) -> send_system_msg(Name, {debug, {log_to_file, FileName}}). --spec log_to_file(name(), l2f_fname(), timeout()) -> 'ok' | {'error','open_file'}. +-spec log_to_file(Name, Flag, Timeout) -> 'ok' | {'error','open_file'} when + Name :: name(), + Flag :: (FileName :: string()) | 'false', + Timeout :: timeout(). log_to_file(Name, FileName, Timeout) -> send_system_msg(Name, {debug, {log_to_file, FileName}}, Timeout). +-spec statistics(Name, Flag) -> 'ok' | {'ok', Statistics} when + Name :: name(), + Flag :: 'true' | 'false' | 'get', + Statistics :: [StatisticsTuple] | no_statistics, + StatisticsTuple :: {'start_time', DateTime1} + | {'current_time', DateTime2} + | {'reductions', non_neg_integer()} + | {'messages_in', non_neg_integer()} + | {'messages_out', non_neg_integer()}, + DateTime1 :: file:date_time(), + DateTime2 :: file:date_time(). statistics(Name, Flag) -> send_system_msg(Name, {debug, {statistics, Flag}}). + +-spec statistics(Name, Flag, Timeout) -> 'ok' | {'ok', Statistics} when + Name :: name(), + Flag :: 'true' | 'false' | 'get', + Statistics :: [StatisticsTuple] | no_statistics, + StatisticsTuple :: {'start_time', DateTime1} + | {'current_time', DateTime2} + | {'reductions', non_neg_integer()} + | {'messages_in', non_neg_integer()} + | {'messages_out', non_neg_integer()}, + DateTime1 :: file:date_time(), + DateTime2 :: file:date_time(), + Timeout :: timeout(). statistics(Name, Flag, Timeout) -> send_system_msg(Name, {debug, {statistics, Flag}}, Timeout). --spec no_debug(name()) -> 'ok'. +-spec no_debug(Name) -> 'ok' when + Name :: name(). no_debug(Name) -> send_system_msg(Name, {debug, no_debug}). --spec no_debug(name(), timeout()) -> 'ok'. +-spec no_debug(Name, Timeout) -> 'ok' when + Name :: name(), + Timeout :: timeout(). no_debug(Name, Timeout) -> send_system_msg(Name, {debug, no_debug}, Timeout). +-spec install(Name, FuncSpec) -> Void when + Name :: name(), + FuncSpec :: {Func, FuncState}, + Func :: dbg_fun(), + FuncState :: term(), + Void :: term(). install(Name, {Func, FuncState}) -> send_system_msg(Name, {debug, {install, {Func, FuncState}}}). +-spec install(Name, FuncSpec, Timeout) -> Void when + Name :: name(), + FuncSpec :: {Func, FuncState}, + Func :: dbg_fun(), + FuncState :: term(), + Timeout :: timeout(), + Void :: term(). install(Name, {Func, FuncState}, Timeout) -> send_system_msg(Name, {debug, {install, {Func, FuncState}}}, Timeout). +-spec remove(Name, Func) -> Void when + Name :: name(), + Func :: dbg_fun(), + Void :: term(). remove(Name, Func) -> send_system_msg(Name, {debug, {remove, Func}}). +-spec remove(Name, Func, Timeout) -> Void when + Name :: name(), + Func :: dbg_fun(), + Timeout :: timeout(), + Void :: term(). remove(Name, Func, Timeout) -> send_system_msg(Name, {debug, {remove, Func}}, Timeout). @@ -150,6 +266,14 @@ mfa(Name, Req, Timeout) -> %% The Module must export system_continue/3, system_terminate/4 %% and format_status/2 for status information. %%----------------------------------------------------------------- +-spec handle_system_msg(Msg, From, Parent, Module, Debug, Misc) -> Void when + Msg :: term(), + From :: {pid(), Tag :: _}, + Parent :: pid(), + Module :: module(), + Debug :: [dbg_opt()], + Misc :: term(), + Void :: term(). handle_system_msg(Msg, From, Parent, Module, Debug, Misc) -> handle_system_msg(running, Msg, From, Parent, Module, Debug, Misc, false). @@ -176,6 +300,11 @@ handle_system_msg(SysState, Msg, From, Parent, Mod, Debug, Misc, Hib) -> %% Func is a formatting function, called as Func(Device, Event). %% Returns: [debug_opts()] %%----------------------------------------------------------------- +-spec handle_debug(Debug, FormFunc, Extra, Event) -> [dbg_opt()] when + Debug :: [dbg_opt()], + FormFunc :: dbg_fun(), + Extra :: term(), + Event :: system_event(). handle_debug([{trace, true} | T], FormFunc, State, Event) -> print_event({Event, State, FormFunc}), [{trace, true} | handle_debug(T, FormFunc, State, Event)]; @@ -341,24 +470,36 @@ trim(N, LogData) -> %% Debug structure manipulating functions %%----------------------------------------------------------------- install_debug(Item, Data, Debug) -> - case get_debug(Item, Debug, undefined) of + case get_debug2(Item, Debug, undefined) of undefined -> [{Item, Data} | Debug]; _ -> Debug end. remove_debug(Item, Debug) -> lists:keydelete(Item, 1, Debug). + +-spec get_debug(Item, Debug, Default) -> term() when + Item :: 'log' | 'statistics', + Debug :: [dbg_opt()], + Default :: term(). get_debug(Item, Debug, Default) -> + get_debug2(Item, Debug, Default). + +%% Workaround: accepts more Item types than get_debug/3. +get_debug2(Item, Debug, Default) -> case lists:keysearch(Item, 1, Debug) of {value, {Item, Data}} -> Data; _ -> Default end. +-spec print_log(Debug) -> Void when + Debug :: [dbg_opt()], + Void :: term(). print_log(Debug) -> {_N, Logs} = get_debug(log, Debug, {0, []}), lists:foreach(fun print_event/1, lists:reverse(Logs)). close_log_file(Debug) -> - case get_debug(log_to_file, Debug, []) of + case get_debug2(log_to_file, Debug, []) of [] -> Debug; Fd -> @@ -375,6 +516,15 @@ close_log_file(Debug) -> %% system messages. %% Returns: [debug_opts()] %%----------------------------------------------------------------- + +-spec debug_options(Options) -> [dbg_opt()] when + Options :: [Opt], + Opt :: 'trace' | 'log' | 'statistics' | {'log_to_file', FileName} + | {'install', FuncSpec}, + FileName :: file:name(), + FuncSpec :: {Func, FuncState}, + Func :: dbg_fun(), + FuncState :: term(). debug_options(Options) -> debug_options(Options, []). debug_options([trace | T], Debug) -> diff --git a/lib/stdlib/src/timer.erl b/lib/stdlib/src/timer.erl index b456c5d6c1..689e42051f 100644 --- a/lib/stdlib/src/timer.erl +++ b/lib/stdlib/src/timer.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -22,7 +22,7 @@ send_after/3, send_after/2, exit_after/3, exit_after/2, kill_after/2, kill_after/1, apply_interval/4, send_interval/3, send_interval/2, - cancel/1, sleep/1, tc/2, tc/3, now_diff/2, + cancel/1, sleep/1, tc/1, tc/2, tc/3, now_diff/2, seconds/1, minutes/1, hours/1, hms/3]). -export([start_link/0, start/0, @@ -46,103 +46,189 @@ %% -opaque tref() :: {integer(), reference()}. -type time() :: non_neg_integer(). --type timestamp() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}. %% %% Interface functions %% --spec apply_after(time(), atom(), atom(), [term()]) -> {'ok', tref()} | {'error', term()}. +-spec apply_after(Time, Module, Function, Arguments) -> + {'ok', TRef} | {'error', Reason} when + Time :: time(), + Module :: module(), + Function :: atom(), + Arguments :: [term()], + TRef :: tref(), + Reason :: term(). + apply_after(Time, M, F, A) -> req(apply_after, {Time, {M, F, A}}). --spec send_after(time(), pid() | atom(), term()) -> {'ok', tref()} | {'error', term()}. +-spec send_after(Time, Pid, Message) -> {'ok', TRef} | {'error', Reason} when + Time :: time(), + Pid :: pid() | (RegName :: atom()), + Message :: term(), + TRef :: tref(), + Reason :: term(). send_after(Time, Pid, Message) -> req(apply_after, {Time, {?MODULE, send, [Pid, Message]}}). --spec send_after(time(), term()) -> {'ok', tref()} | {'error', term()}. +-spec send_after(Time, Message) -> {'ok', TRef} | {'error', Reason} when + Time :: time(), + Message :: term(), + TRef :: tref(), + Reason :: term(). send_after(Time, Message) -> send_after(Time, self(), Message). --spec exit_after(time(), pid() | atom(), term()) -> {'ok', tref()} | {'error', term()}. +-spec exit_after(Time, Pid, Reason1) -> {'ok', TRef} | {'error', Reason2} when + Time :: time(), + Pid :: pid() | (RegName :: atom()), + TRef :: tref(), + Reason1 :: term(), + Reason2 :: term(). exit_after(Time, Pid, Reason) -> req(apply_after, {Time, {erlang, exit, [Pid, Reason]}}). --spec exit_after(time(), term()) -> {'ok', tref()} | {'error', term()}. +-spec exit_after(Time, Reason1) -> {'ok', TRef} | {'error', Reason2} when + Time :: time(), + TRef :: tref(), + Reason1 :: term(), + Reason2 :: term(). exit_after(Time, Reason) -> exit_after(Time, self(), Reason). --spec kill_after(time(), pid() | atom()) -> {'ok', tref()} | {'error', term()}. +-spec kill_after(Time, Pid) -> {'ok', TRef} | {'error', Reason2} when + Time :: time(), + Pid :: pid() | (RegName :: atom()), + TRef :: tref(), + Reason2 :: term(). kill_after(Time, Pid) -> exit_after(Time, Pid, kill). --spec kill_after(time()) -> {'ok', tref()} | {'error', term()}. +-spec kill_after(Time) -> {'ok', TRef} | {'error', Reason2} when + Time :: time(), + TRef :: tref(), + Reason2 :: term(). kill_after(Time) -> exit_after(Time, self(), kill). --spec apply_interval(time(), atom(), atom(), [term()]) -> {'ok', tref()} | {'error', term()}. +-spec apply_interval(Time, Module, Function, Arguments) -> + {'ok', TRef} | {'error', Reason} when + Time :: time(), + Module :: module(), + Function :: atom(), + Arguments :: [term()], + TRef :: tref(), + Reason :: term(). apply_interval(Time, M, F, A) -> req(apply_interval, {Time, self(), {M, F, A}}). --spec send_interval(time(), pid() | atom(), term()) -> {'ok', tref()} | {'error', term()}. +-spec send_interval(Time, Pid, Message) -> + {'ok', TRef} | {'error', Reason} when + Time :: time(), + Pid :: pid() | (RegName :: atom()), + Message :: term(), + TRef :: tref(), + Reason :: term(). send_interval(Time, Pid, Message) -> req(apply_interval, {Time, Pid, {?MODULE, send, [Pid, Message]}}). --spec send_interval(time(), term()) -> {'ok', tref()} | {'error', term()}. +-spec send_interval(Time, Message) -> {'ok', TRef} | {'error', Reason} when + Time :: time(), + Message :: term(), + TRef :: tref(), + Reason :: term(). send_interval(Time, Message) -> send_interval(Time, self(), Message). --spec cancel(tref()) -> {'ok', 'cancel'} | {'error', term()}. +-spec cancel(TRef) -> {'ok', 'cancel'} | {'error', Reason} when + TRef :: tref(), + Reason :: term(). cancel(BRef) -> req(cancel, BRef). --spec sleep(timeout()) -> 'ok'. +-spec sleep(Time) -> 'ok' when + Time :: timeout(). sleep(T) -> receive after T -> ok end. +%% +%% Measure the execution time (in microseconds) for Fun(). +%% +-spec tc(Fun) -> {Time, Value} when + Fun :: function(), + Time :: integer(), + Value :: term(). +tc(F) -> + Before = os:timestamp(), + Val = F(), + After = os:timestamp(), + {now_diff(After, Before), Val}. %% %% Measure the execution time (in microseconds) for Fun(Args). %% --spec tc(function(), [_]) -> {time(), term()}. +-spec tc(Fun, Arguments) -> {Time, Value} when + Fun :: function(), + Arguments :: [term()], + Time :: integer(), + Value :: term(). tc(F, A) -> - Before = erlang:now(), - Val = (catch apply(F, A)), - After = erlang:now(), + Before = os:timestamp(), + Val = apply(F, A), + After = os:timestamp(), {now_diff(After, Before), Val}. %% %% Measure the execution time (in microseconds) for an MFA. %% --spec tc(atom(), atom(), [term()]) -> {time(), term()}. +-spec tc(Module, Function, Arguments) -> {Time, Value} when + Module :: module(), + Function :: atom(), + Arguments :: [term()], + Time :: integer(), + Value :: term(). tc(M, F, A) -> - Before = erlang:now(), - Val = (catch apply(M, F, A)), - After = erlang:now(), + Before = os:timestamp(), + Val = apply(M, F, A), + After = os:timestamp(), {now_diff(After, Before), Val}. %% %% Calculate the time difference (in microseconds) of two %% erlang:now() timestamps, T2-T1. %% --spec now_diff(timestamp(), timestamp()) -> integer(). +-spec now_diff(T2, T1) -> Tdiff when + T1 :: erlang:timestamp(), + T2 :: erlang:timestamp(), + Tdiff :: integer(). now_diff({A2, B2, C2}, {A1, B1, C1}) -> ((A2-A1)*1000000 + B2-B1)*1000000 + C2-C1. %% %% Convert seconds, minutes etc. to milliseconds. %% --spec seconds(non_neg_integer()) -> non_neg_integer(). +-spec seconds(Seconds) -> MilliSeconds when + Seconds :: non_neg_integer(), + MilliSeconds :: non_neg_integer(). seconds(Seconds) -> 1000*Seconds. --spec minutes(non_neg_integer()) -> non_neg_integer(). +-spec minutes(Minutes) -> MilliSeconds when + Minutes :: non_neg_integer(), + MilliSeconds :: non_neg_integer(). minutes(Minutes) -> 1000*60*Minutes. --spec hours(non_neg_integer()) -> non_neg_integer(). +-spec hours(Hours) -> MilliSeconds when + Hours :: non_neg_integer(), + MilliSeconds :: non_neg_integer(). hours(Hours) -> 1000*60*60*Hours. --spec hms(non_neg_integer(), non_neg_integer(), non_neg_integer()) -> non_neg_integer(). +-spec hms(Hours, Minutes, Seconds) -> MilliSeconds when + Hours :: non_neg_integer(), + Minutes :: non_neg_integer(), + Seconds :: non_neg_integer(), + MilliSeconds :: non_neg_integer(). hms(H, M, S) -> hours(H) + minutes(M) + seconds(S). diff --git a/lib/stdlib/src/unicode.erl b/lib/stdlib/src/unicode.erl index 12bc60623d..e9b90befe6 100644 --- a/lib/stdlib/src/unicode.erl +++ b/lib/stdlib/src/unicode.erl @@ -30,12 +30,34 @@ characters_to_binary/3, bom_to_encoding/1, encoding_to_bom/1]). --export_type([encoding/0]). +-export_type([chardata/0, charlist/0, encoding/0, external_chardata/0, + external_charlist/0, latin1_chardata/0, + latin1_charlist/0, unicode_binary/0, unicode_char/0]). -type encoding() :: 'latin1' | 'unicode' | 'utf8' | 'utf16' | {'utf16', endian()} | 'utf32' | {'utf32', endian()}. -type endian() :: 'big' | 'little'. +-type unicode_binary() :: binary(). +-type unicode_char() :: non_neg_integer(). +-type charlist() :: [unicode_char() | unicode_binary() | charlist()]. +-type chardata() :: charlist() | unicode_binary(). +-type external_unicode_binary() :: binary(). +-type external_chardata() :: external_charlist() | external_unicode_binary(). +-type external_charlist() :: [unicode_char() | external_unicode_binary() + | external_charlist()]. +-type latin1_binary() :: binary(). +-type latin1_char() :: byte(). +-type latin1_chardata() :: latin1_charlist() | latin1_binary(). +-type latin1_charlist() :: [latin1_char() | latin1_binary() + | latin1_charlist()]. + +-spec characters_to_list(Data) -> Result when + Data :: latin1_chardata() | chardata() | external_chardata(), + Result :: list() + | {error, list(), RestData} + | {incomplete, list(), binary()}, + RestData :: latin1_chardata() | chardata() | external_chardata(). characters_to_list(ML) -> unicode:characters_to_list(ML,unicode). @@ -51,7 +73,7 @@ characters_to_list_int(ML, Encoding) -> _ -> badarg end, - {'EXIT',{new_stacktrace,[{Mod,_,L}|Rest]}} = + {'EXIT',{new_stacktrace,[{Mod,_,L,_}|Rest]}} = (catch erlang:error(new_stacktrace, [ML,Encoding])), erlang:raise(error,TheError,[{Mod,characters_to_list,L}|Rest]) @@ -69,6 +91,13 @@ do_characters_to_list(ML, Encoding) -> end. +-spec characters_to_binary(Data) -> Result when + Data :: latin1_chardata() | chardata() | external_chardata(), + Result :: binary() + | {error, binary(), RestData} + | {incomplete, binary(), binary()}, + RestData :: latin1_chardata() | chardata() | external_chardata(). + characters_to_binary(ML) -> try unicode:characters_to_binary(ML,unicode) @@ -80,7 +109,7 @@ characters_to_binary(ML) -> _ -> badarg end, - {'EXIT',{new_stacktrace,[{Mod,_,L}|Rest]}} = + {'EXIT',{new_stacktrace,[{Mod,_,L,_}|Rest]}} = (catch erlang:error(new_stacktrace, [ML])), erlang:raise(error,TheError,[{Mod,characters_to_binary,L}|Rest]) @@ -98,12 +127,21 @@ characters_to_binary_int(ML,InEncoding) -> _ -> badarg end, - {'EXIT',{new_stacktrace,[{Mod,_,L}|Rest]}} = + {'EXIT',{new_stacktrace,[{Mod,_,L,_}|Rest]}} = (catch erlang:error(new_stacktrace, [ML,InEncoding])), erlang:raise(error,TheError,[{Mod,characters_to_binary,L}|Rest]) end. +-spec characters_to_binary(Data, InEncoding, OutEncoding) -> Result when + Data :: latin1_chardata() | chardata() | external_chardata(), + InEncoding :: encoding(), + OutEncoding :: encoding(), + Result :: binary() + | {error, binary(), RestData} + | {incomplete, binary(), binary()}, + RestData :: latin1_chardata() | chardata() | external_chardata(). + characters_to_binary(ML, latin1, latin1) when is_binary(ML) -> ML; characters_to_binary(ML, latin1, Uni) when is_binary(ML) and ((Uni =:= utf8) or (Uni =:= unicode)) -> @@ -121,7 +159,7 @@ characters_to_binary(ML, latin1, Uni) when is_binary(ML) and ((Uni =:= utf8) or _ -> badarg end, - {'EXIT',{new_stacktrace,[{Mod,_,L}|Rest]}} = + {'EXIT',{new_stacktrace,[{Mod,_,L,_}|Rest]}} = (catch erlang:error(new_stacktrace, [ML,latin1,Uni])), erlang:raise(error,TheError, @@ -143,7 +181,7 @@ characters_to_binary(ML,Uni,latin1) when is_binary(ML) and ((Uni =:= utf8) or _ -> badarg end, - {'EXIT',{new_stacktrace,[{Mod,_,L}|Rest]}} = + {'EXIT',{new_stacktrace,[{Mod,_,L,_}|Rest]}} = (catch erlang:error(new_stacktrace, [ML,Uni,latin1])), erlang:raise(error,TheError, @@ -162,7 +200,7 @@ characters_to_binary(ML, InEncoding, OutEncoding) -> _ -> badarg end, - {'EXIT',{new_stacktrace,[{Mod,_,L}|Rest]}} = + {'EXIT',{new_stacktrace,[{Mod,_,L,_}|Rest]}} = (catch erlang:error(new_stacktrace, [ML,InEncoding,OutEncoding])), erlang:raise(error,TheError,[{Mod,characters_to_binary,L}|Rest]) @@ -215,6 +253,13 @@ characters_to_binary_int(ML, InEncoding, OutEncoding) -> Res end. +-spec bom_to_encoding(Bin) -> {Encoding, Length} when + Bin :: binary(), + Encoding :: 'latin1' | 'utf8' + | {'utf16', endian()} + | {'utf32', endian()}, + Length :: non_neg_integer(). + bom_to_encoding(<<239,187,191,_/binary>>) -> {utf8,3}; bom_to_encoding(<<0,0,254,255,_/binary>>) -> @@ -228,6 +273,10 @@ bom_to_encoding(<<255,254,_/binary>>) -> bom_to_encoding(Bin) when is_binary(Bin) -> {latin1,0}. +-spec encoding_to_bom(InEncoding) -> Bin when + Bin :: binary(), + InEncoding :: encoding(). + encoding_to_bom(unicode) -> <<239,187,191>>; encoding_to_bom(utf8) -> diff --git a/lib/stdlib/src/win32reg.erl b/lib/stdlib/src/win32reg.erl index ee0d17bc94..598e77ffdc 100644 --- a/lib/stdlib/src/win32reg.erl +++ b/lib/stdlib/src/win32reg.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -52,14 +52,17 @@ -define(reg_dword, 4). %% Basic types internal to this file. --type open_mode() :: 'read' | 'write'. --type reg_handle() :: {'win32reg',port()}. +-opaque reg_handle() :: {'win32reg',port()}. -type name() :: string() | 'default'. -type value() :: string() | integer() | binary(). %%% Exported functions. --spec open([open_mode()]) -> {'ok', reg_handle()} | {'error', 'enotsup'}. +-spec open(OpenModeList) -> ReturnValue when + OpenModeList :: [OpenMode], + OpenMode :: 'read' | 'write', + ReturnValue :: {'ok', RegHandle} | {'error', ErrorId :: 'enotsup'}, + RegHandle :: reg_handle(). open(Modes) -> case os:type() of @@ -75,14 +78,17 @@ open(Modes) -> {error, enotsup} end. --spec close(reg_handle()) -> 'ok'. +-spec close(RegHandle) -> 'ok' when + RegHandle :: reg_handle(). close({win32reg, Reg}) when is_port(Reg) -> unlink(Reg), exit(Reg, die), ok. --spec current_key(reg_handle()) -> {'ok', string()}. +-spec current_key(RegHandle) -> ReturnValue when + RegHandle :: reg_handle(), + ReturnValue :: {'ok', string()}. current_key({win32reg, Reg}) when is_port(Reg) -> Cmd = [?cmd_get_current], @@ -94,12 +100,18 @@ current_key({win32reg, Reg}) when is_port(Reg) -> _ -> Root ++ [$\\|Name] end}. --spec change_key(reg_handle(), string()) -> 'ok' | {'error', atom()}. +-spec change_key(RegHandle, Key) -> ReturnValue when + RegHandle :: reg_handle(), + Key :: string(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. change_key({win32reg, Reg}, Key) when is_port(Reg) -> change_key(Reg, ?cmd_open_key, Key). --spec change_key_create(reg_handle(), string()) -> 'ok' | {'error', atom()}. +-spec change_key_create(RegHandle, Key) -> ReturnValue when + RegHandle :: reg_handle(), + Key :: string(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. change_key_create({win32reg, Reg}, Key) when is_port(Reg) -> change_key(Reg, ?cmd_create_key, Key). @@ -113,21 +125,30 @@ change_key(Reg, Cmd, Key) -> {error, Reason} end. --spec sub_keys(reg_handle()) -> {'ok', [string()]} | {'error', atom()}. +-spec sub_keys(RegHandle) -> ReturnValue when + RegHandle :: reg_handle(), + ReturnValue :: {'ok', [SubKey]} | {'error', ErrorId :: atom()}, + SubKey :: string(). sub_keys({win32reg, Reg}) when is_port(Reg) -> Cmd = [?cmd_get_all_subkeys], Reg ! {self(), {command, Cmd}}, collect_keys(Reg, []). --spec delete_key(reg_handle()) -> 'ok' | {'error', atom()}. +-spec delete_key(RegHandle) -> ReturnValue when + RegHandle :: reg_handle(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. delete_key({win32reg, Reg}) when is_port(Reg) -> Cmd = [?cmd_delete_key], Reg ! {self(), {command, Cmd}}, get_result(Reg). --spec set_value(reg_handle(), name(), value()) -> 'ok' | {'error', atom()}. +-spec set_value(RegHandle, Name, Value) -> ReturnValue when + RegHandle :: reg_handle(), + Name :: name(), + Value :: value(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. set_value({win32reg, Reg}, Name0, Value) when is_port(Reg) -> Name = @@ -140,7 +161,10 @@ set_value({win32reg, Reg}, Name0, Value) when is_port(Reg) -> Reg ! {self(), {command, Cmd}}, get_result(Reg). --spec value(reg_handle(), name()) -> {'ok', value()} | {'error', atom()}. +-spec value(RegHandle, Name) -> ReturnValue when + RegHandle :: reg_handle(), + Name :: name(), + ReturnValue :: {'ok', Value :: value()} | {'error', ErrorId :: atom()}. value({win32reg, Reg}, Name) when is_port(Reg) -> Cmd = [?cmd_get_value, Name, 0], @@ -152,14 +176,20 @@ value({win32reg, Reg}, Name) when is_port(Reg) -> {error, Reason} end. --spec values(reg_handle()) -> {'ok', [{name(), value()}]} | {'error', atom()}. +-spec values(RegHandle) -> ReturnValue when + RegHandle :: reg_handle(), + ReturnValue :: {'ok', [ValuePair]} | {'error', ErrorId :: atom()}, + ValuePair :: {Name :: name(), Value :: value()}. values({win32reg, Reg}) when is_port(Reg) -> Cmd = [?cmd_get_all_values], Reg ! {self(), {command, Cmd}}, collect_values(Reg, []). --spec delete_value(reg_handle(), name()) -> 'ok' | {'error', atom()}. +-spec delete_value(RegHandle, Name) -> ReturnValue when + RegHandle :: reg_handle(), + Name :: name(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. delete_value({win32reg, Reg}, Name0) when is_port(Reg) -> Name = @@ -171,7 +201,9 @@ delete_value({win32reg, Reg}, Name0) when is_port(Reg) -> Reg ! {self(), {command, Cmd}}, get_result(Reg). --spec expand(string()) -> string(). +-spec expand(String) -> ExpandedString when + String :: string(), + ExpandedString :: string(). expand(Value) -> expand(Value, [], []). @@ -195,7 +227,9 @@ expand([C|Rest], Env, Result) -> expand([], [], Result) -> lists:reverse(Result). --spec format_error(atom()) -> string(). +-spec format_error(ErrorId) -> ErrorString when + ErrorId :: atom(), + ErrorString :: string(). format_error(ErrorId) -> erl_posix_msg:message(ErrorId). @@ -203,7 +237,7 @@ format_error(ErrorId) -> %%% Implementation. -spec collect_values(port(), [{name(), value()}]) -> - {'ok', [{name(), value()}]} | {'error', atom()}. + {'ok', [{name(), value()}]} | {'error', ErrorId :: atom()}. collect_values(P, Result) -> case get_result(P) of @@ -215,7 +249,7 @@ collect_values(P, Result) -> {error, Reason} end. --spec collect_keys(port(), string()) -> {'ok', [string()]} | {'error', atom()}. +-spec collect_keys(port(), string()) -> {'ok', [string()]} | {'error', ErrorId :: atom()}. collect_keys(P, Result) -> case get_result(P) of diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl index d41aeefa59..c82c8159b6 100644 --- a/lib/stdlib/src/zip.erl +++ b/lib/stdlib/src/zip.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -203,6 +203,9 @@ zip_comment_length}). +-type zip_file() :: #zip_file{}. +-type zip_comment() :: #zip_comment{}. + %% Open a zip archive with options %% @@ -220,7 +223,7 @@ openzip_open(F, Options) -> do_openzip_open(F, Options) -> Opts = get_openzip_options(Options), #openzip_opts{output = Output, open_opts = OpO, cwd = CWD} = Opts, - Input = get_zip_input(F), + Input = get_input(F), In0 = Input({open, F, OpO -- [write]}, []), {[#zip_comment{comment = C} | Files], In1} = get_central_dir(In0, fun raw_file_info_etc/5, Input), @@ -323,8 +326,33 @@ openzip_close(_) -> %% Accepted options: %% verbose, cooked, file_list, keep_old_files, file_filter, memory +-spec(unzip(Archive) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, FileList} + | {ok, FileBinList} + | {error, Reason :: term()} + | {error, {Name :: file:name(), Reason :: term()}}, + FileList :: [file:name()], + FileBinList :: [{file:name(),binary()}]). + unzip(F) -> unzip(F, []). +-spec(unzip(Archive, Options) -> RetValue when + Archive :: file:name() | binary(), + Options :: [Option], + Option :: {file_list, FileList} + | keep_old_files | verbose | memory | + {file_filter, FileFilter} | {cwd, CWD}, + FileList :: [file:name()], + FileBinList :: [{file:name(),binary()}], + FileFilter :: fun((ZipFile) -> boolean()), + CWD :: string(), + ZipFile :: zip_file(), + RetValue :: {ok, FileList} + | {ok, FileBinList} + | {error, Reason :: term()} + | {error, {Name :: file:name(), Reason :: term()}}). + unzip(F, Options) -> case ?CATCH do_unzip(F, Options) of {ok, R} -> {ok, R}; @@ -345,6 +373,18 @@ do_unzip(F, Options) -> {ok, Files}. %% Iterate over all files in a zip archive +-spec(foldl(Fun, Acc0, Archive) -> {ok, Acc1} | {error, Reason} when + Fun :: fun((FileInArchive, GetInfo, GetBin, AccIn) -> AccOut), + FileInArchive :: file:name(), + GetInfo :: fun(() -> file:file_info()), + GetBin :: fun(() -> binary()), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Archive :: file:name() | {file:name(), binary()}, + Reason :: term()). + foldl(Fun, Acc0, Archive) when is_function(Fun, 4) -> ZipFun = fun({Name, GetInfo, GetBin}, A) -> @@ -368,8 +408,33 @@ foldl(_,_, _) -> %% Accepted options: %% verbose, cooked, memory, comment +-spec(zip(Name, FileList) -> RetValue when + Name :: file:name(), + FileList :: [FileSpec], + FileSpec :: file:name() | {file:name(), binary()} + | {file:name(), binary(), file:file_info()}, + RetValue :: {ok, FileName :: file:name()} + | {ok, {FileName :: file:name(), binary()}} + | {error, Reason :: term()}). + zip(F, Files) -> zip(F, Files, []). +-spec(zip(Name, FileList, Options) -> RetValue when + Name :: file:name(), + FileList :: [FileSpec], + FileSpec :: file:name() | {file:name(), binary()} + | {file:name(), binary(), file:file_info()}, + Options :: [Option], + Option :: memory | cooked | verbose | {comment, Comment} + | {cwd, CWD} | {compress, What} | {uncompress, What}, + What :: all | [Extension] | {add, [Extension]} | {del, [Extension]}, + Extension :: string(), + Comment :: string(), + CWD :: string(), + RetValue :: {ok, FileName :: file:name()} + | {ok, {FileName :: file:name(), binary()}} + | {error, Reason :: term()}). + zip(F, Files, Options) -> case ?CATCH do_zip(F, Files, Options) of {ok, R} -> {ok, R}; @@ -392,8 +457,20 @@ do_zip(F, Files, Options) -> %% Accepted options: %% cooked, file_filter, file_output (latter 2 undocumented) +-spec(list_dir(Archive) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, CommentAndFiles} | {error, Reason :: term()}, + CommentAndFiles :: [zip_comment() | zip_file()]). + list_dir(F) -> list_dir(F, []). +-spec(list_dir(Archive, Options) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, CommentAndFiles} | {error, Reason :: term()}, + CommentAndFiles :: [zip_comment() | zip_file()], + Options :: [Option], + Option :: cooked). + list_dir(F, Options) -> case ?CATCH do_list_dir(F, Options) of {ok, R} -> {ok, R}; @@ -411,6 +488,10 @@ do_list_dir(F, Options) -> %% Print zip directory in short form +-spec(t(Archive) -> ok when + Archive :: file:name() | binary() | ZipHandle, + ZipHandle :: pid()). + t(F) when is_pid(F) -> zip_t(F); t(F) when is_record(F, openzip) -> openzip_t(F); t(F) -> t(F, fun raw_short_print_info_etc/5). @@ -431,6 +512,10 @@ do_t(F, RawPrint) -> %% Print zip directory in long form (like ls -l) +-spec(tt(Archive) -> ok when + Archive :: file:name() | binary() | ZipHandle, + ZipHandle :: pid()). + tt(F) when is_pid(F) -> zip_tt(F); tt(F) when is_record(F, openzip) -> openzip_tt(F); tt(F) -> t(F, fun raw_long_print_info_etc/5). @@ -605,11 +690,78 @@ get_list_dir_options(F, Options) -> get_list_dir_opt(Options, Opts). %% aliases for erl_tar compatibility +-spec(table(Archive) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, CommentAndFiles} | {error, Reason :: term()}, + CommentAndFiles :: [zip_comment() | zip_file()]). + table(F) -> list_dir(F). + +-spec(table(Archive, Options) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, CommentAndFiles} | {error, Reason :: term()}, + CommentAndFiles :: [zip_comment() | zip_file()], + + Options :: [Option], + Option :: cooked). + table(F, O) -> list_dir(F, O). + +-spec(create(Name, FileList) -> RetValue when + Name :: file:name(), + FileList :: [FileSpec], + FileSpec :: file:name() | {file:name(), binary()} + | {file:name(), binary(), file:file_info()}, + RetValue :: {ok, FileName :: file:name()} + | {ok, {FileName :: file:name(), binary()}} + | {error, Reason :: term()}). + create(F, Fs) -> zip(F, Fs). + +-spec(create(Name, FileList, Options) -> RetValue when + Name :: file:name(), + FileList :: [FileSpec], + FileSpec :: file:name() | {file:name(), binary()} + | {file:name(), binary(), file:file_info()}, + Options :: [Option], + Option :: memory | cooked | verbose | {comment, Comment} + | {cwd, CWD} | {compress, What} | {uncompress, What}, + What :: all | [Extension] | {add, [Extension]} | {del, [Extension]}, + Extension :: string(), + Comment :: string(), + CWD :: string(), + RetValue :: {ok, FileName :: file:name()} + | {ok, {FileName :: file:name(), binary()}} + | {error, Reason :: term()}). create(F, Fs, O) -> zip(F, Fs, O). + +-spec(extract(Archive) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, FileList} + | {ok, FileBinList} + | {error, Reason :: term()} + | {error, {Name :: file:name(), Reason :: term()}}, + FileList :: [file:name()], + FileBinList :: [{file:name(),binary()}]). + extract(F) -> unzip(F). + +-spec(extract(Archive, Options) -> RetValue when + Archive :: file:name() | binary(), + Options :: [Option], + Option :: {file_list, FileList} + | keep_old_files | verbose | memory | + {file_filter, FileFilter} | {cwd, CWD}, + FileList :: [file:name()], + FileBinList :: [{file:name(),binary()}], + FileFilter :: fun((ZipFile) -> boolean()), + CWD :: string(), + ZipFile :: zip_file(), + RetValue :: {ok, FileList} + | {ok, FileBinList} + | {error, Reason :: term()} + | {error, {Name :: file:name(), Reason :: term()}}). + extract(F, O) -> unzip(F, O). @@ -990,21 +1142,52 @@ server_loop(OpenZip) -> {error, bad_msg} end. +-spec(zip_open(Archive) -> {ok, ZipHandle} | {error, Reason} when + Archive :: file:name() | binary(), + ZipHandle :: pid(), + Reason :: term()). + zip_open(Archive) -> zip_open(Archive, []). +-spec(zip_open(Archive, Options) -> {ok, ZipHandle} | {error, Reason} when + Archive :: file:name() | binary(), + ZipHandle :: pid(), + Options :: [Option], + Option :: cooked | memory | {cwd, CWD :: string()}, + Reason :: term()). + zip_open(Archive, Options) -> Pid = spawn(fun() -> server_loop(not_open) end), request(self(), Pid, {open, Archive, Options}). +-spec(zip_get(ZipHandle) -> {ok, [Result]} | {error, Reason} when + ZipHandle :: pid(), + Result :: file:name() | {file:name(), binary()}, + Reason :: term()). + zip_get(Pid) when is_pid(Pid) -> request(self(), Pid, get). +-spec(zip_close(ZipHandle) -> ok | {error, einval} when + ZipHandle :: pid()). + zip_close(Pid) when is_pid(Pid) -> request(self(), Pid, close). +-spec(zip_get(FileName, ZipHandle) -> {ok, Result} | {error, Reason} when + FileName :: file:name(), + ZipHandle :: pid(), + Result :: file:name() | {file:name(), binary()}, + Reason :: term()). + zip_get(FileName, Pid) when is_pid(Pid) -> request(self(), Pid, {get, FileName}). +-spec(zip_list_dir(ZipHandle) -> {ok, Result} | {error, Reason} when + Result :: [zip_comment() | zip_file()], + ZipHandle :: pid(), + Reason :: term()). + zip_list_dir(Pid) when is_pid(Pid) -> request(self(), Pid, list_dir). |