Commit | Line | Data |
---|---|---|
d44e3c4f | 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 | * Balasko, Jeno | |
10 | * Beres, Szabolcs | |
11 | * Kovacs, Ferenc | |
12 | * Raduly, Csaba | |
13 | * | |
14 | ******************************************************************************/ | |
970ed795 EL |
15 | #include "ArrayDimensions.hh" |
16 | #include "../string.hh" | |
17 | #include "../CompilerError.hh" | |
18 | #include "../Type.hh" | |
19 | #include "../Value.hh" | |
20 | #include "AST_ttcn3.hh" | |
21 | ||
22 | #include <limits.h> | |
23 | ||
24 | namespace Ttcn { | |
25 | ||
26 | using namespace Common; | |
27 | ||
28 | // ================================= | |
29 | // ===== ArrayDimension | |
30 | // ================================= | |
31 | ||
32 | ArrayDimension::ArrayDimension(const ArrayDimension& p) | |
33 | : Node(p), Location(p), checked(false), is_range(p.is_range), | |
34 | has_error(false), size(0), offset(0) | |
35 | { | |
36 | if (is_range) { | |
37 | u.range.lower = p.u.range.lower->clone(); | |
38 | u.range.upper = p.u.range.upper->clone(); | |
39 | } else { | |
40 | u.single = p.u.single->clone(); | |
41 | } | |
42 | } | |
43 | ||
44 | ArrayDimension::ArrayDimension(Value *p_single) | |
45 | : Node(), checked(false), is_range(false), has_error(false), | |
46 | size(0), offset(0) | |
47 | { | |
48 | if (!p_single) FATAL_ERROR("ArrayDimension::ArrayDimension()"); | |
49 | u.single = p_single; | |
50 | } | |
51 | ||
52 | ArrayDimension::ArrayDimension(Value *p_lower, Value *p_upper) | |
53 | : Node(), checked(false), is_range(true), has_error(false), | |
54 | size(0), offset(0) | |
55 | { | |
56 | if (!p_lower || !p_upper) FATAL_ERROR("ArrayDimension::ArrayDimension()"); | |
57 | u.range.lower = p_lower; | |
58 | u.range.upper = p_upper; | |
59 | } | |
60 | ||
61 | ArrayDimension::~ArrayDimension() | |
62 | { | |
63 | if (is_range) { | |
64 | delete u.range.lower; | |
65 | delete u.range.upper; | |
66 | } else { | |
67 | delete u.single; | |
68 | } | |
69 | } | |
70 | ||
71 | ArrayDimension *ArrayDimension::clone() const | |
72 | { | |
73 | return new ArrayDimension(*this); | |
74 | } | |
75 | ||
76 | void ArrayDimension::set_my_scope(Scope *p_scope) | |
77 | { | |
78 | if (!p_scope) FATAL_ERROR("ArrayDimension::set_my_scope()"); | |
79 | my_scope = p_scope; | |
80 | if (is_range) { | |
81 | u.range.lower->set_my_scope(p_scope); | |
82 | u.range.upper->set_my_scope(p_scope); | |
83 | } else { | |
84 | u.single->set_my_scope(p_scope); | |
85 | } | |
86 | } | |
87 | ||
88 | void ArrayDimension::set_fullname(const string& p_fullname) | |
89 | { | |
90 | Node::set_fullname(p_fullname); | |
91 | if (is_range) { | |
92 | u.range.lower->set_fullname(p_fullname + ".<lower>"); | |
93 | u.range.upper->set_fullname(p_fullname + ".<upper>"); | |
94 | } else { | |
95 | u.single->set_fullname(p_fullname); | |
96 | } | |
97 | } | |
98 | ||
99 | void ArrayDimension::chk() | |
100 | { | |
101 | if (checked) return; | |
102 | checked = true; | |
103 | Int int_size = 0; | |
104 | has_error = false; | |
105 | if (is_range) { | |
106 | { | |
107 | Error_Context cntxt(u.range.lower, "In lower bound of array indices"); | |
108 | u.range.lower->chk_expr_int(Type::EXPECTED_CONSTANT); | |
109 | } | |
110 | { | |
111 | Error_Context cntxt(u.range.upper, "In upper bound of array indices"); | |
112 | u.range.upper->chk_expr_int(Type::EXPECTED_CONSTANT); | |
113 | } | |
114 | Value *v_lower = u.range.lower->get_value_refd_last(); | |
115 | if (v_lower->get_valuetype() != Value::V_INT) has_error = true; | |
116 | Value *v_upper = u.range.upper->get_value_refd_last(); | |
117 | if (v_upper->get_valuetype() != Value::V_INT) has_error = true; | |
118 | if (!has_error) { | |
119 | const int_val_t *lower_int = v_lower->get_val_Int(); | |
120 | const int_val_t *upper_int = v_upper->get_val_Int(); | |
121 | if (*lower_int > INT_MAX) { | |
122 | u.range.lower->error("The lower bound of an array index should be " | |
123 | "less than `%d' instead of `%s'", INT_MAX, | |
124 | (lower_int->t_str()).c_str()); | |
125 | has_error = true; | |
126 | } | |
127 | if (*upper_int > INT_MAX) { | |
128 | u.range.upper->error("The upper bound of an array index should be " | |
129 | "less than `%d' instead of `%s'", INT_MAX, | |
130 | (upper_int->t_str()).c_str()); | |
131 | has_error = true; | |
132 | } | |
133 | if (!has_error) { | |
134 | Int lower = lower_int->get_val(); | |
135 | Int upper = upper_int->get_val(); | |
136 | if (lower > upper) { | |
137 | error("The lower bound of array index (%s) is " | |
138 | "greater than the upper bound (%s)", Int2string(lower).c_str(), | |
139 | Int2string(upper).c_str()); | |
140 | has_error = true; | |
141 | } else { | |
142 | int_size = upper - lower + 1; | |
143 | offset = lower; | |
144 | } | |
145 | } | |
146 | } | |
147 | } else { | |
148 | { | |
149 | Error_Context cntxt(u.single, "In array size"); | |
150 | u.single->chk_expr_int(Type::EXPECTED_CONSTANT); | |
151 | } | |
152 | Value *v = u.single->get_value_refd_last(); | |
153 | if (v->get_valuetype() != Value::V_INT) has_error = true; | |
154 | if (!has_error) { | |
155 | const int_val_t *int_size_int = v->get_val_Int(); | |
156 | if (*int_size_int > INT_MAX) { | |
157 | u.single->error("The array size should be less than `%d' instead " | |
158 | "of `%s'", INT_MAX, (int_size_int->t_str()).c_str()); | |
159 | has_error = true; | |
160 | } | |
161 | if (!has_error) { | |
162 | int_size = int_size_int->get_val(); | |
163 | if (int_size <= 0) { | |
164 | u.single->error("A positive integer value was expected as array " | |
165 | "size instead of `%s'", Int2string(int_size).c_str()); | |
166 | has_error = true; | |
167 | } else { | |
168 | size = int_size; | |
169 | offset = 0; | |
170 | } | |
171 | } | |
172 | } | |
173 | } | |
174 | if (!has_error) { | |
175 | size = static_cast<size_t>(int_size); | |
176 | if (static_cast<Int>(size) != int_size) { | |
177 | error("Array size `%s' is too large for being represented in memory", | |
178 | Int2string(int_size).c_str()); | |
179 | has_error = true; | |
180 | } | |
181 | } | |
182 | } | |
183 | ||
184 | void ArrayDimension::chk_index(Value *index, Type::expected_value_t exp_val) | |
185 | { | |
186 | if (!checked) chk(); | |
187 | index->chk_expr_int(exp_val); | |
188 | if (has_error || index->is_unfoldable()) return; | |
189 | const int_val_t *v_index_int = index->get_value_refd_last() | |
190 | ->get_val_Int(); | |
191 | if (*v_index_int < offset) { | |
192 | index->error("Array index underflow: the index value must be at least " | |
193 | "`%s' instead of `%s'", Int2string(offset).c_str(), | |
194 | (v_index_int->t_str()).c_str()); | |
195 | index->set_valuetype(Value::V_ERROR); | |
196 | } else if (*v_index_int >= offset + static_cast<Int>(size)) { | |
197 | index->error("Array index overflow: the index value must be at most " | |
198 | "`%s' instead of `%s'", Int2string(offset + size - 1).c_str(), | |
199 | (v_index_int->t_str()).c_str()); | |
200 | index->set_valuetype(Value::V_ERROR); | |
201 | } | |
202 | } | |
203 | ||
204 | size_t ArrayDimension::get_size() | |
205 | { | |
206 | if (!checked) chk(); | |
207 | if (has_error) return 0; | |
208 | else return size; | |
209 | } | |
210 | ||
211 | Int ArrayDimension::get_offset() | |
212 | { | |
213 | if (!checked) chk(); | |
214 | if (has_error) return 0; | |
215 | else return offset; | |
216 | } | |
217 | ||
218 | string ArrayDimension::get_stringRepr() | |
219 | { | |
220 | if (!checked) chk(); | |
221 | string ret_val("["); | |
222 | if (has_error) ret_val += "<erroneous>"; | |
223 | else if (is_range) { | |
224 | ret_val += Int2string(offset); | |
225 | ret_val += ".."; | |
226 | ret_val += Int2string(offset + size - 1); | |
227 | } else ret_val += Int2string(size); | |
228 | ret_val += "]"; | |
229 | return ret_val; | |
230 | } | |
231 | ||
232 | bool ArrayDimension::is_identical(ArrayDimension *p_dim) | |
233 | { | |
234 | if (!p_dim) FATAL_ERROR("ArrayDimension::is_identical()"); | |
235 | if (!checked) chk(); | |
236 | if (!p_dim->checked) p_dim->chk(); | |
237 | if (has_error || p_dim->has_error) return true; | |
238 | else return size == p_dim->size && offset == p_dim->offset; | |
239 | } | |
240 | ||
241 | string ArrayDimension::get_value_type(Type *p_element_type, Scope *p_scope) | |
242 | { | |
243 | if (!checked) chk(); | |
244 | if (has_error) FATAL_ERROR("ArrayDimension::get_value_type()"); | |
245 | string ret_val("VALUE_ARRAY<"); | |
246 | ret_val += p_element_type->get_genname_value(p_scope); | |
247 | ret_val += ", "; | |
248 | ret_val += Int2string(size); | |
249 | ret_val += ", "; | |
250 | ret_val += Int2string(offset); | |
251 | ret_val += '>'; | |
252 | return ret_val; | |
253 | } | |
254 | ||
255 | string ArrayDimension::get_template_type(Type *p_element_type, Scope *p_scope) | |
256 | { | |
257 | if (!checked) chk(); | |
258 | if (has_error) FATAL_ERROR("ArrayDimension::get_template_type()"); | |
259 | string ret_val("TEMPLATE_ARRAY<"); | |
260 | ret_val += p_element_type->get_genname_value(p_scope); | |
261 | ret_val += ", "; | |
262 | ret_val += p_element_type->get_genname_template(p_scope); | |
263 | ret_val += ", "; | |
264 | ret_val += Int2string(size); | |
265 | ret_val += ", "; | |
266 | ret_val += Int2string(offset); | |
267 | ret_val += '>'; | |
268 | return ret_val; | |
269 | } | |
270 | ||
271 | void ArrayDimension::dump(unsigned level) const | |
272 | { | |
273 | DEBUG(level, "Array dimension:"); | |
274 | if (is_range) { | |
275 | u.range.lower->dump(level + 1); | |
276 | DEBUG(level, ".."); | |
277 | u.range.upper->dump(level + 1); | |
278 | } else { | |
279 | u.single->dump(level + 1); | |
280 | } | |
281 | } | |
282 | ||
283 | // ================================= | |
284 | // ===== ArrayDimensions | |
285 | // ================================= | |
286 | ||
287 | ArrayDimensions::~ArrayDimensions() | |
288 | { | |
289 | for (size_t i = 0; i < dims.size(); i++) delete dims[i]; | |
290 | dims.clear(); | |
291 | } | |
292 | ||
293 | ArrayDimensions *ArrayDimensions::clone() const | |
294 | { | |
295 | FATAL_ERROR("ArrayDimensions::clone"); | |
296 | } | |
297 | ||
298 | void ArrayDimensions::set_my_scope(Scope *p_scope) | |
299 | { | |
300 | for (size_t i = 0; i < dims.size(); i++) dims[i]->set_my_scope(p_scope); | |
301 | } | |
302 | ||
303 | void ArrayDimensions::set_fullname(const string& p_fullname) | |
304 | { | |
305 | Node::set_fullname(p_fullname); | |
306 | for (size_t i = 0; i < dims.size(); i++) | |
307 | dims[i]->set_fullname(p_fullname + "." + Int2string(i + 1)); | |
308 | } | |
309 | ||
310 | void ArrayDimensions::add(ArrayDimension *dim) | |
311 | { | |
312 | if (!dim) FATAL_ERROR("ArrayDimensions::add()"); | |
313 | dims.add(dim); | |
314 | } | |
315 | ||
316 | void ArrayDimensions::chk() | |
317 | { | |
318 | for (size_t i = 0; i < dims.size(); i++) { | |
319 | ArrayDimension *dim = dims[i]; | |
320 | Error_Context cntxt(dim, "In array dimension #%lu", (unsigned long)(i+1)); | |
321 | dim->chk(); | |
322 | } | |
323 | } | |
324 | ||
325 | void ArrayDimensions::chk_indices(Common::Reference *ref, const char *def_name, | |
326 | bool allow_slicing, Type::expected_value_t exp_val) | |
327 | { | |
328 | FieldOrArrayRefs *subrefs = ref->get_subrefs(); | |
329 | if (!subrefs) { | |
330 | if (!allow_slicing) | |
331 | ref->error("Reference to a %s array without array index", def_name); | |
332 | return; | |
333 | } | |
334 | size_t nof_refs = subrefs->get_nof_refs(), nof_dims = dims.size(); | |
335 | size_t upper_limit = nof_refs > nof_dims ? nof_dims : nof_refs; | |
336 | for (size_t i = 0; i < upper_limit; i++) { | |
337 | FieldOrArrayRef *subref = subrefs->get_ref(i); | |
338 | if (subref->get_type() != FieldOrArrayRef::ARRAY_REF) { | |
339 | subref->error("Invalid field reference `%s' in a %s array", | |
340 | subref->get_id()->get_dispname().c_str(), def_name); | |
341 | return; | |
342 | } | |
343 | Error_Context cntxt(subref, "In array index #%lu", (unsigned long)(i+1)); | |
344 | dims[i]->chk_index(subref->get_val(), exp_val); | |
345 | } | |
346 | if (nof_refs < nof_dims) { | |
347 | if (!allow_slicing) ref->error("Too few indices in a reference to a %s " | |
348 | "array: the array has %lu dimensions, but the reference has only %lu " | |
349 | "array %s", def_name, (unsigned long)nof_dims, (unsigned long)nof_refs, | |
350 | nof_refs > 1 ? "indices" : "index"); | |
351 | } else if (nof_refs > nof_dims) { | |
352 | ref->error("Too many indices in a reference to a %s array: the reference " | |
353 | "has %lu array indices, but the array has only %lu dimension%s", | |
354 | def_name, (unsigned long) nof_refs, (unsigned long) nof_dims, | |
355 | nof_dims > 1 ? "s" : ""); | |
356 | } | |
357 | } | |
358 | ||
359 | size_t ArrayDimensions::get_array_size() | |
360 | { | |
361 | size_t ret_val = 1; | |
362 | for (size_t i = 0; i < dims.size(); i++) | |
363 | ret_val *= dims[i]->get_size(); | |
364 | return ret_val; | |
365 | } | |
366 | ||
367 | char *ArrayDimensions::generate_element_names(char *str, | |
368 | const string& p_name, size_t start_dim) | |
369 | { | |
370 | ArrayDimension *dim = dims[start_dim]; | |
371 | size_t dim_size = dim->get_size(); | |
372 | Int dim_offset = dim->get_offset(); | |
373 | if (start_dim + 1 < dims.size()) { | |
374 | // there are more dimensions to generate | |
375 | for (size_t i = 0; i < dim_size; i++) { | |
376 | if (i > 0) str = mputstr(str, ", "); | |
377 | str = generate_element_names(str, | |
378 | p_name + "[" + Int2string(dim_offset + i) + "]", start_dim + 1); | |
379 | } | |
380 | } else { | |
381 | // we are in the last dimension | |
382 | for (size_t i = 0; i < dim_size; i++) { | |
383 | if (i > 0) str = mputstr(str, ", "); | |
384 | str = mputprintf(str, "\"%s[%s]\"", p_name.c_str(), | |
385 | Int2string(dim_offset + i).c_str()); | |
386 | } | |
387 | } | |
388 | return str; | |
389 | } | |
390 | ||
391 | string ArrayDimensions::get_timer_type(size_t start_dim) | |
392 | { | |
393 | string ret_val("TIMER"); | |
394 | // the wrapping is started with the rightmost array dimension | |
395 | for (size_t i = dims.size(); i > start_dim; i--) { | |
396 | ArrayDimension *dim = dims[i - 1]; | |
397 | ret_val = "TIMER_ARRAY<" + ret_val + ", " + Int2string(dim->get_size()) + | |
398 | ", " + Int2string(dim->get_offset()) + ">"; | |
399 | } | |
400 | return ret_val; | |
401 | } | |
402 | ||
403 | string ArrayDimensions::get_port_type(const string& p_genname) | |
404 | { | |
405 | string ret_val(p_genname); | |
406 | // the wrapping is started with the rightmost array dimension | |
407 | for (size_t i = dims.size(); i > 0; i--) { | |
408 | ArrayDimension *dim = dims[i - 1]; | |
409 | ret_val = "PORT_ARRAY<" + ret_val + ", " + Int2string(dim->get_size()) + | |
410 | ", " + Int2string(dim->get_offset()) + ">"; | |
411 | } | |
412 | return ret_val; | |
413 | } | |
414 | ||
415 | void ArrayDimensions::dump(unsigned level) const | |
416 | { | |
417 | DEBUG(level, "Array dimensions: (%lu pcs.)", (unsigned long) dims.size()); | |
418 | for (size_t i = 0; i < dims.size(); i++) dims[i]->dump(level + 1); | |
419 | } | |
420 | ||
421 | bool ArrayDimensions::is_identical(ArrayDimensions *other) | |
422 | { | |
423 | if (!other) FATAL_ERROR("ArrayDimensions::is_identical()"); | |
424 | if (dims.size()!=other->dims.size()) return false; | |
425 | for (size_t i = 0; i < dims.size(); i++) | |
426 | if (!dims[i]->is_identical(other->dims[i])) return false; | |
427 | return true; | |
428 | } | |
429 | ||
430 | } |