Coverage Report

Created: 2017-04-15 07:07

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