Lichen

Annotated templates/ops.c

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