Add init_pvar to trans_state and use it in c++ constructor

Summary:
public
Add optional field to trans_state that denotes variable that is being initialized.
This information will be used by certain constructs (such as c++ constructors or
list initialization).
Passing it in trans_state will enable us to deal with more complicated AST structures where
there might be multiple nodes between variable declaration and its initialization.

Reviewed By: jvillard

Differential Revision: D2854988

fb-gh-sync-id: c100380
master
Andrzej Kotulski 9 years ago committed by facebook-github-bot-7
parent f379383b04
commit 5eb7d9e994

@ -568,7 +568,8 @@ struct
leaf_nodes = leaf_nodes;
ids = res_trans_idx.ids @ res_trans_a.ids;
instrs = res_trans_a.instrs @ res_trans_idx.instrs;
exps = [(array_exp, typ)]}
exps = [(array_exp, typ)];
initd_exps = res_trans_idx.initd_exps @ res_trans_a.initd_exps; }
and binaryOperator_trans trans_state binary_operator_info stmt_info expr_info stmt_list =
let open Clang_ast_t in
@ -783,7 +784,7 @@ struct
cxx_method_construct_call_trans trans_state_pri result_trans_callee params_stmt
si function_type
and cxxConstructExpr_trans trans_state var_exp expr =
and cxxConstructExpr_trans trans_state expr =
match expr with
| Clang_ast_t.CXXConstructExpr (si, params_stmt, ei, cxx_constr_info) ->
let context = trans_state.context in
@ -791,7 +792,11 @@ struct
let decl_ref = cxx_constr_info.Clang_ast_t.xcei_decl_ref in
let class_type = CTypes_decl.get_type_from_expr_info ei context.CContext.tenv in
let this_type = Sil.Tptr (class_type, Sil.Pk_pointer) in
let this_res_trans = { empty_res_trans with exps = [(var_exp, this_type)]} in
let var_exp = match trans_state.var_exp with Some e -> e | None -> assert false in
let this_res_trans = { empty_res_trans with
exps = [(var_exp, this_type)];
initd_exps = [var_exp];
} in
let res_trans_callee = decl_ref_trans trans_state this_res_trans si ei decl_ref in
let params_stmt' = assign_default_params params_stmt expr in
cxx_method_construct_call_trans trans_state_pri res_trans_callee params_stmt' si
@ -979,7 +984,8 @@ struct
leaf_nodes = [join_node];
ids = [id];
instrs = instrs;
exps = [(Sil.Var id, typ)]
exps = [(Sil.Var id, typ)];
initd_exps = []; (* TODO we should get exps from branches+cond *)
}
| _ -> assert false)
@ -1012,7 +1018,13 @@ struct
let rnodes = if (IList.length res_trans_cond.root_nodes) = 0 then
[prune_t; prune_f]
else res_trans_cond.root_nodes in
{ root_nodes = rnodes; leaf_nodes =[prune_t; prune_f]; ids = res_trans_cond.ids; instrs = instrs'; exps = e' } in
{ root_nodes = rnodes;
leaf_nodes = [prune_t; prune_f];
ids = res_trans_cond.ids;
instrs = instrs';
exps = e';
initd_exps = [];
} in
(* This function translate (s1 binop s2) doing shortcircuit for '&&' and '||' *)
(* At the high level it does cond_trans s1; cond_trans s2; glue_nodes *)
@ -1043,7 +1055,9 @@ struct
leaf_nodes = prune_to_short_c@res_trans_s2.leaf_nodes;
ids = res_trans_s1.ids@res_trans_s2.ids;
instrs = res_trans_s1.instrs@res_trans_s2.instrs;
exps = [(e_cond, typ1)] } in
exps = [(e_cond, typ1)];
initd_exps = [];
} in
Printing.log_out "Translating Condition for Conditional/Loop \n";
let open Clang_ast_t in
match cond with
@ -1100,7 +1114,8 @@ struct
leaf_nodes = [join_node];
ids = res_trans_decl.ids @ res_trans_cond.ids @ ids_t @ ids_f;
instrs = [];
exps = []
exps = [];
initd_exps = [];
}
| _ -> assert false)
@ -1226,7 +1241,7 @@ struct
Cfg.Node.set_succs_exn switch_special_cond_node top_prune_nodes [];
let top_nodes = res_trans_decl.root_nodes in
IList.iter (fun n' -> Cfg.Node.append_instrs_temps n' [] res_trans_cond.ids) succ_nodes; (* succ_nodes will remove the temps *)
{ root_nodes = top_nodes; leaf_nodes = succ_nodes; ids = []; instrs = []; exps =[]}
{ empty_res_trans with root_nodes = top_nodes; leaf_nodes = succ_nodes }
| _ -> assert false
and stmtExpr_trans trans_state stmt_info stmt_list expr_info =
@ -1243,11 +1258,11 @@ struct
let id = Ident.create_fresh Ident.knormal in
let loc = CLocation.get_sil_location stmt_info context in
let instr' = Sil.Letderef (id, last, typ, loc) in
{ root_nodes = res_trans_stmt.root_nodes;
leaf_nodes = res_trans_stmt.leaf_nodes;
{ res_trans_stmt with
ids = id :: idl;
instrs = res_trans_stmt.instrs @ [instr'];
exps = [(Sil.Var id, typ)]}
exps = [(Sil.Var id, typ)];
}
| _ -> assert false
and loop_instruction trans_state loop_kind stmt_info =
@ -1435,6 +1450,7 @@ struct
ids = rh_ids;
instrs = instructions;
exps = [(var_exp, var_type)];
initd_exps = [var_exp];
}
) else {
root_nodes = [];
@ -1442,6 +1458,7 @@ struct
ids = rh_ids;
instrs = instructions;
exps = [(var_exp, var_type)];
initd_exps = [var_exp];
}
)
@ -1460,8 +1477,6 @@ struct
| Some (ImplicitCastExpr (_, [CompoundLiteralExpr (_, [InitListExpr (stmt_info , stmts , expr_info)], _)], _, _))
| Some (ExprWithCleanups (_, [InitListExpr (stmt_info , stmts , expr_info)], _, _)) ->
initListExpr_trans trans_state var_exp stmt_info expr_info stmts
| Some (CXXConstructExpr _ as expr) ->
cxxConstructExpr_trans trans_state var_exp expr
| Some ie -> (*For init expr, translate how to compute it and assign to the var*)
let stmt_info, _ = Clang_ast_proj.get_stmt_tuple ie in
let context = trans_state.context in
@ -1469,7 +1484,7 @@ struct
let trans_state_pri = PriorityNode.try_claim_priority_node trans_state var_stmt_info in
(* if ie is a block the translation need to be done with the block special cases by exec_with_block_priority*)
let res_trans_ie =
let trans_state' = { trans_state_pri with succ_nodes = [] } in
let trans_state' = { trans_state_pri with succ_nodes = []; var_exp = Some var_exp } in
let instruction' =
exec_with_self_exception (exec_with_lvalue_as_reference instruction) in
exec_with_block_priority_exception instruction' trans_state' ie var_stmt_info in
@ -1477,7 +1492,9 @@ struct
"WARNING: In DeclStmt we expect only one expression returned in recursive call\n" in
let rhs_owning_method = CTrans_utils.is_owning_method ie in
let _, instrs_assign, ids_assign =
if !Config.arc_mode &&
(* variable might be initialized already - do nothing in that case*)
if IList.exists (Sil.exp_equal var_exp) res_trans_ie.initd_exps then ([], [], [])
else if !Config.arc_mode &&
(CTrans_utils.is_method_call ie || ObjcInterface_decl.is_pointer_to_objc_class context.CContext.tenv ie_typ) then
(* In arc mode, if it's a method call or we are initializing with a pointer to objc class *)
(* we need to add retain/release *)
@ -1515,7 +1532,8 @@ struct
{ root_nodes = res_trans_tmp.root_nodes; leaf_nodes = [];
ids = res_trans_tmp.ids @ res_trans_vd.ids;
instrs = res_trans_tmp.instrs @ res_trans_vd.instrs;
exps = res_trans_tmp.exps @ res_trans_vd.exps }
exps = res_trans_tmp.exps @ res_trans_vd.exps;
initd_exps = res_trans_tmp.initd_exps @ res_trans_vd.initd_exps; }
| CXXRecordDecl _ :: var_decls' (*C++/C record decl treated in the same way *)
| RecordDecl _ :: var_decls' ->
(* Record declaration is done in the beginning when procdesc is defined.*)
@ -1595,11 +1613,11 @@ struct
let cast_kind = cast_expr_info.Clang_ast_t.cei_cast_kind in
(* This gives the differnece among cast operations kind*)
let cast_ids, cast_inst, cast_exp = cast_operation context cast_kind res_trans_stmt.exps typ sil_loc is_objc_bridged in
{ root_nodes = res_trans_stmt.root_nodes;
leaf_nodes = res_trans_stmt.leaf_nodes;
{ res_trans_stmt with
ids = res_trans_stmt.ids @ cast_ids;
instrs = res_trans_stmt.instrs @ cast_inst;
exps = [cast_exp] }
exps = [cast_exp];
}
(* function used in the computation for both Member_Expr and ObjCIVarRefExpr *)
and do_memb_ivar_ref_exp trans_state expr_info stmt_info stmt_list decl_ref =
@ -1867,6 +1885,9 @@ struct
| CXXOperatorCallExpr(stmt_info, stmt_list, ei) ->
callExpr_trans trans_state stmt_info stmt_list ei
| CXXConstructExpr _ ->
cxxConstructExpr_trans trans_state instr
| ObjCMessageExpr(stmt_info, stmt_list, expr_info, obj_c_message_expr_info) ->
if is_block_enumerate_function obj_c_message_expr_info then
block_enumeration_trans trans_state stmt_info stmt_list expr_info
@ -2069,7 +2090,8 @@ struct
leaf_nodes = [];
ids = res_trans_s.ids @ res_trans_tail.ids;
instrs = res_trans_tail.instrs @ res_trans_s.instrs;
exps = res_trans_tail.exps @ res_trans_s.exps }
exps = res_trans_tail.exps @ res_trans_s.exps;
initd_exps = res_trans_tail.initd_exps @ res_trans_s.initd_exps; }
and get_clang_stmt_trans stmt_list =
let instruction' = fun stmt -> fun trans_state -> instruction trans_state stmt in
@ -2093,6 +2115,7 @@ struct
succ_nodes = [];
continuation = None;
priority = Free;
var_exp = None;
} in
let res_trans_stmt = instruction trans_state stmt in
fst (CTrans_utils.extract_exp_from_list res_trans_stmt.exps warning)
@ -2103,6 +2126,7 @@ struct
succ_nodes = [exit_node];
continuation = None;
priority = Free;
var_exp = None;
} in
let clang_ast_trans = get_clang_stmt_trans clang_stmt_list in
let extra_stmt_trans = get_custom_stmt_trans extra_instrs in

