summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Cottlehuber <dch@apache.org>2013-06-25 17:08:27 +0200
committerDave Cottlehuber <dch@apache.org>2013-06-25 17:08:27 +0200
commite05542490d180e9ab1bfcaeb362ebebb79522705 (patch)
tree3d2772510d88d5f0b67087c3c8665077003555f2
parent5f4a26ae140844770569fa04bd68280a9f0369d1 (diff)
parent6170654a016af86feb01573db3610d62905b6028 (diff)
downloadcouchdb-1833-feature-r16b01-with-mochiweb-backport.tar.gz
Merge branch 'refs/heads/1696-backport-mochiweb-2-4-2-1.3.x' into 1.3.x1833-feature-r16b01-with-mochiweb-backport
- support Erlang/OTP R16B and R16B01 releases - see COUCHDB-1696 and COUCHDB-1833 respectively Conflicts: share/doc/src/changelog.rst
-rw-r--r--CHANGES1
-rw-r--r--NEWS3
-rw-r--r--NOTICE4
-rw-r--r--configure.ac4
-rw-r--r--share/doc/src/changelog.rst15
-rw-r--r--src/couchdb/couch_app.erl2
-rw-r--r--src/mochiweb/Makefile.am6
-rw-r--r--src/mochiweb/mochifmt_records.erl2
-rw-r--r--src/mochiweb/mochifmt_std.erl2
-rw-r--r--src/mochiweb/mochiweb_request.erl2
-rw-r--r--src/mochiweb/mochiweb_response.erl2
-rw-r--r--src/mochiweb/pmod_pt.erl463
-rwxr-xr-xtest/etap/120-stats-collect.t14
13 files changed, 507 insertions, 13 deletions
diff --git a/CHANGES b/CHANGES
index a26f4b6d2..0355f7360 100644
--- a/CHANGES
+++ b/CHANGES
@@ -21,6 +21,7 @@ Misc:
* Improve documentation: better structure, improve language, less duplication.
* Improvements to test suite and VPATH build system.
+ * Add support for Erlang R16B through pmod-transform library.
Version 1.3.0
-------------
diff --git a/NEWS b/NEWS
index 486daf03e..9d777b8c0 100644
--- a/NEWS
+++ b/NEWS
@@ -14,7 +14,8 @@ Version 1.3.1
* Don't log about missing .compact files.
* Fix bug in WARN level logging from 1.3.0.
* Improve documentation: better structure, improve language, less duplication.
- * Improvements to test suite.
+ * Improvements to test suite and build system.
+ * Add support for Erlang R16B through pmod-transform library.
Version 1.3.0
-------------
diff --git a/NOTICE b/NOTICE
index e6ef17d47..a8e72102e 100644
--- a/NOTICE
+++ b/NOTICE
@@ -73,3 +73,7 @@ This product also includes the following third-party components:
* Underscore.js 1.4.2 (http://underscorejs.org)
Copyright 2012, Jeremy Ashkenas
+
+ * Erlang/OTP pmod parse-transfrom library
+
+ Copyright, 2013, ERLANG PUBLIC LICENSE Version 1.1
diff --git a/configure.ac b/configure.ac
index 53e23f147..dafdb9edc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -409,7 +409,7 @@ esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking Erlang version compatibility" >&5
$as_echo_n "checking Erlang version compatibility... " >&6; }
-erlang_version_error="The installed Erlang version must be >= R13B04 (erts-5.7.4) and < R16A (erts-5.10)"
+erlang_version_error="The installed Erlang version must be >= R13B04 (erts-5.7.4) and < R17 (erts-5.11)"
version="`${ERL} -version 2>&1 | ${SED} 's/[[^0-9]]/ /g'` 0 0 0"
major_version=`echo $version | ${AWK} "{print \\$1}"`
@@ -420,7 +420,7 @@ if test $major_version -ne 5; then
as_fn_error $? "$erlang_version_error" "$LINENO" 5
fi
-if test $minor_version -lt 7 -o $minor_version -gt 9; then
+if test $minor_version -lt 7 -o $minor_version -gt 10; then
as_fn_error $? "$erlang_version_error" "$LINENO" 5
fi
diff --git a/share/doc/src/changelog.rst b/share/doc/src/changelog.rst
index b933b9a78..ff6eca431 100644
--- a/share/doc/src/changelog.rst
+++ b/share/doc/src/changelog.rst
@@ -43,6 +43,19 @@ CouchDB 1.0.x release.
CouchDB 1.0.x you must back up your data files before performing the
upgrade and compaction process.
+Version 1.3.2
+-------------
+
+This version has not been released yet.
+
+Build System
+^^^^^^^^^^^^
+
+* :issue:`1696`: Add support for Erlang R16B through pmod-transform library.
+ :commit:`50e5b1e`
+* :issue:`1833`: Support Erlang R16B01.
+ :commit:`01afaa4f`
+
Version 1.3.1
-------------
@@ -69,7 +82,7 @@ Miscellaneous
* Improve documentation: better structure, improve language, less duplication.
* :issue:`1784`: Improvements to test suite and VPATH build system.
- :commit:`01afaa4f`
+* Improvements to test suite.
Version 1.3.0
-------------
diff --git a/src/couchdb/couch_app.erl b/src/couchdb/couch_app.erl
index f109780de..e73c402a1 100644
--- a/src/couchdb/couch_app.erl
+++ b/src/couchdb/couch_app.erl
@@ -20,7 +20,7 @@
start(_Type, DefaultIniFiles) ->
IniFiles = get_ini_files(DefaultIniFiles),
- case start_apps([crypto, public_key, sasl, inets, oauth, ssl, ibrowse, mochiweb, os_mon]) of
+ case start_apps([crypto, asn1, public_key, sasl, inets, oauth, ssl, ibrowse, mochiweb, os_mon]) of
ok ->
couch_server_sup:start_link(IniFiles);
{error, Reason} ->
diff --git a/src/mochiweb/Makefile.am b/src/mochiweb/Makefile.am
index d31a6eb9f..d4c150e15 100644
--- a/src/mochiweb/Makefile.am
+++ b/src/mochiweb/Makefile.am
@@ -17,6 +17,7 @@ endif
mochiwebebindir = $(localerlanglibdir)/mochiweb-1.4.1/ebin
mochiweb_file_collection = \
+ pmod_pt.erl \
mochifmt.erl \
mochifmt_records.erl \
mochifmt_std.erl \
@@ -55,6 +56,7 @@ mochiweb_file_collection = \
reloader.erl
mochiwebebin_make_generated_file_list = \
+ pmod_pt.beam \
mochifmt.beam \
mochifmt_records.beam \
mochifmt_std.beam \
@@ -106,5 +108,7 @@ CLEANFILES = \
mochijson2.beam: mochijson2.erl
$(ERLC) $(ERLC_FLAGS) $(MOCHIJSON_ERLC_FLAGS) $<
+mochifmt_records.beam mochifmt_std.beam mochiweb_request.beam mochiweb_response.beam: pmod_pt.beam
+
%.beam: %.erl
- $(ERLC) $(ERLC_FLAGS) $<
+ $(ERLC) $(ERLC_FLAGS) -pa . $<
diff --git a/src/mochiweb/mochifmt_records.erl b/src/mochiweb/mochifmt_records.erl
index 2326d1dda..d720df27a 100644
--- a/src/mochiweb/mochifmt_records.erl
+++ b/src/mochiweb/mochifmt_records.erl
@@ -13,6 +13,8 @@
-author('bob@mochimedia.com').
-export([get_value/2]).
+-compile({parse_transform, pmod_pt}).
+
get_value(Key, Rec) when is_tuple(Rec) and is_atom(element(1, Rec)) ->
try begin
Atom = list_to_existing_atom(Key),
diff --git a/src/mochiweb/mochifmt_std.erl b/src/mochiweb/mochifmt_std.erl
index d4d74f6f6..4005f6d17 100644
--- a/src/mochiweb/mochifmt_std.erl
+++ b/src/mochiweb/mochifmt_std.erl
@@ -7,6 +7,8 @@
-author('bob@mochimedia.com').
-export([format/2, get_value/2, format_field/2, get_field/2, convert_field/2]).
+-compile({parse_transform, pmod_pt}).
+
format(Format, Args) ->
mochifmt:format(Format, Args, THIS).
diff --git a/src/mochiweb/mochiweb_request.erl b/src/mochiweb/mochiweb_request.erl
index 980f5ad01..aa10e7d0d 100644
--- a/src/mochiweb/mochiweb_request.erl
+++ b/src/mochiweb/mochiweb_request.erl
@@ -23,6 +23,8 @@
-export([accepted_encodings/1]).
-export([accepts_content_type/1]).
+-compile({parse_transform, pmod_pt}).
+
-define(SAVE_QS, mochiweb_request_qs).
-define(SAVE_PATH, mochiweb_request_path).
-define(SAVE_RECV, mochiweb_request_recv).
diff --git a/src/mochiweb/mochiweb_response.erl b/src/mochiweb/mochiweb_response.erl
index ab8ee61ce..12bdfad4c 100644
--- a/src/mochiweb/mochiweb_response.erl
+++ b/src/mochiweb/mochiweb_response.erl
@@ -11,6 +11,8 @@
-export([get_header_value/1, get/1, dump/0]).
-export([send/1, write_chunk/1]).
+-compile({parse_transform, pmod_pt}).
+
%% @spec get_header_value(string() | atom() | binary()) -> string() | undefined
%% @doc Get the value of the given response header.
get_header_value(K) ->
diff --git a/src/mochiweb/pmod_pt.erl b/src/mochiweb/pmod_pt.erl
new file mode 100644
index 000000000..53cff6675
--- /dev/null
+++ b/src/mochiweb/pmod_pt.erl
@@ -0,0 +1,463 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013. 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(pmod_pt).
+-export([parse_transform/2,
+ format_error/1]).
+
+%% Expand function definition forms of parameterized module.
+%% The code is based on the code in sys_expand_pmod which used to be
+%% included in the compiler, but details are different because
+%% sys_pre_expand has not been run. In particular:
+%%
+%% * Record definitions are still present and must be handled.
+%%
+%% * (Syntatic) local calls may actually be calls to an imported
+%% funtion or a BIF. It is a local call if and only if there
+%% is a definition for the function in the module.
+%%
+%% * When we introduce the module parameters and 'THIS' in each
+%% function, we must artificially use it to avoid a warning for
+%% unused variables.
+%%
+%% * On the other hand, we don't have to worry about module_info/0,1
+%% because they have not been added yet.
+
+-record(pmod, {parameters,
+ defined
+ }).
+
+parse_transform(Forms0, _Options) ->
+ put(?MODULE, []),
+ Forms = transform(Forms0),
+ case erase(?MODULE) of
+ [] ->
+ Forms;
+ [_|_]=Errors ->
+ File = get_file(Forms),
+ {error,[{File,Errors}],[]}
+ end.
+
+format_error(extends_self) ->
+ "cannot extend from self";
+format_error(define_instance) ->
+ "defining instance function not allowed in parameterized module".
+
+add_error(Line, Error) ->
+ put(?MODULE, get(?MODULE) ++ [{Line,?MODULE,Error}]).
+
+get_file([{attribute,_,file,{File,_}}|_]) -> File;
+get_file([_|T]) -> get_file(T).
+
+transform(Forms0) ->
+ Def = collect_defined(Forms0),
+ {Base,ModAs,Forms1} = attribs(Forms0, [], undefined, []),
+ {Mod,Ps0} = case ModAs of
+ {M0,P0} -> {M0,P0};
+ M0 -> {M0,undefined}
+ end,
+ Forms2 = case Ps0 of
+ undefined ->
+ Forms1;
+ _ ->
+ pmod_expand(Forms1, Mod, Base, Ps0, Def)
+ end,
+
+ %% Add new functions.
+ NewFs0 = maybe_extend(Base, Mod, Ps0),
+ NewExps = collect_defined(NewFs0),
+ Forms3 = add_attributes(Forms2, [{attribute,0,export,NewExps}]),
+ add_new_funcs(Forms3, NewFs0).
+
+pmod_expand(Forms0, Mod, Base, Ps0, Def) ->
+ Ps = if is_atom(Base) ->
+ ['BASE' | Ps0];
+ true ->
+ Ps0
+ end,
+ St0 = #pmod{parameters=Ps,defined=gb_sets:from_list(Def)},
+ {Forms1,_} = forms(Forms0, St0),
+ Forms2 = update_exps(Forms1),
+ Forms3 = update_forms(Forms2),
+ NewFs0 = add_instance(Mod, Ps, []),
+ NewFs = ensure_new(Base, Ps0, NewFs0),
+ Forms = add_new_funcs(Forms3, NewFs),
+ NewExps = collect_defined(NewFs),
+ add_attributes(Forms, [{attribute,0,export,NewExps}]).
+
+add_attributes([{attribute,_,module,_}=F|Fs], Attrs) ->
+ [F|Attrs++Fs];
+add_attributes([F|Fs], Attrs) ->
+ [F|add_attributes(Fs, Attrs)].
+
+add_new_funcs([{eof,_}|_]=Fs, NewFs) ->
+ NewFs ++ Fs;
+add_new_funcs([F|Fs], Es) ->
+ [F|add_new_funcs(Fs, Es)].
+
+maybe_extend([], _, _) ->
+ %% No 'extends' attribute.
+ [];
+maybe_extend(Base, _Mod, undefined) ->
+ %% There is a an 'extends' attribute; the module is not parameterized.
+ Name = '$handle_undefined_function',
+ Args = [{var,0,'Func'},{var,0,'Args'}],
+ Body = [make_apply({atom,0,Base}, {var,0,'Func'}, {var,0,'Args'})],
+ F = {function,0,Name,2,[{clause,0,Args,[],Body}]},
+ [F];
+maybe_extend(Base, Mod, Ps) ->
+ %% There is a an 'extends' attribute; the module is parameterized.
+ Name = '$handle_undefined_function',
+ Args = [{var,0,'Func'},{var,0,'Args'}],
+ DontCares = [{var,0,'_'} || _ <- Ps],
+ TuplePs = {tuple,0,[{atom,0,Mod},{var,0,'BaseVars'}|DontCares]},
+ G = [{call,0,{atom,0,is_atom},
+ [{call,0,{atom,0,element},
+ [{integer,0,1},{var,0,'BaseVars'}]}]}],
+ FixedArgs = make_lists_rev([{var,0,'Rs'},
+ {cons,0,{var,0,'BaseVars'},{nil,0}}]),
+ Body = [{'case',0,make_lists_rev([{var,0,'Args'}]),
+ [{clause,0,[{cons,0,TuplePs,{var,0,'Rs'}}],[G],
+ [make_apply({atom,0,Base}, {var,0,'Func'}, FixedArgs)]},
+ {clause,0,[{var,0,'_'}],[],
+ [make_apply({atom,0,Base}, {var,0,'Func'}, {var,0,'Args'})]}
+ ]}],
+ F = {function,0,Name,2,[{clause,0,Args,[],Body}]},
+ [F].
+
+make_apply(M, F, A) ->
+ {call,0,{remote,0,{atom,0,erlang},{atom,0,apply}},[M,F,A]}.
+
+make_lists_rev(As) ->
+ {call,0,{remote,0,{atom,0,lists},{atom,0,reverse}},As}.
+
+ensure_new(Base, Ps, Fs) ->
+ case has_new(Fs) of
+ true ->
+ Fs;
+ false ->
+ add_new(Base, Ps, Fs)
+ end.
+
+has_new([{function,_L,new,_A,_Cs} | _Fs]) ->
+ true;
+has_new([_ | Fs]) ->
+ has_new(Fs);
+has_new([]) ->
+ false.
+
+add_new(Base, Ps, Fs) ->
+ Vs = [{var,0,V} || V <- Ps],
+ As = if is_atom(Base) ->
+ [{call,0,{remote,0,{atom,0,Base},{atom,0,new}},Vs} | Vs];
+ true ->
+ Vs
+ end,
+ Body = [{call,0,{atom,0,instance},As}],
+ add_func(new, Vs, Body, Fs).
+
+add_instance(Mod, Ps, Fs) ->
+ Vs = [{var,0,V} || V <- Ps],
+ AbsMod = [{tuple,0,[{atom,0,Mod}|Vs]}],
+ add_func(instance, Vs, AbsMod, Fs).
+
+add_func(Name, Args, Body, Fs) ->
+ A = length(Args),
+ F = {function,0,Name,A,[{clause,0,Args,[],Body}]},
+ [F|Fs].
+
+collect_defined(Fs) ->
+ [{N,A} || {function,_,N,A,_} <- Fs].
+
+attribs([{attribute,Line,module,{Mod,_}=ModAs}|T], Base, _, Acc) ->
+ attribs(T, Base, ModAs, [{attribute,Line,module,Mod}|Acc]);
+attribs([{attribute,_,module,Mod}=H|T], Base, _, Acc) ->
+ attribs(T, Base, Mod, [H|Acc]);
+attribs([{attribute,Line,extends,Base}|T], Base0, Ps, Acc) when is_atom(Base) ->
+ Mod = case Ps of
+ {Mod0,_} -> Mod0;
+ Mod0 -> Mod0
+ end,
+ case Mod of
+ Base ->
+ add_error(Line, extends_self),
+ attribs(T, Base0, Ps, Acc);
+ _ ->
+ attribs(T, Base, Ps, Acc)
+ end;
+attribs([H|T], Base, Ps, Acc) ->
+ attribs(T, Base, Ps, [H|Acc]);
+attribs([], Base, Ps, Acc) ->
+ {Base,Ps,lists:reverse(Acc)}.
+
+%% This is extremely simplistic for now; all functions get an extra
+%% parameter, whether they need it or not, except for static functions.
+
+update_function_name({F,A}) when F =/= new ->
+ {F,A+1};
+update_function_name(E) ->
+ E.
+
+update_forms([{function,L,N,A,Cs}|Fs]) when N =/= new ->
+ [{function,L,N,A+1,Cs}|update_forms(Fs)];
+update_forms([F|Fs]) ->
+ [F|update_forms(Fs)];
+update_forms([]) ->
+ [].
+
+update_exps([{attribute,Line,export,Es0}|T]) ->
+ Es = [update_function_name(E) || E <- Es0],
+ [{attribute,Line,export,Es}|update_exps(T)];
+update_exps([H|T]) ->
+ [H|update_exps(T)];
+update_exps([]) ->
+ [].
+
+%% Process the program forms.
+
+forms([F0|Fs0],St0) ->
+ {F1,St1} = form(F0,St0),
+ {Fs1,St2} = forms(Fs0,St1),
+ {[F1|Fs1],St2};
+forms([], St0) ->
+ {[], St0}.
+
+%% Only function definitions are of interest here. State is not updated.
+form({function,Line,instance,_Arity,_Clauses}=F,St) ->
+ add_error(Line, define_instance),
+ {F,St};
+form({function,Line,Name0,Arity0,Clauses0},St) when Name0 =/= new ->
+ {Name,Arity,Clauses} = function(Name0, Arity0, Clauses0, St),
+ {{function,Line,Name,Arity,Clauses},St};
+%% Pass anything else through
+form(F,St) -> {F,St}.
+
+function(Name, Arity, Clauses0, St) ->
+ Clauses1 = clauses(Clauses0,St),
+ {Name,Arity,Clauses1}.
+
+clauses([C|Cs],#pmod{parameters=Ps}=St) ->
+ {clause,L,H,G,B0} = clause(C,St),
+ T = {tuple,L,[{var,L,V} || V <- ['_'|Ps]]},
+ B = [{match,L,{var,L,'_'},{var,L,V}} || V <- ['THIS'|Ps]] ++ B0,
+ [{clause,L,H++[{match,L,T,{var,L,'THIS'}}],G,B}|clauses(Cs,St)];
+clauses([],_St) -> [].
+
+clause({clause,Line,H,G,B0},St) ->
+ %% We never update H and G, so we will just copy them.
+ B1 = exprs(B0,St),
+ {clause,Line,H,G,B1}.
+
+pattern_grp([{bin_element,L1,E1,S1,T1} | Fs],St) ->
+ S2 = case S1 of
+ default ->
+ default;
+ _ ->
+ expr(S1,St)
+ end,
+ T2 = case T1 of
+ default ->
+ default;
+ _ ->
+ bit_types(T1)
+ end,
+ [{bin_element,L1,expr(E1,St),S2,T2} | pattern_grp(Fs,St)];
+pattern_grp([],_St) ->
+ [].
+
+bit_types([]) ->
+ [];
+bit_types([Atom | Rest]) when is_atom(Atom) ->
+ [Atom | bit_types(Rest)];
+bit_types([{Atom, Integer} | Rest]) when is_atom(Atom), is_integer(Integer) ->
+ [{Atom, Integer} | bit_types(Rest)].
+
+exprs([E0|Es],St) ->
+ E1 = expr(E0,St),
+ [E1|exprs(Es,St)];
+exprs([],_St) -> [].
+
+expr({var,_L,_V}=Var,_St) ->
+ Var;
+expr({integer,_Line,_I}=Integer,_St) -> Integer;
+expr({float,_Line,_F}=Float,_St) -> Float;
+expr({atom,_Line,_A}=Atom,_St) -> Atom;
+expr({string,_Line,_S}=String,_St) -> String;
+expr({char,_Line,_C}=Char,_St) -> Char;
+expr({nil,_Line}=Nil,_St) -> Nil;
+expr({cons,Line,H0,T0},St) ->
+ H1 = expr(H0,St),
+ T1 = expr(T0,St),
+ {cons,Line,H1,T1};
+expr({lc,Line,E0,Qs0},St) ->
+ Qs1 = lc_bc_quals(Qs0,St),
+ E1 = expr(E0,St),
+ {lc,Line,E1,Qs1};
+expr({bc,Line,E0,Qs0},St) ->
+ Qs1 = lc_bc_quals(Qs0,St),
+ E1 = expr(E0,St),
+ {bc,Line,E1,Qs1};
+expr({tuple,Line,Es0},St) ->
+ Es1 = expr_list(Es0,St),
+ {tuple,Line,Es1};
+expr({record_index,_,_,_}=RI, _St) ->
+ RI;
+expr({record,Line,Name,Is0},St) ->
+ Is = record_fields(Is0,St),
+ {record,Line,Name,Is};
+expr({record,Line,E0,Name,Is0},St) ->
+ E = expr(E0,St),
+ Is = record_fields(Is0,St),
+ {record,Line,E,Name,Is};
+expr({record_field,Line,E0,Name,Key},St) ->
+ E = expr(E0,St),
+ {record_field,Line,E,Name,Key};
+expr({block,Line,Es0},St) ->
+ Es1 = exprs(Es0,St),
+ {block,Line,Es1};
+expr({'if',Line,Cs0},St) ->
+ Cs1 = icr_clauses(Cs0,St),
+ {'if',Line,Cs1};
+expr({'case',Line,E0,Cs0},St) ->
+ E1 = expr(E0,St),
+ Cs1 = icr_clauses(Cs0,St),
+ {'case',Line,E1,Cs1};
+expr({'receive',Line,Cs0},St) ->
+ Cs1 = icr_clauses(Cs0,St),
+ {'receive',Line,Cs1};
+expr({'receive',Line,Cs0,To0,ToEs0},St) ->
+ To1 = expr(To0,St),
+ ToEs1 = exprs(ToEs0,St),
+ Cs1 = icr_clauses(Cs0,St),
+ {'receive',Line,Cs1,To1,ToEs1};
+expr({'try',Line,Es0,Scs0,Ccs0,As0},St) ->
+ Es1 = exprs(Es0,St),
+ Scs1 = icr_clauses(Scs0,St),
+ Ccs1 = icr_clauses(Ccs0,St),
+ As1 = exprs(As0,St),
+ {'try',Line,Es1,Scs1,Ccs1,As1};
+expr({'fun',_,{function,_,_,_}}=ExtFun,_St) ->
+ ExtFun;
+expr({'fun',Line,Body},St) ->
+ case Body of
+ {clauses,Cs0} ->
+ Cs1 = fun_clauses(Cs0,St),
+ {'fun',Line,{clauses,Cs1}};
+ {function,F,A} = Function ->
+ {F1,A1} = update_function_name({F,A}),
+ if A1 =:= A ->
+ {'fun',Line,Function};
+ true ->
+ %% Must rewrite local fun-name to a fun that does a
+ %% call with the extra THIS parameter.
+ As = make_vars(A, Line),
+ As1 = As ++ [{var,Line,'THIS'}],
+ Call = {call,Line,{atom,Line,F1},As1},
+ Cs = [{clause,Line,As,[],[Call]}],
+ {'fun',Line,{clauses,Cs}}
+ end;
+ {function,_M,_F,_A} = Fun4 -> %This is an error in lint!
+ {'fun',Line,Fun4}
+ end;
+expr({call,Lc,{atom,_,instance}=Name,As0},St) ->
+ %% All local functions 'instance(...)' are static by definition,
+ %% so they do not take a 'THIS' argument when called
+ As1 = expr_list(As0,St),
+ {call,Lc,Name,As1};
+expr({call,Lc,{atom,_,new}=Name,As0},St) ->
+ %% All local functions 'new(...)' are static by definition,
+ %% so they do not take a 'THIS' argument when called
+ As1 = expr_list(As0,St),
+ {call,Lc,Name,As1};
+expr({call,Lc,{atom,_Lf,F}=Atom,As0}, #pmod{defined=Def}=St) ->
+ As1 = expr_list(As0,St),
+ case gb_sets:is_member({F,length(As0)}, Def) of
+ false ->
+ %% BIF or imported function.
+ {call,Lc,Atom,As1};
+ true ->
+ %% Local function call - needs THIS parameter.
+ {call,Lc,Atom,As1 ++ [{var,0,'THIS'}]}
+ end;
+expr({call,Line,F0,As0},St) ->
+ %% Other function call
+ F1 = expr(F0,St),
+ As1 = expr_list(As0,St),
+ {call,Line,F1,As1};
+expr({'catch',Line,E0},St) ->
+ E1 = expr(E0,St),
+ {'catch',Line,E1};
+expr({match,Line,P,E0},St) ->
+ E1 = expr(E0,St),
+ {match,Line,P,E1};
+expr({bin,Line,Fs},St) ->
+ Fs2 = pattern_grp(Fs,St),
+ {bin,Line,Fs2};
+expr({op,Line,Op,A0},St) ->
+ A1 = expr(A0,St),
+ {op,Line,Op,A1};
+expr({op,Line,Op,L0,R0},St) ->
+ L1 = expr(L0,St),
+ R1 = expr(R0,St),
+ {op,Line,Op,L1,R1};
+%% The following are not allowed to occur anywhere!
+expr({remote,Line,M0,F0},St) ->
+ M1 = expr(M0,St),
+ F1 = expr(F0,St),
+ {remote,Line,M1,F1}.
+
+expr_list([E0|Es],St) ->
+ E1 = expr(E0,St),
+ [E1|expr_list(Es,St)];
+expr_list([],_St) -> [].
+
+record_fields([{record_field,L,K,E0}|T],St) ->
+ E = expr(E0,St),
+ [{record_field,L,K,E}|record_fields(T,St)];
+record_fields([],_) -> [].
+
+icr_clauses([C0|Cs],St) ->
+ C1 = clause(C0,St),
+ [C1|icr_clauses(Cs,St)];
+icr_clauses([],_St) -> [].
+
+lc_bc_quals([{generate,Line,P,E0}|Qs],St) ->
+ E1 = expr(E0,St),
+ [{generate,Line,P,E1}|lc_bc_quals(Qs,St)];
+lc_bc_quals([{b_generate,Line,P,E0}|Qs],St) ->
+ E1 = expr(E0,St),
+ [{b_generate,Line,P,E1}|lc_bc_quals(Qs,St)];
+lc_bc_quals([E0|Qs],St) ->
+ E1 = expr(E0,St),
+ [E1|lc_bc_quals(Qs,St)];
+lc_bc_quals([],_St) -> [].
+
+fun_clauses([C0|Cs],St) ->
+ C1 = clause(C0,St),
+ [C1|fun_clauses(Cs,St)];
+fun_clauses([],_St) -> [].
+
+make_vars(N, L) ->
+ make_vars(1, N, L).
+
+make_vars(N, M, L) when N =< M ->
+ V = list_to_atom("X"++integer_to_list(N)),
+ [{var,L,V} | make_vars(N + 1, M, L)];
+make_vars(_, _, _) ->
+ [].
diff --git a/test/etap/120-stats-collect.t b/test/etap/120-stats-collect.t
index dee88765c..a30f9ac5d 100755
--- a/test/etap/120-stats-collect.t
+++ b/test/etap/120-stats-collect.t
@@ -84,7 +84,7 @@ test_proc_counting() ->
etap:is(
couch_stats_collector:get(hoopla),
1,
- "track_process_count incrememnts the counter."
+ "track_process_count increments the counter."
),
TwicePid = spawn(fun() ->
@@ -124,21 +124,21 @@ test_all() ->
couch_stats_collector:record(bar, 0.0),
couch_stats_collector:record(bar, 1.0),
etap:is(
- couch_stats_collector:all(),
- [{foo, 0}, {hoopla, 0}, {bar, [1.0, 0.0]}],
+ lists:sort(couch_stats_collector:all()),
+ [ {bar,[1.0,0.0]}, {foo,0}, { hoopla,0} ],
"all/0 returns all counters and absolute values."
),
etap:is(
- couch_stats_collector:all(incremental),
- [{foo, 0}, {hoopla, 0}],
+ lists:sort(couch_stats_collector:all(incremental)),
+ [ {foo, 0}, {hoopla, 0} ],
"all/1 returns only the specified type."
),
couch_stats_collector:record(zing, 90),
etap:is(
- couch_stats_collector:all(absolute),
- [{zing, [90]}, {bar, [1.0, 0.0]}],
+ lists:sort(couch_stats_collector:all(absolute)),
+ [ {bar,[1.0,0.0]}, {zing,"Z"} ],
"all/1 returns only the specified type."
),
ok.