Commit | Line | Data |
---|---|---|
7329404e BB |
1 | /****************************************************************************** |
2 | * Copyright (c) 2000-2016 Ericsson Telecom AB | |
3 | * All rights reserved. This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License v1.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * http://www.eclipse.org/legal/epl-v10.html | |
7 | * | |
8 | * Contributors: | |
9 | * | |
10 | * Baranyi, Botond – initial implementation | |
11 | * | |
12 | ******************************************************************************/ | |
13 | ||
14 | #include "DebuggerStuff.hh" | |
15 | #include "AST.hh" | |
16 | #include "Type.hh" | |
17 | #include "ttcn3/AST_ttcn3.hh" | |
18 | #include "ttcn3/ArrayDimensions.hh" | |
19 | ||
20 | namespace Common { | |
21 | ||
22 | /** Returns a string, that contains the template parameters of the debugger's | |
23 | * port array or timer array printing function. | |
24 | * | |
25 | * Recursive (handles one array dimension per call). | |
26 | * | |
27 | * @param p_dims the array's dimensions | |
28 | * @param p_element_type name of the array's element C++ class | |
29 | * @param p_array_type name of the array's C++ class ("PORT_ARRAY" or "TIMER_ARRAY") | |
30 | * @param index the index of the currently handled dimension in \a p_dims | |
31 | */ | |
32 | string function_params_for_array_dims(Ttcn::ArrayDimensions* p_dims, | |
33 | string p_element_type, | |
34 | string p_array_type, | |
35 | size_t index = 0) | |
36 | { | |
37 | if (index == p_dims->get_nof_dims()) { | |
38 | return p_element_type; | |
39 | } | |
40 | string ret_val; | |
41 | if (index != 0) { | |
42 | ret_val = p_array_type + string("<"); | |
43 | } | |
44 | ret_val += function_params_for_array_dims(p_dims, p_element_type, p_array_type, index + 1); | |
45 | Ttcn::ArrayDimension* dim = p_dims->get_dim_byIndex(index); | |
46 | ret_val += string(", ") + Int2string(dim->get_size()) + | |
47 | string(", ") + Int2string(dim->get_offset()); | |
48 | if (index != 0) { | |
49 | ret_val += string(">"); | |
50 | } | |
51 | return ret_val; | |
52 | } | |
53 | ||
54 | /** Returns a string, that contains the template parameters of the debugger's | |
55 | * value array or template array printing function. | |
56 | * | |
57 | * Recursive (handles one array dimension per call). | |
58 | * | |
59 | * @param p_type the current type (either an array or the array element) | |
60 | * @param p_scope the current scope | |
61 | * @param p_templ indicates whether it's a template array or value array | |
62 | * @param p_first indicates whether this is the first call or a recursive call | |
63 | */ | |
64 | string function_params_for_array_type(Type* p_type, | |
65 | Scope* p_scope, | |
66 | bool p_templ, | |
67 | bool p_first = true) | |
68 | { | |
69 | string ret_val; | |
70 | if (p_type->get_typetype() != Type::T_ARRAY) { | |
71 | ret_val = p_type->get_genname_value(p_scope); | |
72 | if (p_templ) { | |
73 | ret_val += "_template"; | |
74 | } | |
75 | } | |
76 | else { | |
77 | if (!p_first) { | |
78 | if (p_templ) { | |
79 | ret_val = "TEMPLATE_ARRAY<"; | |
80 | } | |
81 | else { | |
82 | ret_val = "VALUE_ARRAY<"; | |
83 | } | |
84 | } | |
85 | Type* elem_type = p_type->get_ofType()->get_type_refd_last(); | |
86 | if (p_templ) { | |
87 | ret_val += function_params_for_array_type(elem_type, p_scope, false, false) + | |
88 | ", " + function_params_for_array_type(elem_type, p_scope, true, false); | |
89 | } | |
90 | else { | |
91 | ret_val += function_params_for_array_type(elem_type, p_scope, false, false); | |
92 | } | |
93 | Ttcn::ArrayDimension* dim = p_type->get_dimension(); | |
94 | ret_val += string(", ") + Int2string(dim->get_size()) + | |
95 | string(", ") + Int2string(dim->get_offset()); | |
96 | if (!p_first) { | |
97 | ret_val += string(">"); | |
98 | } | |
99 | } | |
100 | return ret_val; | |
101 | } | |
102 | ||
103 | /** Appends the string representations of the specified array dimensions. */ | |
104 | string array_dimensions_to_string(Ttcn::ArrayDimensions* p_dims) | |
105 | { | |
106 | string ret_val; | |
107 | for (size_t i = 0; i < p_dims->get_nof_dims(); ++i) { | |
108 | ret_val += p_dims->get_dim_byIndex(i)->get_stringRepr(); | |
109 | } | |
110 | return ret_val; | |
111 | } | |
112 | ||
f08ff9ca BB |
113 | void calculate_type_name_and_debug_functions_from_type(Type* p_type, |
114 | Type* p_type_last, | |
115 | Module* p_module, | |
116 | string& p_type_name, | |
117 | string& p_print_function, | |
118 | string& p_set_function) | |
7329404e BB |
119 | { |
120 | if (p_type_last->get_typetype() == Type::T_COMPONENT) { | |
121 | p_type_name = "component"; | |
122 | } | |
123 | else if (p_type_last->is_structured_type() || | |
124 | p_type_last->get_typetype() == Type::T_ENUM_A || | |
125 | p_type_last->get_typetype() == Type::T_ENUM_T || | |
7329404e BB |
126 | p_type_last->get_typetype() == Type::T_SIGNATURE || |
127 | p_type_last->get_typetype() == Type::T_FUNCTION || | |
128 | p_type_last->get_typetype() == Type::T_ALTSTEP || | |
129 | p_type_last->get_typetype() == Type::T_TESTCASE) { | |
130 | // user-defined type | |
131 | if (p_type_last->is_pard_type_instance()) { | |
132 | // if the referenced type is an instance of an ASN.1 parameterized type, | |
133 | // then use the last non-parameterized type in the reference chain to | |
134 | // calculate the type name | |
135 | Type* t = p_type; | |
136 | while (t->is_ref() && !t->is_pard_type_instance()) { | |
137 | p_type_name = t->get_dispname(); | |
138 | t = t->get_type_refd(); | |
139 | } | |
140 | } | |
141 | else { | |
142 | p_type_name = p_type_last->get_dispname(); | |
143 | } | |
144 | const Module* var_type_mod = p_type_last->get_my_scope()->get_scope_mod(); | |
f08ff9ca | 145 | string module_prefix; |
7329404e | 146 | if (var_type_mod != p_module) { |
f08ff9ca | 147 | module_prefix = var_type_mod->get_modid().get_name() + "::"; |
7329404e | 148 | } |
f08ff9ca BB |
149 | p_print_function = module_prefix + "print_var_" + |
150 | var_type_mod->get_modid().get_ttcnname(); | |
151 | if (p_type_last->get_typetype() != Type::T_SIGNATURE && | |
152 | p_type_last->get_typetype() != Type::T_PORT) { | |
153 | p_set_function = module_prefix + "set_var_" + | |
154 | var_type_mod->get_modid().get_ttcnname(); | |
7329404e | 155 | } |
7329404e BB |
156 | } |
157 | else { | |
158 | // built-in type, get the TTCN-3 version of the type if possible | |
159 | switch (p_type_last->get_typetype()) { | |
160 | case Type::T_GENERALSTRING: | |
161 | case Type::T_GRAPHICSTRING: | |
162 | case Type::T_TELETEXSTRING: | |
163 | case Type::T_VIDEOTEXSTRING: | |
164 | // these ASN.1 string types are not converted right by Type::get_typetype_ttcn3() | |
165 | p_type_name = "universal charstring"; | |
166 | break; | |
788b5893 BB |
167 | case Type::T_PORT: |
168 | p_type_name = "port"; | |
169 | break; | |
7329404e BB |
170 | case Type::T_UNRESTRICTEDSTRING: |
171 | case Type::T_EMBEDDED_PDV: | |
172 | case Type::T_EXTERNAL: | |
173 | // these are converted to T_SEQ_T by Type::get_typetype_ttcn3() | |
174 | p_type_name = Type::get_typename_builtin(p_type_last->get_typetype()); | |
175 | break; | |
176 | default: | |
177 | p_type_name = Type::get_typename_builtin(p_type_last->get_typetype_ttcn3()); | |
178 | break; | |
179 | } | |
180 | } | |
181 | } | |
182 | ||
183 | char* generate_code_debugger_add_var(char* str, Common::Assignment* var_ass, | |
184 | Module* current_mod /* = NULL */, | |
185 | const char* scope_name /* = NULL */) | |
186 | { | |
187 | if (current_mod == NULL) { | |
188 | current_mod = var_ass->get_my_scope()->get_scope_mod(); | |
189 | } | |
190 | ||
191 | bool is_lazy_param = false; | |
f08ff9ca | 192 | bool is_constant = false; |
7329404e BB |
193 | switch (var_ass->get_asstype()) { |
194 | case Common::Assignment::A_PAR_VAL: | |
195 | case Common::Assignment::A_PAR_VAL_IN: | |
f08ff9ca | 196 | case Common::Assignment::A_PAR_TEMPL_IN: { |
7329404e BB |
197 | if (var_ass->get_lazy_eval()) { |
198 | // lazy parameters have their own printing function | |
199 | is_lazy_param = true; | |
200 | } | |
f08ff9ca BB |
201 | Ttcn::FormalPar* fpar = dynamic_cast<Ttcn::FormalPar*>(var_ass); |
202 | is_constant = fpar == NULL || !fpar->get_used_as_lvalue(); | |
203 | break; } | |
204 | case Common::Assignment::A_CONST: | |
205 | case Common::Assignment::A_EXT_CONST: | |
206 | case Common::Assignment::A_MODULEPAR: | |
207 | case Common::Assignment::A_MODULEPAR_TEMP: | |
208 | case Common::Assignment::A_TEMPLATE: | |
f57971fe | 209 | is_constant = true; //scope_name != NULL; |
7329404e BB |
210 | default: |
211 | break; | |
212 | } | |
213 | ||
f08ff9ca BB |
214 | // recreate the TTCN-3 version of the type name and determine the type's |
215 | // printing and overwriting functions | |
216 | string type_name, print_function, set_function; | |
7329404e BB |
217 | print_function = is_lazy_param ? "TTCN3_Debugger::print_lazy_param<" : |
218 | "TTCN3_Debugger::print_base_var"; | |
f08ff9ca | 219 | set_function = "TTCN3_Debugger::set_base_var"; |
7329404e BB |
220 | if (var_ass->get_asstype() == Common::Assignment::A_TIMER || |
221 | var_ass->get_asstype() == Common::Assignment::A_PAR_TIMER) { | |
222 | type_name = "timer"; | |
223 | if (var_ass->get_Dimensions() != NULL) { | |
224 | // timer array | |
225 | type_name += array_dimensions_to_string(var_ass->get_Dimensions()); | |
226 | print_function = string("TTCN3_Debugger::print_timer_array<") + | |
227 | function_params_for_array_dims(var_ass->get_Dimensions(), | |
228 | string("TIMER"), string("TIMER_ARRAY")) + | |
229 | string(">"); | |
230 | } | |
231 | } | |
232 | else { | |
233 | Common::Type* var_type = var_ass->get_Type(); | |
234 | // get the type at the end of the reference chain, but don't go through | |
235 | // CHARACTER STRINGs, EMBEDDED PDVs and EXTERNALs | |
236 | while (var_type->is_ref() && var_type->get_typetype() != Type::T_EXTERNAL && | |
237 | var_type->get_typetype() != Type::T_EMBEDDED_PDV && | |
238 | var_type->get_typetype() != Type::T_UNRESTRICTEDSTRING) { | |
239 | var_type = var_type->get_type_refd(); | |
240 | } | |
241 | if (is_lazy_param) { | |
242 | print_function += var_type->get_genname_value(current_mod); | |
243 | } | |
244 | if (var_type->get_typetype() == Type::T_PORT && var_ass->get_Dimensions() != NULL) { | |
245 | // port array | |
246 | type_name = var_type->get_dispname() + | |
247 | array_dimensions_to_string(var_ass->get_Dimensions()); | |
248 | if (!is_lazy_param) { | |
249 | print_function = string("TTCN3_Debugger::print_port_array<") + | |
250 | function_params_for_array_dims(var_ass->get_Dimensions(), | |
251 | var_type->get_genname_value(current_mod), | |
252 | string("PORT_ARRAY")) + | |
253 | string(">"); | |
254 | } | |
255 | } | |
256 | else if (var_type->get_typetype() == Type::T_ARRAY) { | |
257 | string dims_str; | |
258 | Type* t = var_type; | |
259 | while (t->get_typetype() == Type::T_ARRAY) { | |
260 | dims_str += t->get_dimension()->get_stringRepr(); | |
261 | t = t->get_ofType()->get_type_refd_last(); | |
262 | } | |
f08ff9ca BB |
263 | string dummy1, dummy2; |
264 | calculate_type_name_and_debug_functions_from_type(t, t, current_mod, | |
265 | type_name, dummy1, dummy2); | |
7329404e BB |
266 | type_name += dims_str; |
267 | if (!is_lazy_param) { | |
268 | switch (var_ass->get_asstype()) { | |
269 | case Common::Assignment::A_MODULEPAR_TEMP: | |
270 | case Common::Assignment::A_TEMPLATE: | |
271 | case Common::Assignment::A_VAR_TEMPLATE: | |
272 | case Common::Assignment::A_PAR_TEMPL_IN: | |
273 | case Common::Assignment::A_PAR_TEMPL_OUT: | |
274 | case Common::Assignment::A_PAR_TEMPL_INOUT: | |
275 | // template array | |
276 | print_function = string("TTCN3_Debugger::print_template_array<") + | |
277 | function_params_for_array_type(var_type, current_mod, true) + | |
278 | string(">"); | |
f08ff9ca BB |
279 | set_function = string("TTCN3_Debugger::set_template_array<") + |
280 | function_params_for_array_type(var_type, current_mod, true) + | |
281 | string(">"); | |
7329404e BB |
282 | break; |
283 | default: | |
284 | // value array | |
285 | print_function = string("TTCN3_Debugger::print_value_array<") + | |
286 | function_params_for_array_type(var_type, current_mod, false) + | |
287 | string(">"); | |
f08ff9ca BB |
288 | set_function = string("TTCN3_Debugger::set_value_array<") + |
289 | function_params_for_array_type(var_type, current_mod, false) + | |
290 | string(">"); | |
7329404e BB |
291 | break; |
292 | } | |
293 | } | |
294 | } | |
295 | else { | |
296 | string dummy; | |
f08ff9ca BB |
297 | calculate_type_name_and_debug_functions_from_type(var_ass->get_Type(), |
298 | var_type, current_mod, type_name, is_lazy_param ? dummy : print_function, | |
299 | set_function); | |
7329404e BB |
300 | } |
301 | } | |
302 | ||
303 | switch (var_ass->get_asstype()) { | |
304 | case Common::Assignment::A_MODULEPAR_TEMP: | |
305 | case Common::Assignment::A_TEMPLATE: | |
306 | case Common::Assignment::A_VAR_TEMPLATE: | |
307 | case Common::Assignment::A_PAR_TEMPL_IN: | |
308 | case Common::Assignment::A_PAR_TEMPL_OUT: | |
309 | case Common::Assignment::A_PAR_TEMPL_INOUT: | |
310 | // add a suffix, if it's a template | |
311 | type_name += " template"; | |
312 | if (is_lazy_param) { | |
313 | print_function += "_template"; | |
314 | } | |
315 | break; | |
316 | default: | |
317 | break; | |
318 | } | |
319 | ||
320 | if (is_lazy_param) { | |
321 | print_function += ">"; | |
322 | } | |
323 | ||
1cb5b229 BB |
324 | string module_str; |
325 | if (scope_name != NULL && !strcmp(scope_name, "global")) { | |
326 | // only store the module name for global variables | |
327 | module_str = string("\"") + | |
328 | var_ass->get_my_scope()->get_scope_mod()->get_modid().get_ttcnname() + string("\""); | |
329 | } | |
330 | else { | |
331 | module_str = "NULL"; | |
332 | } | |
333 | ||
334 | return mputprintf(str, "%s%s_scope%sadd_variable(&%s, \"%s\", \"%s\", %s, %s%s%s);\n", | |
7329404e BB |
335 | scope_name != NULL ? " " : "", // add indenting for global variables |
336 | scope_name != NULL ? scope_name : "debug", // the prefix of the debugger scope: | |
337 | // ("global" for global variables, "debug" for local variables, | |
338 | // or the component name for component variables) | |
339 | scope_name != NULL ? "->" : ".", // global scopes are pointers, local scopes | |
340 | // are local variables | |
341 | var_ass->get_genname_from_scope(current_mod, "").c_str(), // variable name in C++ | |
342 | // (HACK: an empty string is passed as the prefix parameter to get_genname_from_scope, | |
343 | // so the lazy parameter evaluation code is not generated) | |
344 | var_ass->get_id().get_ttcnname().c_str(), // variable name in TTCN-3 | |
345 | type_name.c_str(), // variable type in TTCN-3, with a suffix if it's a template | |
1cb5b229 | 346 | module_str.c_str(), // module name, where the variable was defined |
f08ff9ca BB |
347 | print_function.c_str(), // variable printing function |
348 | is_constant ? "" : ", ", is_constant ? "" : set_function.c_str()); | |
349 | // variable overwriting function (not generated for constants) | |
7329404e BB |
350 | } |
351 | ||
352 | char* generate_code_debugger_function_init(char* str, Common::Assignment* func_ass) | |
353 | { | |
354 | string comp_str = func_ass->get_RunsOnType() == NULL ? string("NULL") : | |
355 | string("\"") + func_ass->get_RunsOnType()->get_dispname() + string("\""); | |
356 | const char* func_type_str = NULL; | |
357 | switch (func_ass->get_asstype()) { | |
358 | case Common::Assignment::A_FUNCTION: | |
359 | case Common::Assignment::A_FUNCTION_RVAL: | |
360 | case Common::Assignment::A_FUNCTION_RTEMP: | |
361 | func_type_str = "function"; | |
362 | break; | |
363 | case Common::Assignment::A_EXT_FUNCTION: | |
364 | case Common::Assignment::A_EXT_FUNCTION_RVAL: | |
365 | case Common::Assignment::A_EXT_FUNCTION_RTEMP: | |
366 | func_type_str = "external function"; | |
367 | break; | |
368 | case Common::Assignment::A_TESTCASE: | |
369 | func_type_str = "testcase"; | |
370 | break; | |
371 | case Common::Assignment::A_ALTSTEP: | |
372 | func_type_str = "altstep"; | |
373 | break; | |
374 | case Common::Assignment::A_TEMPLATE: // parameterized template | |
375 | func_type_str = "template"; | |
376 | break; | |
377 | default: | |
378 | break; | |
379 | } | |
380 | Ttcn::FormalParList* fp_list = func_ass != NULL ? func_ass->get_FormalParList() : NULL; | |
381 | if (fp_list != NULL && fp_list->get_nof_fps() != 0) { | |
382 | // has parameters | |
383 | char* fp_names_str = NULL; | |
384 | char* fp_types_str = NULL; | |
385 | char* fp_add_var_str = NULL; | |
386 | for (size_t i = 0; i < fp_list->get_nof_fps(); ++i) { | |
387 | // gather everything needed for this parameter in sub-strings | |
388 | Ttcn::FormalPar* fp = fp_list->get_fp_byIndex(i); | |
389 | const char* fp_type_str = NULL; | |
390 | switch (fp->get_asstype()) { | |
391 | case Common::Assignment::A_PAR_VAL: | |
392 | case Common::Assignment::A_PAR_VAL_IN: | |
393 | case Common::Assignment::A_PAR_TEMPL_IN: | |
394 | fp_type_str = "in"; | |
395 | break; | |
396 | case Common::Assignment::A_PAR_VAL_INOUT: | |
397 | case Common::Assignment::A_PAR_TEMPL_INOUT: | |
398 | case Common::Assignment::A_PAR_TIMER: // treat timers and ports as 'inout' parameters | |
399 | case Common::Assignment::A_PAR_PORT: | |
400 | fp_type_str = "inout"; | |
401 | break; | |
402 | case Common::Assignment::A_PAR_VAL_OUT: | |
403 | case Common::Assignment::A_PAR_TEMPL_OUT: | |
404 | fp_type_str = "out"; | |
405 | break; | |
406 | default: | |
407 | break; | |
408 | } | |
409 | fp_names_str = mputprintf(fp_names_str, | |
410 | "param_names[%d] = \"%s\";\n", (int)i, fp->get_id().get_ttcnname().c_str()); | |
411 | fp_types_str = mputprintf(fp_types_str, | |
412 | "param_types[%d] = \"%s\";\n", (int)i, fp_type_str); | |
413 | fp_add_var_str = generate_code_debugger_add_var(fp_add_var_str, fp); | |
414 | } | |
415 | str = mputprintf(str, | |
416 | "charstring_list param_names;\n" | |
417 | "%s" | |
418 | "charstring_list param_types;\n" | |
419 | "%s" | |
420 | "TTCN3_Debug_Function debug_scope(\"%s\", \"%s\", \"%s\", param_names, param_types, %s);\n" | |
421 | "%s" | |
422 | "debug_scope.initial_snapshot();\n" | |
423 | , fp_names_str, fp_types_str | |
424 | , func_ass->get_id().get_dispname().c_str(), func_type_str | |
425 | , func_ass->get_my_scope()->get_scope_mod()->get_modid().get_ttcnname().c_str() | |
426 | , comp_str.c_str(), fp_add_var_str); | |
427 | Free(fp_names_str); | |
428 | Free(fp_types_str); | |
429 | Free(fp_add_var_str); | |
430 | } | |
431 | else { | |
432 | // no parameters | |
433 | str = mputprintf(str, | |
434 | "charstring_list no_params = NULL_VALUE;\n" | |
435 | "TTCN3_Debug_Function debug_scope(\"%s\", \"%s\", \"%s\", no_params, no_params, %s);\n" | |
436 | "debug_scope.initial_snapshot();\n" | |
437 | , func_ass->get_id().get_dispname().c_str(), func_type_str | |
438 | , func_ass->get_my_scope()->get_scope_mod()->get_modid().get_ttcnname().c_str() | |
439 | , comp_str.c_str()); | |
440 | } | |
441 | return str; | |
442 | } | |
443 | ||
444 | } |