Coverage Report

Created: 2018-07-03 15:31

/home/travis/build/MoarVM/MoarVM/src/6model/6model.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
/* Gets the HOW (meta-object), which may be lazily deserialized. */
4
1.56M
MVMObject * MVM_6model_get_how(MVMThreadContext *tc, MVMSTable *st) {
5
1.56M
    MVMObject *HOW = st->HOW;
6
1.56M
    if (!HOW)
7
5.04k
        st->HOW = HOW = MVM_sc_get_object(tc, st->HOW_sc, st->HOW_idx);
8
1.56M
    return HOW;
9
1.56M
}
10
11
/* Gets the HOW (meta-object), which may be lazily deserialized, through the
12
 * STable of the passed object. */
13
19
MVMObject * MVM_6model_get_how_obj(MVMThreadContext *tc, MVMObject *obj) {
14
19
    return MVM_6model_get_how(tc, STABLE(obj));
15
19
}
16
17
/* Obtains the method cache, lazily deserializing if it needed. */
18
39.3M
static MVMObject * get_method_cache(MVMThreadContext *tc, MVMSTable *st) {
19
39.3M
    if (!st->method_cache)
20
26.9M
        MVM_serialization_finish_deserialize_method_cache(tc, st);
21
39.3M
    return st->method_cache;
22
39.3M
}
23
24
/* Locates a method by name, checking in the method cache only. */
25
27.8M
MVMObject * MVM_6model_find_method_cache_only(MVMThreadContext *tc, MVMObject *obj, MVMString *name) {
26
27.8M
    MVMObject *cache;
27
27.8M
28
27.8M
    MVMROOT(tc, name, {
29
27.8M
        cache = get_method_cache(tc, STABLE(obj));
30
27.8M
    });
31
27.8M
32
27.8M
    if (cache && IS_CONCRETE(cache))
33
894k
        return MVM_repr_at_key_o(tc, cache, name);
34
26.9M
    return NULL;
35
27.8M
}
36
37
/* Locates a method by name. Returns the method if it exists, or throws an
38
 * exception if it can not be found. */
39
typedef struct {
40
    MVMObject   *obj;
41
    MVMString   *name;
42
    MVMRegister *res;
43
    MVMint64     throw_if_not_found;
44
} FindMethodSRData;
45
10
static void die_over_missing_method(MVMThreadContext *tc, MVMObject *obj, MVMString *name) {
46
10
    MVMObject *handler = MVM_hll_current(tc)->method_not_found_error;
47
10
    if (!MVM_is_null(tc, handler)) {
48
2
        MVMCallsite *methnotfound_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_METH_NOT_FOUND);
49
2
        handler = MVM_frame_find_invokee(tc, handler, NULL);
50
2
        MVM_args_setup_thunk(tc, NULL, MVM_RETURN_VOID, methnotfound_callsite);
51
2
        tc->cur_frame->args[0].o = obj;
52
2
        tc->cur_frame->args[1].s = name;
53
2
        STABLE(handler)->invoke(tc, handler, methnotfound_callsite, tc->cur_frame->args);
54
2
        return;
55
2
    }
56
8
    else {
57
8
        char *c_name = MVM_string_utf8_encode_C_string(tc, name);
58
8
        char *waste[] = { c_name, NULL };
59
8
        MVM_exception_throw_adhoc_free(tc, waste,
60
8
            "Cannot find method '%s' on object of type %s",
61
8
            c_name, MVM_6model_get_debug_name(tc, obj));
62
8
    }
