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