| 1 | /////////////////////////////////////////////////////////////////////////////// |
| 2 | // Copyright (c) 2000-2015 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 | #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 | } |