63
10
}
64
6
static void late_bound_find_method_return(MVMThreadContext *tc, void *sr_data) {
65
6
    FindMethodSRData *fm = (FindMethodSRData *)sr_data;
66
6
    if (MVM_is_null(tc, fm->res->o) || !IS_CONCRETE(fm->res->o)) {
67
2
        if (fm->throw_if_not_found) {
68
2
            MVMObject *obj  = fm->obj;
69
2
            MVMString *name = fm->name;
70
2
            MVM_free(fm);
71
2
            die_over_missing_method(tc, obj, name);
72
2
        }
73
0
        else {
74
0
            fm->res->o = tc->instance->VMNull;
75
0
            MVM_free(fm);
76
0
        }
77
2
    }
78
4
    else {
79
4
        MVM_free(fm);
80
4
    }
81
6
}
82
0
static void mark_find_method_sr_data(MVMThreadContext *tc, MVMFrame *frame, MVMGCWorklist *worklist) {
83
0
    FindMethodSRData *fm = (FindMethodSRData *)frame->extra->special_return_data;
84
0
    MVM_gc_worklist_add(tc, worklist, &fm->obj);
85
0
    MVM_gc_worklist_add(tc, worklist, &fm->name);
86
0
}
87
void MVM_6model_find_method(MVMThreadContext *tc, MVMObject *obj, MVMString *name,
88
11.0M
                            MVMRegister *res, MVMint64 throw_if_not_found) {
89
11.0M
    MVMObject *cache = NULL, *HOW = NULL, *find_method = NULL, *code = NULL;
90
11.0M
    MVMCallsite *findmeth_callsite = NULL;
91
11.0M
92
11.0M
    if (MVM_is_null(tc, obj)) {
93
0
        if (throw_if_not_found) {
94
0
            char *c_name  = MVM_string_utf8_encode_C_string(tc, name);
95
0
            char *waste[] = { c_name, NULL };
96
0
            MVM_exception_throw_adhoc_free(tc, waste,
97
0
                "Cannot call method '%s' on a null object",
98
0
                 c_name);
99
0
        }
100
0
        else {
101
0
            res->o = tc->instance->VMNull;
102
0
            return;
103
0
        }
104
0
    }
105
11.0M
106
11.0M
    /* First try to find it in the cache. If we find it, we have a result.
107
11.0M
     * If we don't find it, but the cache is authoritative, then error. */
108
11.0M
    MVMROOT2(tc, obj, name, {
109
11.0M
        cache = get_method_cache(tc, STABLE(obj));
110
11.0M
    });
111
11.0M
112
11.0M
    if (cache && IS_CONCRETE(cache)) {
113
11.0M
        MVMObject *meth = MVM_repr_at_key_o(tc, cache, name);
114
11.0M
        if (!MVM_is_null(tc, meth)) {
115
10.8M
            res->o = meth;
116
10.8M
            return;
117
10.8M
        }
118
249k
        if (STABLE(obj)->mode_flags & MVM_METHOD_CACHE_AUTHORITATIVE) {
119
249k
            if (throw_if_not_found)
120
8
                die_over_missing_method(tc, obj, name);
121
249k
            else
122
249k
                res->o = tc->instance->VMNull;
123
249k
            return;
124
249k
        }
125
249k
    }
126
11.0M
127
11.0M
    /* Otherwise, need to call the find_method method. We make the assumption
128
11.0M
     * that the invocant's meta-object's type is composed. */
129
7
    MVMROOT3(tc, obj, name, HOW, {
130
7
       HOW = MVM_6model_get_how(tc, STABLE(obj));
131
7
       find_method = MVM_6model_find_method_cache_only(tc, HOW,
132
7
            tc->instance->str_consts.find_method);
133
7
    });
134
7
135
7
    if (MVM_is_null(tc, find_method)) {
136
1
        if (throw_if_not_found) {
137
1
            char *c_name  = MVM_string_utf8_encode_C_string(tc, name);
138
1
            char *waste[] = { c_name, NULL };
139
1
            MVM_exception_throw_adhoc_free(tc, waste,
140
1
                "Cannot find method '%s': no method cache and no .^find_method",
141
1
                 c_name);
142
1
        }
143
0
        else {
144
0
            res->o = tc->instance->VMNull;
145
0
            return;
146
0
        }
147
1
    }
148
7
149
7
    /* Set up the call, using the result register as the target. */
150
7
    code = MVM_frame_find_invokee(tc, find_method, NULL);
151
7
    findmeth_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_FIND_METHOD);
152
7
    MVM_args_setup_thunk(tc, res, MVM_RETURN_OBJ, findmeth_callsite);
153
7
    {
154
7
        FindMethodSRData *fm = MVM_malloc(sizeof(FindMethodSRData));
155
7
        fm->obj  = obj;
156
7
        fm->name = name;
157
7
        fm->res  = res;
158
7
        fm->throw_if_not_found = throw_if_not_found;
159
7
        MVM_frame_special_return(tc, tc->cur_frame, late_bound_find_method_return,
160
7
            NULL, fm, mark_find_method_sr_data);
161
7
    }
