1 /* Common operations. 2 3 Copyright (C) 2015, 2016, 2017 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 "ops.h" 21 #include "progops.h" /* for raising errors */ 22 #include "progconsts.h" 23 #include "progtypes.h" 24 25 /* Direct access and manipulation of static objects. */ 26 27 __attr __load_static_ignore(__ref obj) 28 { 29 return (__attr) {.value=obj}; 30 } 31 32 __attr __load_static_replace(__ref context, __ref obj) 33 { 34 return __update_context(context, (__attr) {.value=obj}); 35 } 36 37 __attr __load_static_test(__ref context, __ref obj) 38 { 39 return __test_context(context, (__attr) {.value=obj}); 40 } 41 42 /* Direct retrieval operations, returning and setting attributes. */ 43 44 __attr __load_via_object__(__ref obj, int pos) 45 { 46 return obj->attrs[pos]; 47 } 48 49 __attr __load_via_class__(__ref obj, int pos) 50 { 51 return __load_via_object__(__get_class(obj), pos); 52 } 53 54 __attr __get_class_and_load__(__ref obj, int pos) 55 { 56 if (__is_instance(obj)) 57 return __load_via_class__(obj, pos); 58 else 59 return __load_via_object__(obj, pos); 60 } 61 62 /* Direct storage operations. */ 63 64 int __store_via_object__(__ref obj, int pos, __attr value) 65 { 66 obj->attrs[pos] = value; 67 return 1; 68 } 69 70 int __get_class_and_store__(__ref obj, int pos, __attr value) 71 { 72 /* Forbid class-relative assignments. */ 73 74 __raise_type_error(); 75 return 0; 76 } 77 78 /* Introspection. */ 79 80 int __is_instance(__ref obj) 81 { 82 return obj->pos == __INSTANCEPOS; 83 } 84 85 int __is_type_instance(__ref obj) 86 { 87 return __HASATTR(__get_class(obj), __TYPE_CLASS_POS, __TYPE_CLASS_CODE); 88 } 89 90 __ref __get_class(__ref obj) 91 { 92 return __load_via_object(obj, __class__).value; 93 } 94 95 __attr __get_class_attr(__ref obj) 96 { 97 return __load_via_object(obj, __class__); 98 } 99 100 /* Attribute testing operations. */ 101 102 __ref __test_specific_instance(__ref obj, __ref type) 103 { 104 return __get_class(obj) == type ? obj : 0; 105 } 106 107 __ref __test_specific_object(__ref obj, __ref type) 108 { 109 return __test_specific_type(obj, type) || __test_specific_instance(obj, type) ? obj : 0; 110 } 111 112 __ref __test_specific_type(__ref obj, __ref type) 113 { 114 return obj == type ? obj : 0; 115 } 116 117 __ref __test_common_instance__(__ref obj, int pos, int code) 118 { 119 return __HASATTR(__get_class(obj), pos, code) ? obj : 0; 120 } 121 122 __ref __test_common_object__(__ref obj, int pos, int code) 123 { 124 return __test_common_type__(obj, pos, code) || __test_common_instance__(obj, pos, code) ? obj : 0; 125 } 126 127 __ref __test_common_type__(__ref obj, int pos, int code) 128 { 129 return __HASATTR(obj, pos, code) ? obj : 0; 130 } 131 132 /* Attribute testing and retrieval operations. */ 133 134 __attr __check_and_load_via_object_null(__ref obj, int pos, int code) 135 { 136 if (__HASATTR(obj, pos, code)) 137 return __load_via_object__(obj, pos); 138 else 139 return __NULL; 140 } 141 142 __attr __check_and_load_via_class__(__ref obj, int pos, int code) 143 { 144 return __check_and_load_via_object__(__get_class(obj), pos, code); 145 } 146 147 __attr __check_and_load_via_object__(__ref obj, int pos, int code) 148 { 149 if (__HASATTR(obj, pos, code)) 150 return __load_via_object__(obj, pos); 151 152 __raise_type_error(); 153 return __NULL; 154 } 155 156 __attr __check_and_load_via_any__(__ref obj, int pos, int code) 157 { 158 __attr out = __check_and_load_via_object_null(obj, pos, code); 159 if (out.value == 0) 160 out = __check_and_load_via_class__(obj, pos, code); 161 return out; 162 } 163 164 /* Attribute testing and storage operations. */ 165 166 int __check_and_store_via_class__(__ref obj, int pos, int code, __attr value) 167 { 168 /* Forbid class-relative assignments. */ 169 170 __raise_type_error(); 171 return 0; 172 } 173 174 int __check_and_store_via_object__(__ref obj, int pos, int code, __attr value) 175 { 176 if (__HASATTR(obj, pos, code)) 177 { 178 __store_via_object__(obj, pos, value); 179 return 1; 180 } 181 182 /* No suitable attribute. */ 183 184 __raise_type_error(); 185 return 0; 186 } 187 188 int __check_and_store_via_any__(__ref obj, int pos, int code, __attr value) 189 { 190 if (__check_and_store_via_object__(obj, pos, code, value)) 191 return 1; 192 193 /* Forbid class-relative assignments. */ 194 195 __raise_type_error(); 196 return 0; 197 } 198 199 /* Context-related operations. */ 200 201 int __test_context_update(__ref context, __attr attr) 202 { 203 /* Return whether the context should be updated for the attribute. */ 204 205 __ref attrcontext = __CONTEXT_AS_VALUE(attr).value; 206 207 /* Preserve any existing null or instance context. */ 208 209 if ((attrcontext == 0) || __is_instance(attrcontext)) 210 return 0; 211 212 /* Test any instance context against the context employed by the 213 attribute. */ 214 215 if (__is_instance(context)) 216 { 217 /* Obtain the special class attribute position and code identifying the 218 attribute context's class, inspecting the context instance for 219 compatibility. */ 220 221 if (__test_common_instance__(context, __TYPEPOS(attrcontext), __TYPECODE(attrcontext))) 222 return 1; 223 else 224 __raise_type_error(); 225 } 226 227 /* Test for access to a type class attribute using a type instance. */ 228 229 if (__test_specific_type(attrcontext, &__TYPE_CLASS_TYPE) && __is_type_instance(context)) 230 return 1; 231 232 /* Otherwise, preserve the attribute as retrieved. */ 233 234 return 0; 235 } 236 237 __attr __test_context(__ref context, __attr attr) 238 { 239 /* Update the context or return the unchanged attribute. */ 240 241 if (__test_context_update(context, attr)) 242 return __update_context(context, attr); 243 else 244 return attr; 245 } 246 247 __attr __update_context(__ref context, __attr attr) 248 { 249 return __new_wrapper(context, attr); 250 } 251 252 __attr __test_context_revert(int target, __ref context, __attr attr, __ref contexts[]) 253 { 254 /* Revert the local context to that employed by the attribute if the 255 supplied context is not appropriate. */ 256 257 if (!__test_context_update(context, attr)) 258 contexts[target] = __CONTEXT_AS_VALUE(attr).value; 259 return attr; 260 } 261 262 __attr __test_context_static(int target, __ref context, __ref value, __ref contexts[]) 263 { 264 /* Set the local context to the specified context if appropriate. */ 265 266 if (__test_context_update(context, (__attr) {.value=value})) 267 contexts[target] = context; 268 return (__attr) {.value=value}; 269 } 270 271 /* Context testing for invocations. */ 272 273 int __type_method_invocation(__ref context, __attr target) 274 { 275 __ref targetcontext = __CONTEXT_AS_VALUE(target).value; 276 277 /* Require instances, not classes, where methods are function instances. */ 278 279 if (!__is_instance(target.value)) 280 return 0; 281 282 /* Access the context of the callable and test if it is the type object. */ 283 284 return ((targetcontext != 0) && __test_specific_type(targetcontext, &__TYPE_CLASS_TYPE) && __is_type_instance(context)); 285 } 286 287 __attr __unwrap_callable(__attr callable) 288 { 289 __attr value = __check_and_load_via_object_null(callable.value, __ATTRPOS(__value__), __ATTRCODE(__value__)); 290 return value.value ? value : callable; 291 } 292 293 __attr (*__get_function(__ref context, __attr target))(__attr[]) 294 { 295 target = __unwrap_callable(target); 296 297 /* Require null or instance contexts for functions and methods respectively, 298 or type instance contexts for type methods. */ 299 300 if ((context == 0) || __is_instance(context) || __type_method_invocation(context, target)) 301 return __load_via_object(target.value, __fn__).fn; 302 else 303 return __unbound_method; 304 } 305 306 __attr (*__check_and_get_function(__ref context, __attr target))(__attr[]) 307 { 308 target = __unwrap_callable(target); 309 310 /* Require null or instance contexts for functions and methods respectively, 311 or type instance contexts for type methods. */ 312 313 if ((context == 0) || __is_instance(context) || __type_method_invocation(context, target)) 314 return __check_and_load_via_object__(target.value, __ATTRPOS(__fn__), __ATTRCODE(__fn__)).fn; 315 else 316 return __unbound_method; 317 } 318 319 /* Basic structure tests. */ 320 321 int __WITHIN(__ref obj, int pos) 322 { 323 return pos < obj->table->size; 324 } 325 326 int __HASATTR(__ref obj, int pos, int code) 327 { 328 return __WITHIN(obj, pos) && (obj->table->attrs[pos] == code); 329 } 330 331 /* Parameter position operations. */ 332 333 int __HASPARAM(const __ptable *ptable, int ppos, int pcode) 334 { 335 __param param; 336 337 if (ppos < ptable->size) 338 { 339 param = ptable->params[ppos]; 340 if (param.code == pcode) 341 return param.pos; 342 } 343 344 return -1; 345 } 346 347 /* Conversions. */ 348 349 __attr __CONTEXT_AS_VALUE(__attr attr) 350 { 351 return __check_and_load_via_object_null(attr.value, __ATTRPOS(__context__), __ATTRCODE(__context__)); 352 } 353 354 /* Type testing. */ 355 356 __ref __ISFUNC(__ref obj) 357 { 358 return __test_specific_instance(obj, &__FUNCTION_TYPE); 359 } 360 361 int __ISNULL(__attr value) 362 { 363 return (value.value == 0); /* __NULL.value */ 364 } 365 366 /* Attribute codes and positions for type objects. */ 367 368 unsigned int __TYPECODE(__ref obj) 369 { 370 return obj->table->attrs[obj->pos]; 371 } 372 373 unsigned int __TYPEPOS(__ref obj) 374 { 375 return obj->pos; 376 } 377 378 /* Memory allocation. */ 379 380 void *__ALLOCATE(size_t nmemb, size_t size) 381 { 382 void *ptr = GC_MALLOC(nmemb * size); /* sets memory to zero */ 383 if (ptr == NULL) 384 __raise_memory_error(); 385 return ptr; 386 } 387 388 void *__REALLOCATE(void *ptr, size_t size) 389 { 390 void *nptr = GC_REALLOC(ptr, size); 391 if (nptr == NULL) 392 __raise_memory_error(); 393 return nptr; 394 } 395 396 /* Copying of structures. */ 397 398 __ref __COPY(__ref obj, int size) 399 { 400 __ref copy = (__ref) __ALLOCATE(1, size); 401 memcpy(copy, obj, size); 402 return copy; 403 }