1 /* Common operations. 2 3 Copyright (C) 2015-2018, 2023, 2024 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 if (!__INTEGER(attr)) 31 return attr.value; 32 else 33 return (__ref) &__common_integer_obj; 34 } 35 36 /* Basic structure tests. */ 37 38 static inline int __HASATTR(__ref obj, int pos, int code) 39 { 40 return (pos < obj->table->size) && (obj->table->attrs[pos] == code); 41 } 42 43 /* Direct access and manipulation of static objects. */ 44 45 __attr __load_static_ignore(__ref obj) 46 { 47 return __ATTRVALUE(obj); 48 } 49 50 __attr __load_static_replace(__attr __context, __attr context, __ref obj) 51 { 52 return __update_context(__context, context, __ATTRVALUE(obj)); 53 } 54 55 __attr __load_static_test(__attr __context, __attr context, __ref obj) 56 { 57 return __test_context(__context, context, __ATTRVALUE(obj)); 58 } 59 60 /* Direct retrieval operations, returning attribute locations. */ 61 62 __attr *__get_class_attr_ref__(__ref obj, int pos) 63 { 64 return __get_object_attr_ref__(__get_class(obj), pos); 65 } 66 67 /* Direct retrieval operations, returning and setting attributes. */ 68 69 __attr __load_via_object__(__ref obj, int pos) 70 { 71 return __load_via_attr_ref(__get_object_attr_ref__(obj, pos)); 72 } 73 74 __attr __load_via_class__(__ref obj, int pos) 75 { 76 return __load_via_object__(__get_class(obj), pos); 77 } 78 79 __attr __get_class_and_load__(__ref obj, int pos) 80 { 81 if (__is_instance(obj)) 82 return __load_via_class__(obj, pos); 83 else 84 return __load_via_object__(obj, pos); 85 } 86 87 /* Introspection. */ 88 89 int __is_instance(__ref obj) 90 { 91 return obj->pos == __INSTANCEPOS; 92 } 93 94 int __is_subclass(__ref obj, __attr cls) 95 { 96 return __HASATTR(obj, __TYPEPOS(__VALUE(cls)), __TYPECODE(__VALUE(cls))); 97 } 98 99 int __is_instance_subclass(__ref obj, __attr cls) 100 { 101 return __is_instance(obj) && __HASATTR(__get_class(obj), __TYPEPOS(__VALUE(cls)), __TYPECODE(__VALUE(cls))); 102 } 103 104 int __is_type_instance(__ref obj) 105 { 106 return __HASATTR(__get_class(obj), __TYPE_CLASS_POS, __TYPE_CLASS_CODE); 107 } 108 109 __ref __get_class(__ref obj) 110 { 111 return __VALUE(__load_via_object(obj, __class__)); 112 } 113 114 __attr __get_class_attr(__ref obj) 115 { 116 return __load_via_object(obj, __class__); 117 } 118 119 /* Attribute testing operations. */ 120 121 __ref __test_specific_instance(__ref obj, __ref type) 122 { 123 return __get_class(obj) == type ? obj : 0; 124 } 125 126 __ref __test_specific_object(__ref obj, __ref type) 127 { 128 return __test_specific_type(obj, type) || __test_specific_instance(obj, type) ? obj : 0; 129 } 130 131 __ref __test_specific_type(__ref obj, __ref type) 132 { 133 return obj == type ? obj : 0; 134 } 135 136 __ref __test_common_instance__(__ref obj, int pos, int code) 137 { 138 return __HASATTR(__get_class(obj), pos, code) ? obj : 0; 139 } 140 141 __ref __test_common_object__(__ref obj, int pos, int code) 142 { 143 return __test_common_type__(obj, pos, code) || __test_common_instance__(obj, pos, code) ? obj : 0; 144 } 145 146 __ref __test_common_type__(__ref obj, int pos, int code) 147 { 148 return __HASATTR(obj, pos, code) ? obj : 0; 149 } 150 151 /* Attribute testing and location operations, returning the address of the 152 attribute as opposed to its value. */ 153 154 __attr *__check_and_get_object_attr_ref_null(__ref obj, int pos, int code) 155 { 156 if (__HASATTR(obj, pos, code)) 157 return __get_object_attr_ref__(obj, pos); 158 else 159 return NULL; 160 } 161 162 __attr *__check_and_get_object_attr_ref__(__ref obj, int pos, int code) 163 { 164 if (__HASATTR(obj, pos, code)) 165 return __get_object_attr_ref__(obj, pos); 166 167 __raise_type_error(); 168 return NULL; 169 } 170 171 /* Attribute testing and retrieval operations. */ 172 173 __attr __check_and_load_via_object_null(__ref obj, int pos, int code) 174 { 175 __attr *attr = __check_and_get_object_attr_ref_null(obj, pos, code); 176 177 if (attr == NULL) 178 return __NULL; 179 else 180 return __load_via_attr_ref(attr); 181 } 182 183 __attr __check_and_load_via_class__(__ref obj, int pos, int code) 184 { 185 return __check_and_load_via_object__(__get_class(obj), pos, code); 186 } 187 188 __attr __check_and_load_via_object__(__ref obj, int pos, int code) 189 { 190 __attr attr = __check_and_load_via_object_null(obj, pos, code); 191 192 if (__ISNULL(attr)) 193 __raise_type_error(); 194 195 return attr; 196 } 197 198 __attr __check_and_load_via_any__(__ref obj, int pos, int code) 199 { 200 __attr attr = __check_and_load_via_object_null(obj, pos, code); 201 202 if (__ISNULL(attr)) 203 attr = __check_and_load_via_class__(obj, pos, code); 204 205 return attr; 206 } 207 208 /* Context-related operations. */ 209 210 int __test_context_update(__attr context, __attr attr, int invoke) 211 { 212 /* Return whether the context should be updated for the attribute. */ 213 214 __attr attrcontext = __CONTEXT_AS_VALUE(attr); 215 __ref attrcontextvalue = __VALUE(attrcontext); 216 217 /* Preserve any existing null or instance context. */ 218 219 if (__ISNULL(attrcontext) || __is_instance(attrcontextvalue)) 220 return 0; 221 222 /* Test any instance context against the context employed by the 223 attribute. */ 224 225 if (__is_instance(__VALUE(context))) 226 { 227 /* Obtain the special class attribute position and code identifying the 228 attribute context's class, inspecting the context instance for 229 compatibility. */ 230 231 if (__test_common_instance__(__VALUE(context), __TYPEPOS(attrcontextvalue), __TYPECODE(attrcontextvalue))) 232 return 1; 233 else 234 __raise_type_error(); 235 } 236 237 /* Without a null or instance context, an invocation cannot be performed. */ 238 239 if (invoke) 240 __raise_unbound_method_error(); 241 242 /* Test for access to a type class attribute using a type instance. */ 243 244 if (__test_specific_type(attrcontextvalue, &__TYPE_CLASS_TYPE) && __is_type_instance(__VALUE(context))) 245 return 1; 246 247 /* Otherwise, preserve the attribute as retrieved. */ 248 249 return 0; 250 } 251 252 __attr __test_context(__attr __context, __attr context, __attr attr) 253 { 254 /* Update the context or return the unchanged attribute. */ 255 256 if (__test_context_update(context, attr, 0)) 257 return __update_context(__context, context, attr); 258 else 259 return attr; 260 } 261 262 __attr __update_context(__attr __context, __attr context, __attr attr) 263 { 264 /* Support context replacement in a similar fashion to value replacement in 265 floats. */ 266 267 __ref obj = __VALUE(__context); 268 269 /* With any existing wrapper object allocated, replace the attributes and 270 return the same context. */ 271 272 if ((obj != NULL) && __is_instance(obj) && 273 (__get_class(obj) == &__builtins___core_wrapper)) 274 { 275 __store_via_attr_ref(__get_object_attr_ref(obj, __context__), context); 276 __store_via_attr_ref(__get_object_attr_ref(obj, __value__), attr); 277 return __context; 278 } 279 280 return __new_wrapper(context, attr); 281 } 282 283 __attr __test_context_revert(int target, __attr context, __attr attr, __attr contexts[]) 284 { 285 /* Revert the local context to that employed by the attribute if the 286 supplied context is not appropriate. */ 287 288 if (!__test_context_update(context, attr, 1)) 289 contexts[target] = __CONTEXT_AS_VALUE(attr); 290 return attr; 291 } 292 293 __attr __test_context_static(int target, __attr context, __ref value, __attr contexts[]) 294 { 295 /* Set the local context to the specified context if appropriate. */ 296 297 if (__test_context_update(context, __ATTRVALUE(value), 1)) 298 contexts[target] = context; 299 return __ATTRVALUE(value); 300 } 301 302 /* Context testing for invocations. */ 303 304 int __type_method_invocation(__attr context, __attr target) 305 { 306 __attr targetcontext = __CONTEXT_AS_VALUE(target); 307 308 /* Require instances, not classes, where methods are function instances. */ 309 310 if (!__is_instance(__VALUE(target))) 311 return 0; 312 313 /* Access the context of the callable and test if it is the type object. */ 314 315 return (!__ISNULL(targetcontext) && __test_specific_type(__VALUE(targetcontext), &__TYPE_CLASS_TYPE) && __is_type_instance(__VALUE(context))); 316 } 317 318 __attr __unwrap_callable(__attr callable) 319 { 320 __attr value = __check_and_load_via_object_null(__VALUE(callable), __ATTRPOS(__value__), __ATTRCODE(__value__)); 321 return __VALUE(value) ? value : callable; 322 } 323 324 __attr (*__get_function_unchecked(__attr target))() 325 { 326 return __load_via_object(__VALUE(__unwrap_callable(target)), __fn__).fn; 327 } 328 329 __attr (*__get_function(__attr context, __attr target))() 330 { 331 return __get_function_unwrapped(context, __unwrap_callable(target)); 332 } 333 334 __attr (*__get_function_unwrapped(__attr context, __attr target))() 335 { 336 /* Require null or instance contexts for functions and methods respectively, 337 or type instance contexts for type methods. */ 338 339 if (__ISNULL(context) || __is_instance(__VALUE(context)) || __type_method_invocation(context, target)) 340 return __get_function_member(target); 341 else 342 return __unbound_method; 343 } 344 345 __attr (*__get_function_member(__attr target))() 346 { 347 return __load_via_object(__VALUE(target), __fn__).fn; 348 } 349 350 __attr (*__check_and_get_function(__attr context, __attr target))() 351 { 352 return __check_and_get_function_unwrapped(context, __unwrap_callable(target)); 353 } 354 355 __attr (*__check_and_get_function_unwrapped(__attr context, __attr target))() 356 { 357 /* Require null or instance contexts for functions and methods respectively, 358 or type instance contexts for type methods. */ 359 360 if (__ISNULL(context) || __is_instance(__VALUE(context)) || __type_method_invocation(context, target)) 361 return __check_and_load_via_object__(__VALUE(target), __ATTRPOS(__fn__), __ATTRCODE(__fn__)).fn; 362 else 363 return __unbound_method; 364 } 365 366 /* Parameter position operations. */ 367 368 int __HASPARAM(const __ptable *ptable, int ppos, int pcode) 369 { 370 __param param; 371 372 if (ppos < ptable->size) 373 { 374 param = ptable->params[ppos]; 375 if (param.code == pcode) 376 return param.pos; 377 } 378 379 return -1; 380 } 381 382 /* Conversions. */ 383 384 __attr __CONTEXT_AS_VALUE(__attr attr) 385 { 386 return __check_and_load_via_object_null(__VALUE(attr), __ATTRPOS(__context__), __ATTRCODE(__context__)); 387 } 388 389 /* Type testing. */ 390 391 __ref __ISFUNC(__ref obj) 392 { 393 return __test_specific_instance(obj, &__FUNCTION_TYPE); 394 } 395 396 /* Attribute codes and positions for type objects. */ 397 398 unsigned int __TYPECODE(__ref obj) 399 { 400 return obj->table->attrs[obj->pos]; 401 } 402 403 unsigned int __TYPEPOS(__ref obj) 404 { 405 return obj->pos; 406 } 407 408 /* Memory allocation. */ 409 410 void *__ALLOCATE(size_t nmemb, size_t size) 411 { 412 void *ptr = GC_MALLOC(nmemb * size); /* sets memory to zero */ 413 if (ptr == NULL) 414 __raise_memory_error(); 415 return ptr; 416 } 417 418 void *__ALLOCATEIM(size_t nmemb, size_t size) 419 { 420 void *ptr = GC_MALLOC_ATOMIC(nmemb * size); 421 if (ptr == NULL) 422 __raise_memory_error(); 423 return ptr; 424 } 425 426 void *__REALLOCATE(void *ptr, size_t size) 427 { 428 void *nptr = GC_REALLOC(ptr, size); 429 if (nptr == NULL) 430 __raise_memory_error(); 431 return nptr; 432 } 433 434 /* Copying of structures. */ 435 436 __ref __COPY(__ref obj, int size) 437 { 438 __ref copy = (__ref) __ALLOCATE(1, size); 439 memcpy(copy, obj, size); 440 return copy; 441 } 442 443 /* Store an attribute in a target location. For targets that support value 444 replacement, a copied object is assigned when initialising the target. */ 445 446 __attr __set_attr(volatile __attr *target, __attr attr) 447 { 448 __ref obj; 449 int obj_size; 450 451 /* Value already replaced in target by an operation. */ 452 453 if (__REPLACING(attr)) 454 return __REPLACED(attr); 455 456 obj = __VALUE(attr); 457 458 /* Value is replaceable and should be copied to avoid inadvertent 459 sharing. */ 460 461 if ((obj != NULL) && __is_instance(obj)) 462 { 463 obj_size = __get_class(obj)->obj_size; 464 465 if (obj_size) 466 attr = __ATTRVALUE(__COPY(obj, obj_size)); 467 } 468 469 /* Set and return the attribute. */ 470 471 *target = attr; 472 return attr; 473 }