162
7
    tc->cur_frame->args[0].o = HOW;
163
7
    tc->cur_frame->args[1].o = obj;
164
7
    tc->cur_frame->args[2].s = name;
165
7
    STABLE(code)->invoke(tc, code, findmeth_callsite, tc->cur_frame->args);
166
7
}
167
168
MVMint32 MVM_6model_find_method_spesh(MVMThreadContext *tc, MVMObject *obj, MVMString *name,
169
20.9k
                                      MVMint32 ss_idx, MVMRegister *res) {
170
20.9k
    MVMObject *meth;
171
20.9k
172
20.9k
    /* Missed mono-morph; try cache-only lookup. */
173
20.9k
174
20.9k
    MVMROOT2(tc, obj, name, {
175
20.9k
        meth = MVM_6model_find_method_cache_only(tc, obj, name);
176
20.9k
    });
177
20.9k
178
20.9k
    if (!MVM_is_null(tc, meth)) {
179
20.9k
        /* Got it; cache. Must be careful due to threads
180
20.9k
         * reading, races, etc. */
181
20.9k
        MVMStaticFrame *sf = tc->cur_frame->static_info;
182
20.9k
        uv_mutex_lock(&tc->instance->mutex_spesh_install);
183
20.9k
        if (!tc->cur_frame->effective_spesh_slots[ss_idx + 1]) {
184
4.56k
            MVMStaticFrameSpesh *spesh = sf->body.spesh;
185
4.56k
            MVM_ASSIGN_REF(tc, &(spesh->common.header),
186
4.56k
                           tc->cur_frame->effective_spesh_slots[ss_idx + 1],
187
4.56k
                           (MVMCollectable *)meth);
188
4.56k
            MVM_barrier();
189
4.56k
            MVM_ASSIGN_REF(tc, &(spesh->common.header),
190
4.56k
                           tc->cur_frame->effective_spesh_slots[ss_idx],
191
4.56k
                           (MVMCollectable *)STABLE(obj));
192
4.56k
        }
193
20.9k
        uv_mutex_unlock(&tc->instance->mutex_spesh_install);
194
20.9k
        res->o = meth;
195
20.9k
        return 0;
196
20.9k
    }
197
0
    else {
198
0
        /* Fully late-bound. */
199
0
        MVM_6model_find_method(tc, obj, name, res, 1);
200
0
        return 1;
201
0
    }
202
20.9k
}
203
204
205
/* Locates a method by name. Returns 1 if it exists; otherwise 0. */
206
2
static void late_bound_can_return(MVMThreadContext *tc, void *sr_data) {
207
2
    /* Transform to an integer result. */
208
2
    MVMRegister *reg = (MVMRegister *)sr_data;
209
1
    reg->i64 = !MVM_is_null(tc, reg->o) && IS_CONCRETE(reg->o) ? 1 : 0;
210
2
}
211
212
467k
MVMint64 MVM_6model_can_method_cache_only(MVMThreadContext *tc, MVMObject *obj, MVMString *name) {
213
467k
    MVMObject *cache;
214
467k
215
467k
    if (MVM_is_null(tc, obj)) {
216
0
        char *c_name = MVM_string_utf8_encode_C_string(tc, name);
217
0
        char *waste[] = { c_name, NULL };
218
0
        MVM_exception_throw_adhoc_free(tc, waste,
219
0
            "Cannot look for method '%s' on a null object",
220
0
             c_name);
221
0
    }
222
467k
223
467k
    /* Consider the method cache. */
224
467k
225
467k
    MVMROOT2(tc, obj, name, {
226
467k
        cache = get_method_cache(tc, STABLE(obj));
227
467k
    });
228
467k
229
467k
    if (cache && IS_CONCRETE(cache)) {
230
467k
        MVMObject *meth = MVM_repr_at_key_o(tc, cache, name);
231
467k
        if (!MVM_is_null(tc, meth)) {
232
440k
            return 1;
233
440k
        }
234
26.9k
        if (STABLE(obj)->mode_flags & MVM_METHOD_CACHE_AUTHORITATIVE) {
235
26.9k
            return 0;
236
26.9k
        }
237
26.9k
    }
238
4
    return -1;
239
467k
}
240
241
467k
void MVM_6model_can_method(MVMThreadContext *tc, MVMObject *obj, MVMString *name, MVMRegister *res) {
242
467k
    MVMObject *HOW = NULL, *find_method = NULL, *code = NULL;
243
467k
    MVMCallsite *findmeth_callsite = NULL;
244
467k
245
467k
    MVMint64 can_cached;
246
467k
247
467k
    MVMROOT2(tc, obj, name, {
248
467k
        can_cached = MVM_6model_can_method_cache_only(tc, obj, name);
249
467k
    });
250
467k
251
467k
    if (can_cached == 0 || can_cached == 1) {
252
467k
        res->i64 = can_cached;
253
467k
        return;
254
467k
    }
255
467k
256
467k
    /* If no method in cache and the cache is not authoritative, need to make
257
467k
     * a late-bound call to find_method. */
258
4
    MVMROOT3(tc, obj, name, HOW, {
259
4
        HOW = MVM_6model_get_how(tc, STABLE(obj));
260
4
        find_method = MVM_6model_find_method_cache_only(tc, HOW,
261
4
            tc->instance->str_consts.find_method);
262
4
    });
263
4
264
4
    if (MVM_is_null(tc, find_method)) {
265
2
        /* This'll count as a "no"... */
266
2
        res->i64 = 0;
267
2
        return;
268
2
    }
269
4
270
4
    /* Set up the call, using the result register as the target. A little bad
271
4
     * as we're really talking about     */
272
2
    code = MVM_frame_find_invokee(tc, find_method, NULL);
273
2
    findmeth_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_FIND_METHOD);