@ -136,6 +136,7 @@ type trans_state = {
succ_nodes: Cfg.Node.t list; (* successor nodes in the cfg *)
continuation: continuation option; (* current continuation *)
priority: priority_node;
var_exp: Sil.exp option;
}
(* A translation result. It is returned by the translation function. *)
@ -145,10 +146,18 @@ type trans_result = {
ids: Ident.t list; (* list of temp identifiers created that need to be removed by the caller *)
instrs: Sil.instr list; (* list of SIL instruction that need to be placed in cfg nodes of the parent*)
exps: (Sil.exp * Sil.typ) list; (* SIL expressions resulting from the translation of the clang stmt *)
initd_exps: Sil.exp list;
}
(* Empty result translation *)
let empty_res_trans = { root_nodes =[]; leaf_nodes =[]; ids =[]; instrs =[]; exps =[]}
let empty_res_trans = {
root_nodes = [];
leaf_nodes = [];
ids = [];
instrs = [];
exps = [];
initd_exps = [];
}
(** Collect the results of translating a list of instructions, and link up the nodes created. *)
let collect_res_trans l =
@ -169,7 +178,8 @@ let collect_res_trans l =
leaf_nodes = leaf_nodes;
ids = rt.ids@rt'.ids;
instrs = rt.instrs@rt'.instrs;
exps = rt.exps@rt'.exps } in
exps = rt.exps@rt'.exps;
initd_exps = rt.initd_exps@rt'.initd_exps; } in
collect l empty_res_trans
(* priority_node is used to enforce some kind of policy for creating nodes *)
@ -232,11 +242,13 @@ struct
(* Invariant: if root_nodes is empty then the params have not created a node.*)
let root_nodes = (if res_state.root_nodes <> [] then res_state.root_nodes
else [node]) in
{ root_nodes = root_nodes;
{ res_state with
root_nodes = root_nodes;
leaf_nodes = [node];
ids = ids_parent;
instrs = [];
exps = []}
exps = [];
}
else
(* The node is created by the parent. We just pass back nodes/leafs params *)
{ res_state with exps = []}
@ -421,21 +433,13 @@ let trans_assertion_failure sil_loc context =
and failure_node =
Nodes.create_node (Cfg.Node.Stmt_node "Assertion failure") [] [call_instr] sil_loc context in
Cfg.Node.set_succs_exn failure_node [exit_node] [];
{ root_nodes = [failure_node];
leaf_nodes = [];
ids = [];
instrs =[];
exps = [] }
{ empty_res_trans with root_nodes = [failure_node]; }
let trans_assume_false sil_loc context succ_nodes =
let instrs_cond = [Sil.Prune (Sil.exp_zero, sil_loc, true, Sil.Ik_land_lor)] in
let prune_node = Nodes.create_node (Nodes.prune_kind true) [] instrs_cond sil_loc context in
Cfg.Node.set_succs_exn prune_node succ_nodes [];
{ root_nodes = [prune_node];
leaf_nodes = [prune_node];
ids = [];
instrs = [];
exps = [] }
{ empty_res_trans with root_nodes = [prune_node]; leaf_nodes = [prune_node] }
let define_condition_side_effects context e_cond instrs_cond sil_loc =
let (e', typ) = extract_exp_from_list e_cond "\nWARNING: Missing expression in IfStmt. Need to be fixed\n" in

@ -24,6 +24,7 @@ type trans_state = {
succ_nodes: Cfg.Node.t list;
continuation: continuation option;
priority: priority_node;
var_exp: Sil.exp option;
}
type trans_result = {
@ -32,6 +33,7 @@ type trans_result = {
ids: Ident.t list;
instrs: Sil.instr list;
exps: (Sil.exp * Sil.typ) list;
initd_exps: Sil.exp list;
}
val empty_res_trans: trans_result

@ -1,13 +1,13 @@
digraph iCFG {
11 [label="11: Call _fun_X_X \n _fun_X_X(&x1:class X *,0:int ,0:int ) [line 22]\n " shape="box"]
11 [label="11: DeclStmt \n _fun_X_X(&x1:class X *,0:int ,0:int ) [line 22]\n " shape="box"]
11 -> 10 ;
10 [label="10: Call _fun_X_X \n _fun_X_X(&x2:class X *,1:int ,0:int ) [line 23]\n " shape="box"]
10 [label="10: DeclStmt \n _fun_X_X(&x2:class X *,1:int ,0:int ) [line 23]\n " shape="box"]
10 -> 9 ;
9 [label="9: Call _fun_X_X \n _fun_X_X(&x3:class X *,0:int ,1:int ) [line 24]\n NULLIFY(&x1,false); [line 24]\n NULLIFY(&x2,false); [line 24]\n NULLIFY(&x3,false); [line 24]\n APPLY_ABSTRACTION; [line 24]\n " shape="box"]
9 [label="9: DeclStmt \n _fun_X_X(&x3:class X *,0:int ,1:int ) [line 24]\n NULLIFY(&x1,false); [line 24]\n NULLIFY(&x2,false); [line 24]\n NULLIFY(&x3,false); [line 24]\n APPLY_ABSTRACTION; [line 24]\n " shape="box"]
9 -> 8 ;

@ -1,5 +1,5 @@
digraph iCFG {
26 [label="26: Call _fun_X_X \n _fun_X_X(&x:class X *,0:int ,1:int ) [line 40]\n " shape="box"]
26 [label="26: DeclStmt \n _fun_X_X(&x:class X *,0:int ,1:int ) [line 40]\n " shape="box"]
26 -> 25 ;
@ -14,7 +14,7 @@ digraph iCFG {
23 -> 26 ;
22 [label="22: Call _fun_X_X \n _fun_X_X(&x:class X *) [line 35]\n " shape="box"]
22 [label="22: DeclStmt \n _fun_X_X(&x:class X *) [line 35]\n " shape="box"]
22 -> 21 ;
@ -29,7 +29,7 @@ digraph iCFG {
19 -> 22 ;
18 [label="18: Call _fun_X_X \n _fun_X_X(&x:class X *,-2:int ,2:int ) [line 30]\n " shape="box"]
18 [label="18: DeclStmt \n _fun_X_X(&x:class X *,-2:int ,2:int ) [line 30]\n " shape="box"]
18 -> 17 ;

@ -1,9 +1,9 @@
digraph iCFG {
24 [label="24: Call _fun_foo::my_record_ \n _fun_foo::my_record_(&x:struct foo::my_record *) [line 45]\n " shape="box"]
24 [label="24: DeclStmt \n _fun_foo::my_record_(&x:struct foo::my_record *) [line 45]\n " shape="box"]
24 -> 23 ;
23 [label="23: Call _fun_bar::Rectangle_Rectangle \n _fun_bar::Rectangle_Rectangle(&rect1:class bar::Rectangle *) [line 47]\n " shape="box"]
23 [label="23: DeclStmt \n _fun_bar::Rectangle_Rectangle(&rect1:class bar::Rectangle *) [line 47]\n " shape="box"]
23 -> 22 ;
@ -11,7 +11,7 @@ digraph iCFG {
22 -> 21 ;
21 [label="21: Call _fun_foo::Rectangle_Rectangle \n _fun_foo::Rectangle_Rectangle(&rect2:class foo::Rectangle *) [line 50]\n " shape="box"]
21 [label="21: DeclStmt \n _fun_foo::Rectangle_Rectangle(&rect2:class foo::Rectangle *) [line 50]\n " shape="box"]
21 -> 20 ;

@ -21,11 +21,11 @@ digraph iCFG {
44 -> 46 ;
43 [label="43: Call _fun_X1_X1 \n _fun_X1_X1(&x1:class X1 *) [line 62]\n " shape="box"]
43 [label="43: DeclStmt \n _fun_X1_X1(&x1:class X1 *) [line 62]\n " shape="box"]
43 -> 42 ;
42 [label="42: Call _fun_X3_X3 \n _fun_X3_X3(&x3:class X3 *) [line 63]\n " shape="box"]
42 [label="42: DeclStmt \n _fun_X3_X3(&x3:class X3 *) [line 63]\n " shape="box"]
42 -> 41 ;
@ -40,11 +40,11 @@ digraph iCFG {
39 -> 43 ;
38 [label="38: Call _fun_X1_X1 \n _fun_X1_X1(&x1:class X1 *) [line 56]\n " shape="box"]
38 [label="38: DeclStmt \n _fun_X1_X1(&x1:class X1 *) [line 56]\n " shape="box"]
38 -> 37 ;
37 [label="37: Call _fun_X3_X3 \n _fun_X3_X3(&x3:class X3 *) [line 57]\n " shape="box"]
37 [label="37: DeclStmt \n _fun_X3_X3(&x3:class X3 *) [line 57]\n " shape="box"]
37 -> 36 ;
@ -81,7 +81,7 @@ digraph iCFG {
28 -> 30 ;
27 [label="27: Call _fun_X1_X1 \n _fun_X1_X1(&x:class X1 *) [line 35]\n " shape="box"]
27 [label="27: DeclStmt \n _fun_X1_X1(&x:class X1 *) [line 35]\n " shape="box"]
27 -> 26 ;
@ -96,7 +96,7 @@ digraph iCFG {
24 -> 27 ;
23 [label="23: Call _fun_X3_X3 \n _fun_X3_X3(&x:class X3 *) [line 35]\n " shape="box"]
23 [label="23: DeclStmt \n _fun_X3_X3(&x:class X3 *) [line 35]\n " shape="box"]
23 -> 22 ;

@ -1,9 +1,9 @@
digraph iCFG {
73 [label="73: Call _fun_X1_X1 \n _fun_X1_X1(&x1:class X1 *) [line 66]\n " shape="box"]
73 [label="73: DeclStmt \n _fun_X1_X1(&x1:class X1 *) [line 66]\n " shape="box"]
73 -> 72 ;
72 [label="72: Call _fun_GetterTempl<X1>_GetterTempl \n _fun_GetterTempl<X1>_GetterTempl(&g:class GetterTempl<X1> *) [line 67]\n " shape="box"]
72 [label="72: DeclStmt \n _fun_GetterTempl<X1>_GetterTempl(&g:class GetterTempl<X1> *) [line 67]\n " shape="box"]
72 -> 71 ;
@ -18,15 +18,15 @@ digraph iCFG {
69 -> 73 ;
68 [label="68: Call _fun_X1_X1 \n _fun_X1_X1(&x1:class X1 *) [line 59]\n " shape="box"]
68 [label="68: DeclStmt \n _fun_X1_X1(&x1:class X1 *) [line 59]\n " shape="box"]
68 -> 67 ;
67 [label="67: Call _fun_X2_X2 \n _fun_X2_X2(&x2:class X2 *) [line 60]\n " shape="box"]
67 [label="67: DeclStmt \n _fun_X2_X2(&x2:class X2 *) [line 60]\n " shape="box"]
67 -> 66 ;
66 [label="66: Call _fun_GetterTempl<X2>_GetterTempl \n _fun_GetterTempl<X2>_GetterTempl(&g:class GetterTempl<X2> *) [line 61]\n " shape="box"]
66 [label="66: DeclStmt \n _fun_GetterTempl<X2>_GetterTempl(&g:class GetterTempl<X2> *) [line 61]\n " shape="box"]
66 -> 65 ;
@ -41,11 +41,11 @@ digraph iCFG {
63 -> 68 ;
62 [label="62: Call _fun_X2_X2 \n _fun_X2_X2(&x2:class X2 *) [line 53]\n " shape="box"]
62 [label="62: DeclStmt \n _fun_X2_X2(&x2:class X2 *) [line 53]\n " shape="box"]
62 -> 61 ;
61 [label="61: Call _fun_GetterTempl<X2>_GetterTempl \n _fun_GetterTempl<X2>_GetterTempl(&g:class GetterTempl<X2> *) [line 54]\n " shape="box"]
61 [label="61: DeclStmt \n _fun_GetterTempl<X2>_GetterTempl(&g:class GetterTempl<X2> *) [line 54]\n " shape="box"]
61 -> 60 ;
@ -60,15 +60,15 @@ digraph iCFG {
58 -> 62 ;
57 [label="57: Call _fun_X2_X2 \n _fun_X2_X2(&x2:class X2 *) [line 46]\n " shape="box"]
57 [label="57: DeclStmt \n _fun_X2_X2(&x2:class X2 *) [line 46]\n " shape="box"]
57 -> 56 ;
56 [label="56: Call _fun_X3_X3 \n _fun_X3_X3(&x3:class X3 *) [line 47]\n " shape="box"]
56 [label="56: DeclStmt \n _fun_X3_X3(&x3:class X3 *) [line 47]\n " shape="box"]
56 -> 55 ;
55 [label="55: Call _fun_GetterTempl<X3>_GetterTempl \n _fun_GetterTempl<X3>_GetterTempl(&g:class GetterTempl<X3> *) [line 48]\n " shape="box"]
55 [label="55: DeclStmt \n _fun_GetterTempl<X3>_GetterTempl(&g:class GetterTempl<X3> *) [line 48]\n " shape="box"]
55 -> 54 ;
@ -83,11 +83,11 @@ digraph iCFG {
52 -> 57 ;
51 [label="51: Call _fun_X1_X1 \n _fun_X1_X1(&x1:class X1 *) [line 40]\n " shape="box"]
51 [label="51: DeclStmt \n _fun_X1_X1(&x1:class X1 *) [line 40]\n " shape="box"]
51 -> 50 ;
50 [label="50: Call _fun_Getter_Getter \n _fun_Getter_Getter(&g:class Getter *) [line 41]\n " shape="box"]
50 [label="50: DeclStmt \n _fun_Getter_Getter(&g:class Getter *) [line 41]\n " shape="box"]
50 -> 49 ;
@ -102,11 +102,11 @@ digraph iCFG {
47 -> 51 ;
46 [label="46: Call _fun_X2_X2 \n _fun_X2_X2(&x2:class X2 *) [line 34]\n " shape="box"]
46 [label="46: DeclStmt \n _fun_X2_X2(&x2:class X2 *) [line 34]\n " shape="box"]
46 -> 45 ;
45 [label="45: Call _fun_Getter_Getter \n _fun_Getter_Getter(&g:class Getter *) [line 35]\n " shape="box"]
45 [label="45: DeclStmt \n _fun_Getter_Getter(&g:class Getter *) [line 35]\n " shape="box"]
45 -> 44 ;

Loading…
Cancel
Save