Lichen

templates/ops.c

1040:c8b0c2d6df5c
5 months ago Paul Boddie Merged changes from the value-replacement branch. value-replacement-for-wrapper
     1 /* Common operations.     2      3 Copyright (C) 2015, 2016, 2017, 2018, 2023 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    NOTE: Only floats and wrappers are currently supported for value replacement.   447          A special copy attribute could be employed to make this generic. */   448    449 __attr __set_attr(volatile __attr *target, __attr attr)   450 {   451     __ref obj;   452    453     /* Value already replaced in target by an operation. */   454    455     if (__REPLACING(attr))   456         return __REPLACED(attr);   457    458     obj = __VALUE(attr);   459    460     /* Value is replaceable and should be copied to avoid inadvertent   461        sharing. */   462    463     if ((obj != NULL) && __is_instance(obj))   464     {   465         if (__get_class(obj) == &__builtins___float_float)   466             attr = __ATTRVALUE(__COPY(obj, __INSTANCESIZE(__builtins___float_float)));   467         else if (__get_class(obj) == &__builtins___core_wrapper)   468             attr = __ATTRVALUE(__COPY(obj, __INSTANCESIZE(__builtins___core_wrapper)));   469     }   470    471     /* Set and return the attribute. */   472    473     *target = attr;   474     return attr;   475 }