274
2
    MVM_args_setup_thunk(tc, res, MVM_RETURN_OBJ, findmeth_callsite);
275
2
    MVM_frame_special_return(tc, tc->cur_frame, late_bound_can_return, NULL, res, NULL);
276
2
    tc->cur_frame->args[0].o = HOW;
277
2
    tc->cur_frame->args[1].o = obj;
278
2
    tc->cur_frame->args[2].s = name;
279
2
    STABLE(code)->invoke(tc, code, findmeth_callsite, tc->cur_frame->args);
280
2
}
281
282
/* Checks if an object has a given type, delegating to the type_check or
283
 * accepts_type methods as needed. */
284
4
static void do_accepts_type_check(MVMThreadContext *tc, MVMObject *obj, MVMObject *type, MVMRegister *res) {
285
4
    MVMObject *HOW = NULL, *meth = NULL;
286
4
287
4
    MVMROOT3(tc, obj, type, HOW, {
288
4
        HOW = MVM_6model_get_how(tc, STABLE(type));
289
4
        meth = MVM_6model_find_method_cache_only(tc, HOW,
290
4
            tc->instance->str_consts.accepts_type);
291
4
    });
292
4
293
4
    if (!MVM_is_null(tc, meth)) {
294
4
        /* Set up the call, using the result register as the target. */
295
4
        MVMObject *code = MVM_frame_find_invokee(tc, meth, NULL);
296
4
        MVMCallsite *typecheck_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_TYPECHECK);
297
4
298
4
        MVM_args_setup_thunk(tc, res, MVM_RETURN_INT, typecheck_callsite);
299
4
        tc->cur_frame->args[0].o = HOW;
300
4
        tc->cur_frame->args[1].o = type;
301
4
        tc->cur_frame->args[2].o = obj;
302
4
        STABLE(code)->invoke(tc, code, typecheck_callsite, tc->cur_frame->args);
303
4
        return;
304
4
    }
305
0
    else {
306
0
        MVM_exception_throw_adhoc(tc,
307
0
            "Expected 'accepts_type' method, but none found in meta-object");
308
0
    }
