aboutsummaryrefslogtreecommitdiffstats
path: root/lib/common_test/src/ct_line.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/common_test/src/ct_line.erl')
-rw-r--r--lib/common_test/src/ct_line.erl266
1 files changed, 266 insertions, 0 deletions
diff --git a/lib/common_test/src/ct_line.erl b/lib/common_test/src/ct_line.erl
new file mode 100644
index 0000000000..4af9da5463
--- /dev/null
+++ b/lib/common_test/src/ct_line.erl
@@ -0,0 +1,266 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2003-2009. 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%
+%%
+
+%%% @doc Parse transform for inserting line numbers
+
+-module(ct_line).
+
+-record(vars, {module, % atom() Module name
+ vsn, % atom()
+
+ init_info=[], % [{M,F,A,C,L}]
+
+ function, % atom()
+ arity, % int()
+ clause, % int()
+ lines, % [int()]
+ depth, % int()
+ is_guard=false % boolean
+ }).
+
+-export([parse_transform/2,
+ line/1]).
+
+line(LOC={{Mod,Func},_Line}) ->
+ Lines = case get(test_server_loc) of
+ [{{Mod,Func},_}|Ls] ->
+ Ls;
+ Ls when is_list(Ls) ->
+ case length(Ls) of
+ 10 ->
+ [_|T]=lists:reverse(Ls),
+ lists:reverse(T);
+ _ ->
+ Ls
+ end;
+ _ ->
+ []
+ end,
+ put(test_server_loc,[LOC|Lines]).
+
+parse_transform(Forms, _Options) ->
+ transform(Forms, _Options).
+
+%% forms(Fs) -> lists:map(fun (F) -> form(F) end, Fs).
+
+transform(Forms, _Options)->
+ Vars0 = #vars{},
+ {ok, MungedForms, _Vars} = transform(Forms, [], Vars0),
+ MungedForms.
+
+
+transform([Form|Forms], MungedForms, Vars) ->
+ case munge(Form, Vars) of
+ ignore ->
+ transform(Forms, MungedForms, Vars);
+ {MungedForm, Vars2} ->
+ transform(Forms, [MungedForm|MungedForms], Vars2)
+ end;
+transform([], MungedForms, Vars) ->
+ {ok, lists:reverse(MungedForms), Vars}.
+
+%% This code traverses the abstract code, stored as the abstract_code
+%% chunk in the BEAM file, as described in absform(3) for Erlang/OTP R8B
+%% (Vsn=abstract_v2).
+%% The abstract format after preprocessing differs slightly from the abstract
+%% format given eg using epp:parse_form, this has been noted in comments.
+munge(Form={attribute,_,module,Module}, Vars) ->
+ Vars2 = Vars#vars{module=Module},
+ {Form, Vars2};
+
+munge({function,0,module_info,_Arity,_Clauses}, _Vars) ->
+ ignore; % module_info will be added again when the forms are recompiled
+munge({function,Line,Function,Arity,Clauses}, Vars) ->
+ Vars2 = Vars#vars{function=Function,
+ arity=Arity,
+ clause=1,
+ lines=[],
+ depth=1},
+ {MungedClauses, Vars3} = munge_clauses(Clauses, Vars2, []),
+ {{function,Line,Function,Arity,MungedClauses}, Vars3};
+munge(Form, Vars) -> % attributes
+ {Form, Vars}.
+
+munge_clauses([{clause,Line,Pattern,Guards,Body}|Clauses], Vars, MClauses) ->
+ {MungedGuards, _Vars} = munge_exprs(Guards, Vars#vars{is_guard=true},[]),
+
+ case Vars#vars.depth of
+ 1 -> % function clause
+ {MungedBody, Vars2} = munge_body(Body, Vars#vars{depth=2}, []),
+ ClauseInfo = {Vars2#vars.module,
+ Vars2#vars.function,
+ Vars2#vars.arity,
+ Vars2#vars.clause,
+ length(Vars2#vars.lines)},
+ InitInfo = [ClauseInfo | Vars2#vars.init_info],
+ Vars3 = Vars2#vars{init_info=InitInfo,
+ clause=(Vars2#vars.clause)+1,
+ lines=[],
+ depth=1},
+ munge_clauses(Clauses, Vars3,
+ [{clause,Line,Pattern,MungedGuards,MungedBody}|
+ MClauses]);
+
+ 2 -> % receive-, case- or if clause
+ {MungedBody, Vars2} = munge_body(Body, Vars, []),
+ munge_clauses(Clauses, Vars2,
+ [{clause,Line,Pattern,MungedGuards,MungedBody}|
+ MClauses])
+ end;
+munge_clauses([], Vars, MungedClauses) ->
+ {lists:reverse(MungedClauses), Vars}.
+
+munge_body([Expr|Body], Vars, MungedBody) ->
+ %% Here is the place to add a call to cover:bump/6!
+ Line = element(2, Expr),
+ Lines = Vars#vars.lines,
+ case lists:member(Line,Lines) of
+ true -> % already a bump at this line!
+ {MungedExpr, Vars2} = munge_expr(Expr, Vars),
+ munge_body(Body, Vars2, [MungedExpr|MungedBody]);
+ false ->
+ Bump = {call, 0, {remote,0,{atom,0,?MODULE},{atom,0,line}},
+ [{tuple,0,[{tuple,0,[{atom,0,Vars#vars.module},
+ {atom, 0, Vars#vars.function}]},
+ {integer, 0, Line}]}]},
+ Lines2 = [Line|Lines],
+
+ {MungedExpr, Vars2} = munge_expr(Expr, Vars#vars{lines=Lines2}),
+ munge_body(Body, Vars2, [MungedExpr,Bump|MungedBody])
+ end;
+munge_body([], Vars, MungedBody) ->
+ {lists:reverse(MungedBody), Vars}.
+
+munge_expr({match,Line,ExprL,ExprR}, Vars) ->
+ {MungedExprL, Vars2} = munge_expr(ExprL, Vars),
+ {MungedExprR, Vars3} = munge_expr(ExprR, Vars2),
+ {{match,Line,MungedExprL,MungedExprR}, Vars3};
+munge_expr({tuple,Line,Exprs}, Vars) ->
+ {MungedExprs, Vars2} = munge_exprs(Exprs, Vars, []),
+ {{tuple,Line,MungedExprs}, Vars2};
+munge_expr({record,Line,Expr,Exprs}, Vars) ->
+ %% Only for Vsn=raw_abstract_v1
+ {MungedExprName, Vars2} = munge_expr(Expr, Vars),
+ {MungedExprFields, Vars3} = munge_exprs(Exprs, Vars2, []),
+ {{record,Line,MungedExprName,MungedExprFields}, Vars3};
+munge_expr({record_field,Line,ExprL,ExprR}, Vars) ->
+ %% Only for Vsn=raw_abstract_v1
+ {MungedExprL, Vars2} = munge_expr(ExprL, Vars),
+ {MungedExprR, Vars3} = munge_expr(ExprR, Vars2),
+ {{record_field,Line,MungedExprL,MungedExprR}, Vars3};
+munge_expr({cons,Line,ExprH,ExprT}, Vars) ->
+ {MungedExprH, Vars2} = munge_expr(ExprH, Vars),
+ {MungedExprT, Vars3} = munge_expr(ExprT, Vars2),
+ {{cons,Line,MungedExprH,MungedExprT}, Vars3};
+munge_expr({op,Line,Op,ExprL,ExprR}, Vars) ->
+ {MungedExprL, Vars2} = munge_expr(ExprL, Vars),
+ {MungedExprR, Vars3} = munge_expr(ExprR, Vars2),
+ {{op,Line,Op,MungedExprL,MungedExprR}, Vars3};
+munge_expr({op,Line,Op,Expr}, Vars) ->
+ {MungedExpr, Vars2} = munge_expr(Expr, Vars),
+ {{op,Line,Op,MungedExpr}, Vars2};
+munge_expr({'catch',Line,Expr}, Vars) ->
+ {MungedExpr, Vars2} = munge_expr(Expr, Vars),
+ {{'catch',Line,MungedExpr}, Vars2};
+munge_expr({call,Line1,{remote,Line2,ExprM,ExprF},Exprs},
+ Vars) when Vars#vars.is_guard==false->
+ {MungedExprM, Vars2} = munge_expr(ExprM, Vars),
+ {MungedExprF, Vars3} = munge_expr(ExprF, Vars2),
+ {MungedExprs, Vars4} = munge_exprs(Exprs, Vars3, []),
+ {{call,Line1,{remote,Line2,MungedExprM,MungedExprF},MungedExprs}, Vars4};
+munge_expr({call,Line1,{remote,_Line2,_ExprM,ExprF},Exprs},
+ Vars) when Vars#vars.is_guard==true ->
+ %% Difference in abstract format after preprocessing: BIF calls in guards
+ %% are translated to {remote,...} (which is not allowed as source form)
+ %% NOT NECESSARY FOR Vsn=raw_abstract_v1
+ munge_expr({call,Line1,ExprF,Exprs}, Vars);
+munge_expr({call,Line,Expr,Exprs}, Vars) ->
+ {MungedExpr, Vars2} = munge_expr(Expr, Vars),
+ {MungedExprs, Vars3} = munge_exprs(Exprs, Vars2, []),
+ {{call,Line,MungedExpr,MungedExprs}, Vars3};
+munge_expr({lc,Line,Expr,LC}, Vars) ->
+ {MungedExpr, Vars2} = munge_expr(Expr, Vars),
+ {MungedLC, Vars3} = munge_lc(LC, Vars2, []),
+ {{lc,Line,MungedExpr,MungedLC}, Vars3};
+munge_expr({block,Line,Body}, Vars) ->
+ {MungedBody, Vars2} = munge_body(Body, Vars, []),
+ {{block,Line,MungedBody}, Vars2};
+munge_expr({'if',Line,Clauses}, Vars) ->
+ {MungedClauses,Vars2} = munge_clauses(Clauses, Vars, []),
+ {{'if',Line,MungedClauses}, Vars2};
+munge_expr({'case',Line,Expr,Clauses}, Vars) ->
+ {MungedExpr,Vars2} = munge_expr(Expr,Vars),
+ {MungedClauses,Vars3} = munge_clauses(Clauses, Vars2, []),
+ {{'case',Line,MungedExpr,MungedClauses}, Vars3};
+munge_expr({'receive',Line,Clauses}, Vars) ->
+ {MungedClauses,Vars2} = munge_clauses(Clauses, Vars, []),
+ {{'receive',Line,MungedClauses}, Vars2};
+munge_expr({'receive',Line,Clauses,Expr,Body}, Vars) ->
+ {MungedClauses,Vars2} = munge_clauses(Clauses, Vars, []),
+ {MungedExpr, Vars3} = munge_expr(Expr, Vars2),
+ {MungedBody, Vars4} = munge_body(Body, Vars3, []),
+ {{'receive',Line,MungedClauses,MungedExpr,MungedBody}, Vars4};
+munge_expr({'try',Line,Exprs,Clauses,CatchClauses}, Vars) ->
+ {MungedExprs, Vars1} = munge_exprs(Exprs, Vars, []),
+ {MungedClauses, Vars2} = munge_clauses(Clauses, Vars1, []),
+ {MungedCatchClauses, Vars3} = munge_clauses(CatchClauses, Vars2, []),
+ {{'try',Line,MungedExprs,MungedClauses,MungedCatchClauses}, Vars3};
+%% Difference in abstract format after preprocessing: Funs get an extra
+%% element Extra.
+%% NOT NECESSARY FOR Vsn=raw_abstract_v1
+munge_expr({'fun',Line,{function,Name,Arity},_Extra}, Vars) ->
+ {{'fun',Line,{function,Name,Arity}}, Vars};
+munge_expr({'fun',Line,{clauses,Clauses},_Extra}, Vars) ->
+ {MungedClauses,Vars2}=munge_clauses(Clauses, Vars, []),
+ {{'fun',Line,{clauses,MungedClauses}}, Vars2};
+munge_expr({'fun',Line,{clauses,Clauses}}, Vars) ->
+ %% Only for Vsn=raw_abstract_v1
+ {MungedClauses,Vars2}=munge_clauses(Clauses, Vars, []),
+ {{'fun',Line,{clauses,MungedClauses}}, Vars2};
+munge_expr(Form, Vars) -> % var|char|integer|float|string|atom|nil|bin|eof
+ {Form, Vars}.
+
+munge_exprs([Expr|Exprs], Vars, MungedExprs) when Vars#vars.is_guard==true,
+ is_list(Expr) ->
+ {MungedExpr, _Vars} = munge_exprs(Expr, Vars, []),
+ munge_exprs(Exprs, Vars, [MungedExpr|MungedExprs]);
+munge_exprs([Expr|Exprs], Vars, MungedExprs) ->
+ {MungedExpr, Vars2} = munge_expr(Expr, Vars),
+ munge_exprs(Exprs, Vars2, [MungedExpr|MungedExprs]);
+munge_exprs([], Vars, MungedExprs) ->
+ {lists:reverse(MungedExprs), Vars}.
+
+munge_lc([{generate,Line,Pattern,Expr}|LC], Vars, MungedLC) ->
+ {MungedExpr, Vars2} = munge_expr(Expr, Vars),
+ munge_lc(LC, Vars2, [{generate,Line,Pattern,MungedExpr}|MungedLC]);
+munge_lc([Expr|LC], Vars, MungedLC) ->
+ {MungedExpr, Vars2} = munge_expr(Expr, Vars),
+ munge_lc(LC, Vars2, [MungedExpr|MungedLC]);
+munge_lc([], Vars, MungedLC) ->
+ {lists:reverse(MungedLC), Vars}.
+
+
+
+
+
+
+
+
+
+