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, __pos___class__).value; 93 } 94 95 __attr __get_class_attr(__ref obj) 96 { 97 return __load_via_object(obj, __pos___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 if (__test_common_instance(context, __TYPEPOS(attrcontext), __TYPECODE(attrcontext))) 218 return 1; 219 else 220 __raise_type_error(); 221 } 222 223 /* Test for access to a type class attribute using a type instance. */ 224 225 if (__test_specific_type(attrcontext, &__TYPE_CLASS_TYPE) && __is_type_instance(context)) 226 return 1; 227 228 /* Otherwise, preserve the attribute as retrieved. */ 229 230 return 0; 231 } 232 233 __attr __test_context(__ref context, __attr attr) 234 { 235 /* Update the context or return the unchanged attribute. */ 236 237 if (__test_context_update(context, attr)) 238 return __update_context(context, attr); 239 else 240 return attr; 241 } 242 243 __attr __update_context(__ref context, __attr attr) 244 { 245 return __new_wrapper(context, attr); 246 } 247 248 /* Context testing for invocations. */ 249 250 int __type_method_invocation(__ref context, __attr target) 251 { 252 __ref targetcontext = __CONTEXT_AS_VALUE(target).value; 253 254 /* Require instances, not classes, where methods are function instances. */ 255 256 if (!__is_instance(target.value)) 257 return 0; 258 259 /* Access the context of the callable and test if it is the type object. */ 260 261 return ((targetcontext != 0) && __test_specific_type(targetcontext, &__TYPE_CLASS_TYPE) && __is_type_instance(context)); 262 } 263 264 __attr __unwrap_callable(__attr callable) 265 { 266 __attr value = __check_and_load_via_object_null(callable.value, __ATTRPOS(__value__), __ATTRCODE(__value__)); 267 return value.value ? value : callable; 268 } 269 270 __attr (*__get_function(__ref context, __attr target))(__attr[]) 271 { 272 target = __unwrap_callable(target); 273 274 /* Require null or instance contexts for functions and methods respectively, 275 or type instance contexts for type methods. */ 276 277 if ((context == 0) || __is_instance(context) || __type_method_invocation(context, target)) 278 return __load_via_object(target.value, __ATTRPOS(__fn__)).fn; 279 else 280 return __unbound_method; 281 } 282 283 __attr (*__check_and_get_function(__ref context, __attr target))(__attr[]) 284 { 285 target = __unwrap_callable(target); 286 287 /* Require null or instance contexts for functions and methods respectively, 288 or type instance contexts for type methods. */ 289 290 if ((context == 0) || __is_instance(context) || __type_method_invocation(context, target)) 291 return __check_and_load_via_object(target.value, __ATTRPOS(__fn__), __ATTRCODE(__fn__)).fn; 292 else 293 return __unbound_method; 294 } 295 296 /* Basic structure tests. */ 297 298 int __WITHIN(__ref obj, int pos) 299 { 300 return pos < obj->table->size; 301 } 302 303 int __HASATTR(__ref obj, int pos, int code) 304 { 305 return __WITHIN(obj, pos) && (obj->table->attrs[pos] == code); 306 } 307 308 /* Parameter position operations. */ 309 310 int __HASPARAM(const __ptable *ptable, int ppos, int pcode) 311 { 312 __param param; 313 314 if (ppos < ptable->size) 315 { 316 param = ptable->params[ppos]; 317 if (param.code == pcode) 318 return param.pos; 319 } 320 321 return -1; 322 } 323 324 /* Conversions. */ 325 326 __attr __CONTEXT_AS_VALUE(__attr attr) 327 { 328 return __check_and_load_via_object_null(attr.value, __ATTRPOS(__context__), __ATTRCODE(__context__)); 329 } 330 331 /* Type testing. */ 332 333 __ref __ISFUNC(__ref obj) 334 { 335 return __test_specific_instance(obj, &__FUNCTION_TYPE); 336 } 337 338 int __ISNULL(__attr value) 339 { 340 return (value.value == 0); /* __NULL.value */ 341 } 342 343 /* Attribute codes and positions for type objects. */ 344 345 unsigned int __TYPECODE(__ref obj) 346 { 347 return obj->table->attrs[obj->pos]; 348 } 349 350 unsigned int __TYPEPOS(__ref obj) 351 { 352 return obj->pos; 353 } 354 355 /* Memory allocation. */ 356 357 void *__ALLOCATE(size_t nmemb, size_t size) 358 { 359 void *ptr = GC_MALLOC(nmemb * size); /* sets memory to zero */ 360 if (ptr == NULL) 361 __raise_memory_error(); 362 return ptr; 363 } 364 365 void *__REALLOCATE(void *ptr, size_t size) 366 { 367 void *nptr = GC_REALLOC(ptr, size); 368 if (nptr == NULL) 369 __raise_memory_error(); 370 return nptr; 371 } 372 373 /* Copying of structures. */ 374 375 __ref __COPY(__ref obj, int size) 376 { 377 __ref copy = (__ref) __ALLOCATE(1, size); 378 memcpy(copy, obj, size); 379 return copy; 380 }