309
4
}
310
typedef struct {
311
    MVMObject   *obj;
312
    MVMObject   *type;
313
    MVMRegister *res;
314
} AcceptsTypeSRData;
315
316
3
static void accepts_type_sr(MVMThreadContext *tc, void *sr_data) {
317
3
    AcceptsTypeSRData *atd = (AcceptsTypeSRData *)sr_data;
318
3
    MVMObject   *obj  = atd->obj;
319
3
    MVMObject   *type = atd->type;
320
3
    MVMRegister *res  = atd->res;
321
3
    MVM_free(atd);
322
3
    if (!res->i64)
323
2
        do_accepts_type_check(tc, obj, type, res);
324
3
}
325
326
0
static void mark_sr_data(MVMThreadContext *tc, MVMFrame *frame, MVMGCWorklist *worklist) {
327
0
    AcceptsTypeSRData *atd = (AcceptsTypeSRData *)frame->extra->special_return_data;
328
0
    MVM_gc_worklist_add(tc, worklist, &atd->obj);
329
0
    MVM_gc_worklist_add(tc, worklist, &atd->type);
330
0
}
331
332
1.44M
void MVM_6model_istype(MVMThreadContext *tc, MVMObject *obj, MVMObject *type, MVMRegister *res) {
333
1.44M
    MVMObject **cache;
334
1.44M
    MVMSTable  *st;
335
1.44M
    MVMint64    mode;
336
1.44M
337
1.44M
    /* Null never type-checks. */
338
1.44M
    if (MVM_is_null(tc, obj)) {
339
1.69k
        res->i64 = 0;
340
1.69k
        return;
341
1.69k
    }
342
1.44M
343
1.44M
    st    = STABLE(obj);
344
1.44M
    mode  = STABLE(type)->mode_flags & MVM_TYPE_CHECK_CACHE_FLAG_MASK;
345
1.44M
    cache = st->type_check_cache;
346
1.44M
    if (cache) {
347
1.02M
        /* We have the cache, so just look for the type object we
348
1.02M
         * want to be in there. */
349
1.02M
        MVMint64 elems = STABLE(obj)->type_check_cache_length;
350
1.02M
        MVMint64 i;
351
3.12M
        for (i = 0; i < elems; i++) {
352
2.70M
            if (cache[i] == type) {
353
604k
                res->i64 = 1;
354
604k
                return;
355
604k
            }
356
2.70M
        }
357
1.02M
358
1.02M
        /* If the type check cache is definitive, we're done. */
359
422k
        if ((mode & MVM_TYPE_CHECK_CACHE_THEN_METHOD) == 0 &&
360
422k
            (mode & MVM_TYPE_CHECK_NEEDS_ACCEPTS) == 0) {
361
422k
            res->i64 = 0;
362
422k
            return;
363
422k
        }
364
422k
    }
365
1.44M
366
1.44M
    /* If we get here, need to call .^type_check on the value we're
367
1.44M
     * checking, unless it's an accepts check. */
368
418k
    if (!cache || (mode & MVM_TYPE_CHECK_CACHE_THEN_METHOD)) {
369
418k
        MVMObject *HOW = NULL, *meth = NULL;
370
418k
371
418k
        MVMROOT3(tc, obj, type, HOW, {
372
418k
            HOW = MVM_6model_get_how(tc, st);
373
418k
            meth = MVM_6model_find_method_cache_only(tc, HOW,
374
418k
                tc->instance->str_consts.type_check);
375
418k
        });
376
418k
        if (!MVM_is_null(tc, meth)) {
377
6
            /* Set up the call, using the result register as the target. */
378
6
            MVMObject *code = MVM_frame_find_invokee(tc, meth, NULL);
379
6
            MVMCallsite *typecheck_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_TYPECHECK);
380
6
381
6
            MVM_args_setup_thunk(tc, res, MVM_RETURN_INT, typecheck_callsite);
382
6
            tc->cur_frame->args[0].o = HOW;
383
6
            tc->cur_frame->args[1].o = obj;
384
6
            tc->cur_frame->args[2].o = type;
385
6
            if (mode & MVM_TYPE_CHECK_NEEDS_ACCEPTS) {
386
3
                AcceptsTypeSRData *atd = MVM_malloc(sizeof(AcceptsTypeSRData));
387
3
                atd->obj = obj;
388
3
                atd->type = type;
389
3
                atd->res = res;
390
3
                MVM_frame_special_return(tc, tc->cur_frame, accepts_type_sr, NULL,
391
3
                    atd, mark_sr_data);
392
3
            }
393
6
            STABLE(code)->invoke(tc, code, typecheck_callsite, tc->cur_frame->args);
394
6
            return;
395
6
        }
396
418k
    }
