1 /* Common operations. 2 3 Copyright (C) 2015-2018, 2021 Paul Boddie <paul@boddie.org.uk> 4 5 This program is free software; you can redistribute it and/or modify it under 6 the terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3 of the License, or (at your option) any later 8 version. 9 10 This program is distributed in the hope that it will be useful, but WITHOUT 11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 12 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 13 details. 14 15 You should have received a copy of the GNU General Public License along with 16 this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 #include "gc.h" /* GC_MALLOC, GC_REALLOC */ 20 #include "types.h" 21 #include "ops.h" 22 #include "progops.h" /* for raising errors */ 23 #include "progconsts.h" 24 #include "progtypes.h" 25 26 /* Get object reference from attribute. */ 27 28 __ref __VALUE(__attr attr) 29 { 30 return (__ref) (attr.rawvalue & (~__TAG_MASK)); 31 } 32 33 /* Basic structure tests. */ 34 35 static inline int __HASATTR(__ref obj, int pos, int code) 36 { 37 return (pos < obj->table->size) && (obj->table->attrs[pos] == code); 38 } 39 40 /* Generic load wrapper employing temporary stack storage. */ 41 42 __attr __load(__attr value) 43 { 44 /* Copy values where appropriate. */ 45 46 if (__COPYABLE(value)) 47 { 48 size_t value_size = __INSTANCE_SIZE(__VALUE(value)); 49 __attr target = __stack_allocate(__stack, value_size); 50 51 __COPY_TO(__VALUE(value), __VALUE(target), value_size); 52 return __TO_MUTABLE(target); 53 } 54 else 55 return value; 56 } 57 58 /* Direct access and manipulation of static objects. */ 59 60 __attr __load_static_ignore(__ref obj) 61 { 62 return __ATTRVALUE(obj); 63 } 64 65 __attr __load_static_replace(__attr context, __ref obj) 66 { 67 return __update_context(context, __ATTRVALUE(obj)); 68 } 69 70 __attr __load_static_test(__attr context, __ref obj) 71 { 72 return __test_context(context, __ATTRVALUE(obj)); 73 } 74 75 /* Direct retrieval operations, returning and setting attributes. */ 76 77 __attr __load_via_object__(__ref obj, int pos) 78 { 79 return obj->attrs[pos]; 80 } 81 82 __attr __load_via_class__(__ref obj, int pos) 83 { 84 return __load_via_object__(__get_class(obj), pos); 85 } 86 87 __attr __get_class_and_load__(__ref obj, int pos) 88 { 89 if (__is_instance(obj)) 90 return __load_via_class__(obj, pos); 91 else 92 return __load_via_object__(obj, pos); 93 } 94 95 /* Direct storage operations. */ 96 97 __attr __store_local(__attr target, __attr value) 98 { 99 /* Copy values where appropriate. */ 100 101 if (__COPYABLE(value)) 102 { 103 size_t value_size = __INSTANCE_SIZE(__VALUE(value)); 104 105 /* Allocate new space for values if the target is not a mutable value 106 or refers to an object that is too small for the value. */ 107 108 if ((__VALUE(target) == NULL) || !__MUTABLE(target) || (value_size > __INSTANCE_SIZE(__VALUE(target)))) 109 target = __stack_allocate(__stack, value_size); 110 111 __COPY_TO(__VALUE(value), __VALUE(target), value_size); 112 return __TO_MUTABLE(target); 113 } 114 else 115 return value; 116 } 117 118 void __store_target(__attr *target, __attr value) 119 { 120 /* Copy values where appropriate. */ 121 122 if (__COPYABLE(value)) 123 { 124 size_t value_size = __INSTANCE_SIZE(__VALUE(value)); 125 126 if ((__VALUE(*target) == NULL) || (!__MUTABLE(*target)) || (value_size > __INSTANCE_SIZE(__VALUE(*target)))) 127 *target = __ATTRVALUE(__ALLOCATEIM(1, value_size)); 128 129 __COPY_TO(__VALUE(value), __VALUE(*target), value_size); 130 *target = __TO_MUTABLE(*target); 131 } 132 else 133 *target = value; 134 } 135 136 int __store_via_object__(__ref obj, int pos, __attr value) 137 { 138 /* Copy values where appropriate. */ 139 140 if (__COPYABLE(value)) 141 { 142 size_t value_size = __INSTANCE_SIZE(__VALUE(value)); 143 __attr target = obj->attrs[pos]; 144 145 /* Allocate new space for values if the target is not a mutable value 146 or refers to an object that is too small for the value. */ 147 148 if ((__VALUE(target) == NULL) || (!__MUTABLE(target)) || (value_size > __INSTANCE_SIZE(__VALUE(target)))) 149 target = __ATTRVALUE(__ALLOCATEIM(1, value_size)); 150 151 __COPY_TO(__VALUE(value), __VALUE(target), value_size); 152 obj->attrs[pos] = __TO_MUTABLE(target); 153 } 154 else 155 obj->attrs[pos] = value; 156 157 return 1; 158 } 159 160 int __store_via_object_internal__(__ref obj, int pos, __attr value) 161 { 162 obj->attrs[pos] = value; 163 return 1; 164 } 165 166 int __store_via_class__(__ref obj, int pos, __attr value) 167 { 168 return __store_via_object__(__get_class(obj), pos, value); 169 } 170 171 int __get_class_and_store__(__ref obj, int pos, __attr value) 172 { 173 /* Forbid class-relative assignments. */ 174 175 __raise_type_error(); 176 return 0; 177 } 178 179 /* Introspection. */ 180 181 int __is_instance(__ref obj) 182 { 183 return obj->pos == __INSTANCEPOS; 184 } 185 186 int __is_subclass(__ref obj, __attr cls) 187 { 188 return __HASATTR(obj, __TYPEPOS(__VALUE(cls)), __TYPECODE(__VALUE(cls))); 189 } 190 191 int __is_instance_subclass(__ref obj, __attr cls) 192 { 193 return __is_instance(obj) && __HASATTR(__get_class(obj), __TYPEPOS(__VALUE(cls)), __TYPECODE(__VALUE(cls))); 194 } 195 196 int __is_type_instance(__ref obj) 197 { 198 return __HASATTR(__get_class(obj), __TYPE_CLASS_POS, __TYPE_CLASS_CODE); 199 } 200 201 __ref __get_class(__ref obj) 202 { 203 return __VALUE(__load_via_object(obj, __class__)); 204 } 205 206 __attr __get_class_attr(__ref obj) 207 { 208 return __load_via_object(obj, __class__); 209 } 210 211 /* Attribute testing operations. */ 212 213 __ref __test_specific_instance(__ref obj, __ref type) 214 { 215 return __get_class(obj) == type ? obj : 0; 216 } 217 218 __ref __test_specific_object(__ref obj, __ref type) 219 { 220 return __test_specific_type(obj, type) || __test_specific_instance(obj, type) ? obj : 0; 221 } 222 223 __ref __test_specific_type(__ref obj, __ref type) 224 { 225 return obj == type ? obj : 0; 226 } 227 228 __ref __test_common_instance__(__ref obj, int pos, int code) 229 { 230 return __HASATTR(__get_class(obj), pos, code) ? obj : 0; 231 } 232 233 __ref __test_common_object__(__ref obj, int pos, int code) 234 { 235 return __test_common_type__(obj, pos, code) || __test_common_instance__(obj, pos, code) ? obj : 0; 236 } 237 238 __ref __test_common_type__(__ref obj, int pos, int code) 239 { 240 return __HASATTR(obj, pos, code) ? obj : 0; 241 } 242 243 /* Attribute testing and retrieval operations. */ 244 245 __attr __check_and_load_via_object_null(__ref obj, int pos, int code) 246 { 247 if (__HASATTR(obj, pos, code)) 248 return __load_via_object__(obj, pos); 249 else 250 return __NULL; 251 } 252 253 __attr __check_and_load_via_class__(__ref obj, int pos, int code) 254 { 255 return __check_and_load_via_object__(__get_class(obj), pos, code); 256 } 257 258 __attr __check_and_load_via_object__(__ref obj, int pos, int code) 259 { 260 if (__HASATTR(obj, pos, code)) 261 return __load_via_object__(obj, pos); 262 263 __raise_type_error(); 264 return __NULL; 265 } 266 267 __attr __check_and_load_via_any__(__ref obj, int pos, int code) 268 { 269 __attr out = __check_and_load_via_object_null(obj, pos, code); 270 if (__ISNULL(out)) 271 out = __check_and_load_via_class__(obj, pos, code); 272 return out; 273 } 274 275 /* Attribute testing and storage operations. */ 276 277 int __check_and_store_via_class__(__ref obj, int pos, int code, __attr value) 278 { 279 /* Forbid class-relative assignments. */ 280 281 __raise_type_error(); 282 return 0; 283 } 284 285 int __check_and_store_via_object__(__ref obj, int pos, int code, __attr value) 286 { 287 if (__HASATTR(obj, pos, code)) 288 { 289 __store_via_object__(obj, pos, value); 290 return 1; 291 } 292 293 /* No suitable attribute. */ 294 295 __raise_type_error(); 296 return 0; 297 } 298 299 int __check_and_store_via_object_internal__(__ref obj, int pos, int code, __attr value) 300 { 301 if (__HASATTR(obj, pos, code)) 302 { 303 __store_via_object_internal__(obj, pos, value); 304 return 1; 305 } 306 307 /* No suitable attribute. */ 308 309 __raise_type_error(); 310 return 0; 311 } 312 313 int __check_and_store_via_any__(__ref obj, int pos, int code, __attr value) 314 { 315 if (__check_and_store_via_object__(obj, pos, code, value)) 316 return 1; 317 318 /* Forbid class-relative assignments. */ 319 320 __raise_type_error(); 321 return 0; 322 } 323 324 /* Context-related operations. */ 325 326 int __test_context_update(__attr context, __attr attr, int invoke) 327 { 328 /* Return whether the context should be updated for the attribute. */ 329 330 __attr attrcontext = __CONTEXT_AS_VALUE(attr); 331 __ref attrcontextvalue = __VALUE(attrcontext); 332 333 /* Preserve any existing null or instance context. */ 334 335 if (__ISNULL(attrcontext) || __is_instance(attrcontextvalue)) 336 return 0; 337 338 /* Test any instance context against the context employed by the 339 attribute. */ 340 341 if (__is_instance(__VALUE(context))) 342 { 343 /* Obtain the special class attribute position and code identifying the 344 attribute context's class, inspecting the context instance for 345 compatibility. */ 346 347 if (__test_common_instance__(__VALUE(context), __TYPEPOS(attrcontextvalue), __TYPECODE(attrcontextvalue))) 348 return 1; 349 else 350 __raise_type_error(); 351 } 352 353 /* Without a null or instance context, an invocation cannot be performed. */ 354 355 if (invoke) 356 __raise_unbound_method_error(); 357 358 /* Test for access to a type class attribute using a type instance. */ 359 360 if (__test_specific_type(attrcontextvalue, &__TYPE_CLASS_TYPE) && __is_type_instance(__VALUE(context))) 361 return 1; 362 363 /* Otherwise, preserve the attribute as retrieved. */ 364 365 return 0; 366 } 367 368 __attr __test_context(__attr context, __attr attr) 369 { 370 /* Update the context or return the unchanged attribute. */ 371 372 if (__test_context_update(context, attr, 0)) 373 return __update_context(context, attr); 374 else 375 return attr; 376 } 377 378 __attr __update_context(__attr context, __attr attr) 379 { 380 return __new_wrapper(context, attr); 381 } 382 383 __attr __test_context_revert(int target, __attr context, __attr attr, __attr contexts[]) 384 { 385 /* Revert the local context to that employed by the attribute if the 386 supplied context is not appropriate. */ 387 388 if (!__test_context_update(context, attr, 1)) 389 contexts[target] = __CONTEXT_AS_VALUE(attr); 390 return attr; 391 } 392 393 __attr __test_context_static(int target, __attr context, __ref value, __attr contexts[]) 394 { 395 /* Set the local context to the specified context if appropriate. */ 396 397 if (__test_context_update(context, __ATTRVALUE(value), 1)) 398 contexts[target] = context; 399 return __ATTRVALUE(value); 400 } 401 402 /* Context testing for invocations. */ 403 404 int __type_method_invocation(__attr context, __attr target) 405 { 406 __attr targetcontext = __CONTEXT_AS_VALUE(target); 407 408 /* Require instances, not classes, where methods are function instances. */ 409 410 if (!__is_instance(__VALUE(target))) 411 return 0; 412 413 /* Access the context of the callable and test if it is the type object. */ 414 415 return (!__ISNULL(targetcontext) && __test_specific_type(__VALUE(targetcontext), &__TYPE_CLASS_TYPE) && __is_type_instance(__VALUE(context))); 416 } 417 418 __attr __unwrap_callable(__attr callable) 419 { 420 __attr value = __check_and_load_via_object_null(__VALUE(callable), __ATTRPOS(__value__), __ATTRCODE(__value__)); 421 return __VALUE(value) ? value : callable; 422 } 423 424 __attr (*__get_function_unchecked(__attr target))() 425 { 426 return __load_via_object(__VALUE(__unwrap_callable(target)), __fn__).fn; 427 } 428 429 __attr (*__get_function(__attr context, __attr target))() 430 { 431 return __get_function_unwrapped(context, __unwrap_callable(target)); 432 } 433 434 __attr (*__get_function_unwrapped(__attr context, __attr target))() 435 { 436 /* Require null or instance contexts for functions and methods respectively, 437 or type instance contexts for type methods. */ 438 439 if (__ISNULL(context) || __is_instance(__VALUE(context)) || __type_method_invocation(context, target)) 440 return __get_function_member(target); 441 else 442 return __unbound_method; 443 } 444 445 __attr (*__get_function_member(__attr target))() 446 { 447 return __load_via_object(__VALUE(target), __fn__).fn; 448 } 449 450 __attr (*__check_and_get_function(__attr context, __attr target))() 451 { 452 return __check_and_get_function_unwrapped(context, __unwrap_callable(target)); 453 } 454 455 __attr (*__check_and_get_function_unwrapped(__attr context, __attr target))() 456 { 457 /* Require null or instance contexts for functions and methods respectively, 458 or type instance contexts for type methods. */ 459 460 if (__ISNULL(context) || __is_instance(__VALUE(context)) || __type_method_invocation(context, target)) 461 return __check_and_load_via_object__(__VALUE(target), __ATTRPOS(__fn__), __ATTRCODE(__fn__)).fn; 462 else 463 return __unbound_method; 464 } 465 466 /* Parameter position operations. */ 467 468 int __HASPARAM(const __ptable *ptable, int ppos, int pcode) 469 { 470 __param param; 471 472 if (ppos < ptable->size) 473 { 474 param = ptable->params[ppos]; 475 if (param.code == pcode) 476 return param.pos; 477 } 478 479 return -1; 480 } 481 482 /* Conversions. */ 483 484 __attr __CONTEXT_AS_VALUE(__attr attr) 485 { 486 return __check_and_load_via_object_null(__VALUE(attr), __ATTRPOS(__context__), __ATTRCODE(__context__)); 487 } 488 489 /* Type testing. */ 490 491 __ref __ISFUNC(__ref obj) 492 { 493 return __test_specific_instance(obj, &__FUNCTION_TYPE); 494 } 495 496 /* Attribute codes and positions for type objects. */ 497 498 unsigned int __TYPECODE(__ref obj) 499 { 500 return obj->table->attrs[obj->pos]; 501 } 502 503 unsigned int __TYPEPOS(__ref obj) 504 { 505 return obj->pos; 506 } 507 508 /* Memory allocation. */ 509 510 void *__ALLOCATE(size_t nmemb, size_t size) 511 { 512 void *ptr = GC_MALLOC(nmemb * size); /* sets memory to zero */ 513 if (ptr == NULL) 514 __raise_memory_error(); 515 return ptr; 516 } 517 518 void *__ALLOCATEIM(size_t nmemb, size_t size) 519 { 520 void *ptr = GC_MALLOC_ATOMIC(nmemb * size); /* sets memory to zero */ 521 if (ptr == NULL) 522 __raise_memory_error(); 523 return ptr; 524 } 525 526 void *__REALLOCATE(void *ptr, size_t size) 527 { 528 void *nptr = GC_REALLOC(ptr, size); 529 if (nptr == NULL) 530 __raise_memory_error(); 531 return nptr; 532 } 533 534 /* Copying of structures. */ 535 536 __ref __COPY(__ref obj, size_t size) 537 { 538 __ref copy = (__ref) __ALLOCATE(1, size); 539 memcpy(copy, obj, size); 540 return copy; 541 } 542 543 void __COPY_TO(__ref source, __ref target, size_t size) 544 { 545 memcpy(target, source, size); 546 } 547 548 /* Stack management. */ 549 550 __attr __stack_init() 551 { 552 __attr __stack; 553 554 __stack.stackdesc = (__stackdesc *) __ALLOCATE(1, sizeof(__stackdesc)); 555 __stack.stackdesc->current = NULL; 556 __stack_expand(__stack); 557 558 return __stack; 559 } 560 561 __attr __stack_allocate(__attr __stack, size_t size) 562 { 563 char *result; 564 __section *section = __stack.stackdesc->current; 565 566 if (section->limit - section->level < size) 567 { 568 __stack_expand(__stack); 569 section = __stack.stackdesc->current; 570 } 571 572 result = section->level; 573 section->level += size; 574 575 return __ATTRVALUE(result); 576 } 577 578 void __stack_expand(__attr __stack) 579 { 580 __section *current = __stack.stackdesc->current; 581 __section *section = (__section *) __ALLOCATE(1, sizeof(__section)); 582 char *base = (char *) __ALLOCATEIM(1, __STACK_SECTION_SIZE); 583 584 section->base = base; 585 section->level = base; 586 section->limit = base + __STACK_SECTION_SIZE; 587 section->previous = current; 588 589 __stack.stackdesc->current = section; 590 } 591 592 __attr __return(__attr result, __section *section, char *level) 593 { 594 __ref obj = __VALUE(result); 595 char *target = level; 596 size_t size; 597 598 if (__COPYABLE(result)) 599 { 600 size = __INSTANCE_SIZE(obj); 601 602 /* Test for space in the section. */ 603 604 if (size > section->limit - level) 605 { 606 __stack_expand(__stack); 607 section = __stack.stackdesc->current; 608 target = section->base; 609 } 610 611 /* Copy into the stack and adjust the level. */ 612 613 __COPY_TO(obj, (__ref) target, size); 614 level = target + size; 615 616 /* Reference the new location of the object. */ 617 618 result = __MUTABLEVALUE(target); 619 } 620 621 /* Set the level and current section.*/ 622 623 section->level = level; 624 __stack.stackdesc->current = section; 625 626 return result; 627 }