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