397
418k
398
418k
    /* If the flag to call .accepts_type on the target value is set, do so. */
399
418k
    if (mode & MVM_TYPE_CHECK_NEEDS_ACCEPTS) {
400
2
        do_accepts_type_check(tc, obj, type, res);
401
2
    }
402
418k
    else {
403
418k
        /* If all else fails... */
404
418k
        res->i64 = 0;
405
418k
    }
406
418k
}
407
408
/* Checks if an object has a given type, using the cache only. */
409
0
MVMint64 MVM_6model_istype_cache_only(MVMThreadContext *tc, MVMObject *obj, MVMObject *type) {
410
0
    if (!MVM_is_null(tc, obj)) {
411
0
        MVMuint16 i, elems = STABLE(obj)->type_check_cache_length;
412
0
        MVMObject  **cache = STABLE(obj)->type_check_cache;
413
0
        if (cache)
414
0
            for (i = 0; i < elems; i++) {
415
0
                if (cache[i] == type)
416
0
                    return 1;
417
0
            }
418
0
    }
419
0
420
0
    return 0;
421
0
}
422
423
/* Tries to do a type check using the cache. If the type is in the cache, then
424
 * result will be set to a true value and a true value will be returned. If it
425
 * is not in the cache and the cache is authoritative, then we know the answer
426
 * too; result is set to zero and a true value is returned. Otherwise, we can
427
 * not tell and a false value is returned and result is undefined. */
428
156
MVMint64 MVM_6model_try_cache_type_check(MVMThreadContext *tc, MVMObject *obj, MVMObject *type, MVMint32 *result) {
429
156
    if (!MVM_is_null(tc, obj)) {
430
156
        MVMuint16 i, elems = STABLE(obj)->type_check_cache_length;
431
156
        MVMObject  **cache = STABLE(obj)->type_check_cache;
432
156
        if (cache) {
433
605
            for (i = 0; i < elems; i++) {
434
506
                if (cache[i] == type) {
435
20
                    *result = 1;
436
20
                    return 1;
437
20
                }
438
506
            }
439
99
            if ((STABLE(obj)->mode_flags & MVM_TYPE_CHECK_CACHE_THEN_METHOD) == 0 &&
440
99
                (STABLE(type)->mode_flags & MVM_TYPE_CHECK_NEEDS_ACCEPTS) == 0) {
441
99
                *result = 0;
442
99
                return 1;
443
99
            }
444
99
        }
445
156
    }
446
37
    return 0;
447
156
}
448
449
/* Default invoke function on STables; for non-invokable objects */
450
0
void MVM_6model_invoke_default(MVMThreadContext *tc, MVMObject *invokee, MVMCallsite *callsite, MVMRegister *args) {
451
0
    MVM_exception_throw_adhoc(tc, "Cannot invoke this object (REPR: %s; %s)", REPR(invokee)->name, MVM_6model_get_debug_name(tc, invokee));
452
0
}
453
454
/* Clean up STable memory. */
455
0
void MVM_6model_stable_gc_free(MVMThreadContext *tc, MVMSTable *st) {
456
0
    /* First have it free its repr_data if it wants. */
457
0
    if (st->REPR->gc_free_repr_data)
458
0
        st->REPR->gc_free_repr_data(tc, st);
459
0
460
0
    /* free various storage. */
461
0
    MVM_free(st->type_check_cache);
462
0
    if (st->container_spec && st->container_spec->gc_free_data)
463
0
        st->container_spec->gc_free_data(tc, st);
464
0
    MVM_free(st->invocation_spec);
465
0
    MVM_free(st->boolification_spec);
466
0
    MVM_free(st->debug_name);
467
0
}
468
469
/* Get the next type cache ID for a newly created STable. */
470
32.3k
MVMuint64 MVM_6model_next_type_cache_id(MVMThreadContext *tc) {
471
32.3k
    return (MVMuint64)MVM_add(&tc->instance->cur_type_cache_id, MVM_TYPE_CACHE_ID_INCR) + MVM_TYPE_CACHE_ID_INCR;
472
32.3k
}
473
474
1
void MVM_6model_never_repossess(MVMThreadContext *tc, MVMObject *obj) {
475
1
    obj->header.flags |= MVM_CF_NEVER_REPOSSESS;
476
1
}