aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib/src')
-rw-r--r--lib/stdlib/src/beam_lib.erl8
-rw-r--r--lib/stdlib/src/epp.erl152
-rw-r--r--lib/stdlib/src/erl_parse.yrl2
-rw-r--r--lib/stdlib/src/filename.erl160
-rw-r--r--lib/stdlib/src/maps.erl14
-rw-r--r--lib/stdlib/src/otp_internal.erl2
-rw-r--r--lib/stdlib/src/stdlib.appup.src8
7 files changed, 316 insertions, 30 deletions
diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl
index 00cd1221fa..7a17226e46 100644
--- a/lib/stdlib/src/beam_lib.erl
+++ b/lib/stdlib/src/beam_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -904,7 +904,11 @@ anno_from_term({Tag, Forms}) when Tag =:= abstract_v1; Tag =:= abstract_v2 ->
anno_from_term(T) ->
T.
-anno_from_forms(Forms) ->
+anno_from_forms(Forms0) ->
+ %% Forms with record field types created before OTP 19.0 are
+ %% replaced by well-formed record forms holding the type
+ %% information.
+ Forms = epp:restore_typed_record_fields(Forms0),
[erl_parse:anno_from_term(Form) || Form <- Forms].
start_crypto() ->
diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl
index be7c2ec346..936c095aef 100644
--- a/lib/stdlib/src/epp.erl
+++ b/lib/stdlib/src/epp.erl
@@ -46,6 +46,10 @@
-type tokens() :: [erl_scan:token()].
-type used() :: {name(), argspec()}.
+-type function_name_type() :: 'undefined'
+ | {atom(),non_neg_integer()}
+ | tokens().
+
-define(DEFAULT_ENCODING, utf8).
%% Epp state record.
@@ -63,7 +67,8 @@
uses = #{} %Macro use structure
:: #{name() => [{argspec(), [used()]}]},
default_encoding = ?DEFAULT_ENCODING :: source_encoding(),
- pre_opened = false :: boolean()
+ pre_opened = false :: boolean(),
+ fname = [] :: function_name_type()
}).
%% open(Options)
@@ -205,6 +210,10 @@ format_error({include,W,F}) ->
io_lib:format("can't find include ~s \"~s\"", [W,F]);
format_error({illegal,How,What}) ->
io_lib:format("~s '-~s'", [How,What]);
+format_error({illegal_function,Macro}) ->
+ io_lib:format("?~s can only be used within a function", [Macro]);
+format_error({illegal_function_usage,Macro}) ->
+ io_lib:format("?~s must not begin a form", [Macro]);
format_error({'NYI',What}) ->
io_lib:format("not yet implemented '~s'", [What]);
format_error(E) -> file:format_error(E).
@@ -545,6 +554,8 @@ predef_macros(File) ->
Machine = list_to_atom(erlang:system_info(machine)),
Anno = line1(),
Defs = [{'FILE', {none,[{string,Anno,File}]}},
+ {'FUNCTION_NAME', undefined},
+ {'FUNCTION_ARITY', undefined},
{'LINE', {none,[{integer,Anno,1}]}},
{'MODULE', undefined},
{'MODULE_STRING', undefined},
@@ -755,7 +766,7 @@ scan_toks([{'-',_Lh},{atom,_Le,elif}=Elif|Toks], From, St) ->
scan_toks([{'-',_Lh},{atom,_Le,endif}=Endif|Toks], From, St) ->
scan_endif(Toks, Endif, From, St);
scan_toks([{'-',_Lh},{atom,_Lf,file}=FileToken|Toks0], From, St) ->
- case catch expand_macros(Toks0, {St#epp.macs, St#epp.uses}) of
+ case catch expand_macros(Toks0, St) of
Toks1 when is_list(Toks1) ->
scan_file(Toks1, FileToken, From, St);
{error,ErrL,What} ->
@@ -763,7 +774,7 @@ scan_toks([{'-',_Lh},{atom,_Lf,file}=FileToken|Toks0], From, St) ->
wait_req_scan(St)
end;
scan_toks(Toks0, From, St) ->
- case catch expand_macros(Toks0, {St#epp.macs, St#epp.uses}) of
+ case catch expand_macros(Toks0, St#epp{fname=Toks0}) of
Toks1 when is_list(Toks1) ->
epp_reply(From, {ok,Toks1}),
wait_req_scan(St#epp{macs=scan_module(Toks1, St#epp.macs)});
@@ -1145,24 +1156,24 @@ macro_expansion([T|Ts], _Anno0) ->
[T|macro_expansion(Ts, T)];
macro_expansion([], Anno0) -> throw({error,loc(Anno0),premature_end}).
-%% expand_macros(Tokens, Macros)
+%% expand_macros(Tokens, St)
%% expand_macro(Tokens, MacroToken, RestTokens)
%% Expand the macros in a list of tokens, making sure that an expansion
%% gets the same location as the macro call.
-expand_macros(MacT, M, Toks, Ms0) ->
- {Ms,U} = Ms0,
+expand_macros(MacT, M, Toks, St) ->
+ #epp{macs=Ms,uses=U} = St,
Lm = loc(MacT),
Tinfo = element(2, MacT),
case expand_macro1(Lm, M, Toks, Ms) of
{ok,{none,Exp}} ->
check_uses([{M,none}], [], U, Lm),
- Toks1 = expand_macros(expand_macro(Exp, Tinfo, [], #{}), Ms0),
- expand_macros(Toks1++Toks, Ms0);
+ Toks1 = expand_macros(expand_macro(Exp, Tinfo, [], #{}), St),
+ expand_macros(Toks1++Toks, St);
{ok,{As,Exp}} ->
check_uses([{M,length(As)}], [], U, Lm),
{Bs,Toks1} = bind_args(Toks, Lm, M, As, #{}),
- expand_macros(expand_macro(Exp, Tinfo, Toks1, Bs), Ms0)
+ expand_macros(expand_macro(Exp, Tinfo, Toks1, Bs), St)
end.
expand_macro1(Lm, M, Toks, Ms) ->
@@ -1211,16 +1222,32 @@ get_macro_uses({M,Arity}, U) ->
%% Macro expansion
%% Note: io:scan_erl_form() does not return comments or white spaces.
-expand_macros([{'?',_Lq},{atom,_Lm,M}=MacT|Toks], Ms) ->
- expand_macros(MacT, M, Toks, Ms);
+expand_macros([{'?',_Lq},{atom,_Lm,M}=MacT|Toks], St) ->
+ expand_macros(MacT, M, Toks, St);
%% Special macros
-expand_macros([{'?',_Lq},{var,Lm,'LINE'}=Tok|Toks], Ms) ->
+expand_macros([{'?',_Lq},{var,Lm,'FUNCTION_NAME'}=Token|Toks], St0) ->
+ St = update_fun_name(Token, St0),
+ case St#epp.fname of
+ undefined ->
+ [{'?',_Lq},Token];
+ {Name,_} ->
+ [{atom,Lm,Name}]
+ end ++ expand_macros(Toks, St);
+expand_macros([{'?',_Lq},{var,Lm,'FUNCTION_ARITY'}=Token|Toks], St0) ->
+ St = update_fun_name(Token, St0),
+ case St#epp.fname of
+ undefined ->
+ [{'?',_Lq},Token];
+ {_,Arity} ->
+ [{integer,Lm,Arity}]
+ end ++ expand_macros(Toks, St);
+expand_macros([{'?',_Lq},{var,Lm,'LINE'}=Tok|Toks], St) ->
Line = erl_scan:line(Tok),
- [{integer,Lm,Line}|expand_macros(Toks, Ms)];
-expand_macros([{'?',_Lq},{var,_Lm,M}=MacT|Toks], Ms) ->
- expand_macros(MacT, M, Toks, Ms);
+ [{integer,Lm,Line}|expand_macros(Toks, St)];
+expand_macros([{'?',_Lq},{var,_Lm,M}=MacT|Toks], St) ->
+ expand_macros(MacT, M, Toks, St);
%% Illegal macros
-expand_macros([{'?',_Lq},Token|_Toks], _Ms) ->
+expand_macros([{'?',_Lq},Token|_Toks], _St) ->
T = case erl_scan:text(Token) of
Text when is_list(Text) ->
Text;
@@ -1229,9 +1256,9 @@ expand_macros([{'?',_Lq},Token|_Toks], _Ms) ->
io_lib:write(Symbol)
end,
throw({error,loc(Token),{call,[$?|T]}});
-expand_macros([T|Ts], Ms) ->
- [T|expand_macros(Ts, Ms)];
-expand_macros([], _Ms) -> [].
+expand_macros([T|Ts], St) ->
+ [T|expand_macros(Ts, St)];
+expand_macros([], _St) -> [].
%% bind_args(Tokens, MacroLocation, MacroName, ArgumentVars, Bindings)
%% Collect the arguments to a macro call.
@@ -1354,6 +1381,93 @@ expand_arg([A|As], Ts, _L, Rest, Bs) ->
expand_arg([], Ts, L, Rest, Bs) ->
expand_macro(Ts, L, Rest, Bs).
+%%%
+%%% Here follows support for the ?FUNCTION_NAME and ?FUNCTION_ARITY
+%%% macros. Since the parser has not been run yet, we don't know the
+%%% name and arity of the current function. Therefore, we will need to
+%%% scan the beginning of the current form to extract the name and
+%%% arity of the function.
+%%%
+
+update_fun_name(Token, #epp{fname=Toks0}=St) when is_list(Toks0) ->
+ %% ?FUNCTION_NAME or ?FUNCTION_ARITY is used for the first time in
+ %% a function. First expand macros (except ?FUNCTION_NAME and
+ %% ?FUNCTION_ARITY) in the form.
+
+ Toks1 = (catch expand_macros(Toks0, St#epp{fname=undefined})),
+
+ %% Now extract the name and arity from the stream of tokens, and store
+ %% the result in the #epp{} record so we don't have to do it
+ %% again.
+
+ case Toks1 of
+ [{atom,_,Name},{'(',_}|Toks] ->
+ %% This is the beginning of a function definition.
+ %% Scan the token stream up to the matching right
+ %% parenthesis and count the number of arguments.
+ FA = update_fun_name_1(Toks, 1, {Name,0}, St),
+ St#epp{fname=FA};
+ [{'?',_}|_] ->
+ %% ?FUNCTION_NAME/?FUNCTION_ARITY used at the beginning
+ %% of a form. Does not make sense.
+ {var,_,Macro} = Token,
+ throw({error,loc(Token),{illegal_function_usage,Macro}});
+ _ when is_list(Toks1) ->
+ %% Not the beginning of a function (an attribute or a
+ %% syntax error).
+ {var,_,Macro} = Token,
+ throw({error,loc(Token),{illegal_function,Macro}});
+ _ ->
+ %% A macro expansion error. Return a dummy value and
+ %% let the caller notice and handle the error.
+ St#epp{fname={'_',0}}
+ end;
+update_fun_name(_Token, St) ->
+ St.
+
+update_fun_name_1([Tok|Toks], L, FA, St) ->
+ case classify_token(Tok) of
+ comma ->
+ if
+ L =:= 1 ->
+ {Name,Arity} = FA,
+ update_fun_name_1(Toks, L, {Name,Arity+1}, St);
+ true ->
+ update_fun_name_1(Toks, L, FA, St)
+ end;
+ left ->
+ update_fun_name_1(Toks, L+1, FA, St);
+ right when L =:= 1 ->
+ FA;
+ right ->
+ update_fun_name_1(Toks, L-1, FA, St);
+ other ->
+ case FA of
+ {Name,0} ->
+ update_fun_name_1(Toks, L, {Name,1}, St);
+ {_,_} ->
+ update_fun_name_1(Toks, L, FA, St)
+ end
+ end;
+update_fun_name_1([], _, FA, _) ->
+ %% Syntax error, but never mind.
+ FA.
+
+classify_token({C,_}) -> classify_token_1(C);
+classify_token(_) -> other.
+
+classify_token_1(',') -> comma;
+classify_token_1('(') -> left;
+classify_token_1('{') -> left;
+classify_token_1('[') -> left;
+classify_token_1('<<') -> left;
+classify_token_1(')') -> right;
+classify_token_1('}') -> right;
+classify_token_1(']') -> right;
+classify_token_1('>>') -> right;
+classify_token_1(_) -> other.
+
+
%%% stringify(Ts, L) returns a list of one token: a string which when
%%% tokenized would yield the token list Ts.
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index b1c574ea60..6f8e5e8449 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -311,7 +311,7 @@ bit_size_expr -> expr_max : '$1'.
list_comprehension -> '[' expr '||' lc_exprs ']' :
{lc,?anno('$1'),'$2','$4'}.
-binary_comprehension -> '<<' binary '||' lc_exprs '>>' :
+binary_comprehension -> '<<' expr_max '||' lc_exprs '>>' :
{bc,?anno('$1'),'$2','$4'}.
lc_exprs -> lc_expr : ['$1'].
lc_exprs -> lc_expr ',' lc_exprs : ['$1'|'$3'].
diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl
index 008beb8b67..d921a69108 100644
--- a/lib/stdlib/src/filename.erl
+++ b/lib/stdlib/src/filename.erl
@@ -36,6 +36,7 @@
extension/1, join/1, join/2, pathtype/1,
rootname/1, rootname/2, split/1, nativename/1]).
-export([find_src/1, find_src/2, flatten/1]).
+-export([basedir/2, basedir/3]).
%% Undocumented and unsupported exports.
-export([append/2]).
@@ -139,6 +140,7 @@ absname_join(AbsBase, Name) ->
-spec basename(Filename) -> file:filename_all() when
Filename :: file:name_all().
+
basename(Name) when is_binary(Name) ->
case os:type() of
{win32,_} ->
@@ -954,3 +956,161 @@ filename_string_to_binary(List) ->
Bin
end.
+%% Application Base Directories
+%% basedir
+%% http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
+
+-type basedir_type() :: 'user_cache' | 'user_config' | 'user_data'
+ | 'user_log'
+ | 'site_config' | 'site_data'.
+
+-spec basedir(Type,Application) -> file:filename_all() when
+ Type :: basedir_type(),
+ Application :: string() | binary().
+
+basedir(Type,Application) when is_atom(Type), is_list(Application) orelse
+ is_binary(Application) ->
+ basedir(Type, Application, #{}).
+
+-spec basedir(Type,Application,Opts) -> file:filename_all() when
+ Type :: basedir_type(),
+ Application :: string() | binary(),
+ Opts :: #{author => string() | binary(),
+ os => 'windows' | 'darwin' | 'linux',
+ version => string() | binary()}.
+
+basedir(Type,Application,Opts) when is_atom(Type), is_map(Opts),
+ is_list(Application) orelse
+ is_binary(Application) ->
+ Os = basedir_os_from_opts(Opts),
+ Name = basedir_name_from_opts(Os,Application,Opts),
+ Base = basedir_from_os(Type,Os),
+ case {Type,Os} of
+ {user_log,linux} ->
+ filename:join([Base,Name,"log"]);
+ {user_log,windows} ->
+ filename:join([Base,Name,"Logs"]);
+ {user_cache,windows} ->
+ filename:join([Base,Name,"Cache"]);
+ {Type,_} when Type =:= site_config orelse Type =:= site_data ->
+ [filename:join([B,Name]) || B <- Base];
+ _ ->
+ filename:join([Base,Name])
+ end.
+
+basedir_os_from_opts(#{os := linux}) -> linux;
+basedir_os_from_opts(#{os := windows}) -> windows;
+basedir_os_from_opts(#{os := darwin}) -> darwin;
+basedir_os_from_opts(#{}) -> basedir_os_type().
+
+basedir_name_from_opts(windows,App,#{author:=Author,version:=Vsn}) ->
+ filename:join([Author,App,Vsn]);
+basedir_name_from_opts(windows,App,#{author:=Author}) ->
+ filename:join([Author,App]);
+basedir_name_from_opts(_,App,#{version:=Vsn}) ->
+ filename:join([App,Vsn]);
+basedir_name_from_opts(_,App,_) ->
+ App.
+
+basedir_from_os(Type,Os) ->
+ case Os of
+ linux -> basedir_linux(Type);
+ darwin -> basedir_darwin(Type);
+ windows -> basedir_windows(Type)
+ end.
+
+-define(basedir_linux_user_data, ".local/share").
+-define(basedir_linux_user_config, ".config").
+-define(basedir_linux_user_cache, ".cache").
+-define(basedir_linux_user_log, ".cache"). %% .cache/App/log
+-define(basedir_linux_site_data, "/usr/local/share/:/usr/share/").
+-define(basedir_linux_site_config, "/etc/xdg").
+
+basedir_linux(Type) ->
+ case Type of
+ user_data -> getenv("XDG_DATA_HOME", ?basedir_linux_user_data, true);
+ user_config -> getenv("XDG_CONFIG_HOME",?basedir_linux_user_config,true);
+ user_cache -> getenv("XDG_CACHE_HOME", ?basedir_linux_user_cache, true);
+ user_log -> getenv("XDG_CACHE_HOME", ?basedir_linux_user_log, true);
+ site_data ->
+ Base = getenv("XDG_DATA_DIRS",?basedir_linux_site_data,false),
+ string:tokens(Base,":");
+ site_config ->
+ Base = getenv("XDG_CONFIG_DIRS",?basedir_linux_site_config,false),
+ string:tokens(Base,":")
+ end.
+
+-define(basedir_darwin_user_data, "Library/Application Support").
+-define(basedir_darwin_user_config, "Library/Application Support").
+-define(basedir_darwin_user_cache, "Library/Caches").
+-define(basedir_darwin_user_log, "Library/Logs").
+-define(basedir_darwin_site_data, "/Library/Application Support").
+-define(basedir_darwin_site_config, "/Library/Application Support").
+
+basedir_darwin(Type) ->
+ case Type of
+ user_data -> basedir_join_home(?basedir_darwin_user_data);
+ user_config -> basedir_join_home(?basedir_darwin_user_config);
+ user_cache -> basedir_join_home(?basedir_darwin_user_cache);
+ user_log -> basedir_join_home(?basedir_darwin_user_log);
+ site_data -> [?basedir_darwin_site_data];
+ site_config -> [?basedir_darwin_site_config]
+ end.
+
+%% On Windows:
+%% ex. C:\Users\egil\AppData\Local\Ericsson\Erlang
+%% %LOCALAPPDATA% is defined on Windows 7 and onwards
+%% %APPDATA% is used instead of %LOCALAPPDATA% if it's not defined.
+%% %APPDATA% is used for roaming, i.e. for user_config on Windows 7 and beyond.
+%%
+%% user_data %LOCALAPPDATA%[/$author]/$appname[/$version]
+%% user_config %APPDATA%[/$author]/$appname[/$version]
+%% user_cache %LOCALAPPDATA%[/$author]/$appname[/$version]/Cache
+%% user_log %LOCALAPPDATA%[/$author]/$appname[/$version]/Logs
+
+basedir_windows(Type) ->
+ %% If LOCALAPPDATA is not defined we are likely on an
+ %% XP machine. Use APPDATA instead.
+ AppData = basedir_windows_appdata(),
+ case Type of
+ user_data -> getenv("LOCALAPPDATA", AppData);
+ user_config -> AppData;
+ user_cache -> getenv("LOCALAPPDATA", AppData);
+ user_log -> getenv("LOCALAPPDATA", AppData);
+ site_data -> [];
+ site_config -> []
+ end.
+
+basedir_windows_appdata() ->
+ case os:getenv("APPDATA") of
+ Invalid when Invalid =:= false orelse Invalid =:= [] ->
+ erlang:error(noappdata);
+ Val -> Val
+ end.
+
+%% basedir aux
+
+getenv(K,Def,false) -> getenv(K,Def);
+getenv(K,Def,true) -> getenv(K,basedir_join_home(Def)).
+
+getenv(K,Def) ->
+ case os:getenv(K) of
+ [] -> Def;
+ false -> Def;
+ Val -> Val
+ end.
+
+basedir_join_home(Dir) ->
+ case os:getenv("HOME") of
+ false ->
+ {ok,[[Home]]} = init:get_argument(home),
+ filename:join(Home,Dir);
+ Home -> filename:join(Home,Dir)
+ end.
+
+basedir_os_type() ->
+ case os:type() of
+ {unix,darwin} -> darwin;
+ {win32,_} -> windows;
+ _ -> linux
+ end.
diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl
index 3c798b7a04..43d10f4800 100644
--- a/lib/stdlib/src/maps.erl
+++ b/lib/stdlib/src/maps.erl
@@ -205,7 +205,7 @@ size(Val) ->
K :: term().
without(Ks,M) when is_list(Ks), is_map(M) ->
- maps:from_list([{K,V}||{K,V} <- maps:to_list(M), not lists:member(K, Ks)]);
+ lists:foldl(fun(K, M1) -> ?MODULE:remove(K, M1) end, M, Ks);
without(Ks,M) ->
erlang:error(error_type(M),[Ks,M]).
@@ -216,8 +216,16 @@ without(Ks,M) ->
Map2 :: map(),
K :: term().
-with(Ks,M) when is_list(Ks), is_map(M) ->
- maps:from_list([{K,V}||{K,V} <- maps:to_list(M), lists:member(K, Ks)]);
+with(Ks,Map1) when is_list(Ks), is_map(Map1) ->
+ Fun = fun(K, List) ->
+ case ?MODULE:find(K, Map1) of
+ {ok, V} ->
+ [{K, V} | List];
+ error ->
+ List
+ end
+ end,
+ ?MODULE:from_list(lists:foldl(Fun, [], Ks));
with(Ks,M) ->
erlang:error(error_type(M),[Ks,M]).
diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl
index 0dc2626ae8..6f546da7b8 100644
--- a/lib/stdlib/src/otp_internal.erl
+++ b/lib/stdlib/src/otp_internal.erl
@@ -650,7 +650,7 @@ obsolete_1(code, rehash, 0) ->
{deprecated, "deprecated because the code path cache feature has been removed"};
obsolete_1(overload, _, _) ->
- {deprecated, "deprecated; will be removed in OTP 19"};
+ {removed, "removed in OTP 19"};
obsolete_1(_, _, _) ->
no.
diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src
index 04cdf31ada..15d8857656 100644
--- a/lib/stdlib/src/stdlib.appup.src
+++ b/lib/stdlib/src/stdlib.appup.src
@@ -18,9 +18,9 @@
%% %CopyrightEnd%
{"%VSN%",
%% Up from - max one major revision back
- [{<<"2\\.[5-7](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-18.*
- {<<"2\\.[0-4](\\.[0-9]+)*">>,[restart_new_emulator]}], % 17.0-17.5
+ [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
+ {<<"2\\.[5-8](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-18.*
%% Down to - max one major revision back
- [{<<"2\\.[5-7](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-18.*
- {<<"2\\.[0-4](\\.[0-9]+)*">>,[restart_new_emulator]}] % 17.0-17.5
+ [{<<"3\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-19.*
+ {<<"2\\.[5-8](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-18.*
}.