Coverage Report

Created: 2018-07-03 15:31

/home/travis/build/MoarVM/MoarVM/src/6model/parametric.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
/* Takes a type and sets it up as a parametric type, provided it's OK to do so. */
4
4
void MVM_6model_parametric_setup(MVMThreadContext *tc, MVMObject *type, MVMObject *parameterizer) {
5
4
    MVMSTable *st = STABLE(type);
6
4
7
4
    /* Ensure that the type is not already parametric or parameterized. */
8
4
    if (st->mode_flags & MVM_PARAMETRIC_TYPE)
9
1
        MVM_exception_throw_adhoc(tc, "This type is already parametric");
10
4
    if (st->mode_flags & MVM_PARAMETERIZED_TYPE)
11
0
        MVM_exception_throw_adhoc(tc, "Cannot make a parameterized type also be parametric");
12
4
13
4
    /* For now, we use a simple pairwise array, with parameters and the type
14
4
     * that is based on those parameters interleaved. It does make resolution
15
4
     * O(n), so we might like to do some hash in the future. */
16
4
    MVMROOT2(tc, st, parameterizer, {
17
4
        MVMObject *lookup = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray);
18
4
        MVM_ASSIGN_REF(tc, &(st->header), st->paramet.ric.lookup, lookup);
19
4
    });
20
4
21
4
     /* Store the parameterizer. (Note, we do this after the allocation
22
4
      * above, since the array allocation may cause GC, but we didn't mark
23
4
      * it as a parametric type yet so it wouldn't mark the parameterizer.) */
24
4
    MVM_ASSIGN_REF(tc, &(st->header), st->paramet.ric.parameterizer, parameterizer);
25
4
26
4
    /* Mark the type as parameterized. */
27
4
    st->mode_flags |= MVM_PARAMETRIC_TYPE;
28
4
}
29
30
/* Parameterize a type. Re-use an existing parameterization of there is one that
31
 * matches. Otherwise, run the parameterization creator. */
32
typedef struct {
33
    MVMObject   *parametric_type;
34
    MVMObject   *parameters;
35
    MVMRegister *result;
36
} ParameterizeReturnData;
37
7
static void finish_parameterizing(MVMThreadContext *tc, void *sr_data) {
38
7
    ParameterizeReturnData *prd = (ParameterizeReturnData *)sr_data;
39
7
    MVMObject *found;
40
7
41
7
    /* Mark parametric and stash required data. */
42
7
    MVMSTable *new_stable = STABLE(prd->result->o);
43
7
    MVM_ASSIGN_REF(tc, &(new_stable->header), new_stable->paramet.erized.parametric_type,
44
7
        prd->parametric_type);
45
7
    MVM_ASSIGN_REF(tc, &(new_stable->header), new_stable->paramet.erized.parameters,
46
7
        prd->parameters);
47
7
    new_stable->mode_flags |= MVM_PARAMETERIZED_TYPE;
48
7
49
7
    /* Add to lookup table. Multiple threads may race to do this, so after
50
7
     * taking the lock to serialize additions we re-check for a match. If we
51
7
     * don't find one, do a defensive copy here so that existing readers of
52
7
     * the table won't be bitten. */
53
7
    uv_mutex_lock(&tc->instance->mutex_parameterization_add);
54
7
    found = MVM_6model_parametric_try_find_parameterization(tc,
55
7
        prd->parametric_type->st, prd->parameters);
56
7
    if (found) {
57
0
        prd->result->o = found;
58
0
    }
59
7
    else {
60
7
        MVMObject *parameters = prd->parameters;
61
7
        MVMObject *parametric_type = prd->parametric_type;
62
7
        MVMROOT2(tc, parameters, parametric_type, {
63
7
            MVMObject *copy = MVM_repr_clone(tc, parametric_type->st->paramet.ric.lookup);
64
7
            MVM_repr_push_o(tc, copy, parameters);
65
7
            MVM_repr_push_o(tc, copy, prd->result->o);
66
7
            MVM_ASSIGN_REF(tc, &(parametric_type->st->header),
67
7
                parametric_type->st->paramet.ric.lookup, copy);
68
7
        });
69
7
    }
70
7
    uv_mutex_unlock(&tc->instance->mutex_parameterization_add);
71
7
72
7
    /* Clean up parametric return data, now we're finished with it. */
73
7
    MVM_free(prd);
74
7
}
75
0
static void mark_parameterize_sr_data(MVMThreadContext *tc, MVMFrame *frame, MVMGCWorklist *worklist) {
76
0
    ParameterizeReturnData *prd = (ParameterizeReturnData *)frame->extra->special_return_data;
77
0
    MVM_gc_worklist_add(tc, worklist, &(prd->parametric_type));
78
0
    MVM_gc_worklist_add(tc, worklist, &(prd->parameters));
79
0
}
80
void MVM_6model_parametric_parameterize(MVMThreadContext *tc, MVMObject *type, MVMObject *params,
81
9
                                        MVMRegister *result) {
82
9
    ParameterizeReturnData *prd;
83
9
    MVMObject *code, *found;
84
9
85
9
    /* Ensure we have a parametric type. */
86
9
    MVMSTable *st = STABLE(type);
87
9
    if (!(st->mode_flags & MVM_PARAMETRIC_TYPE))
88
0
        MVM_exception_throw_adhoc(tc, "This type is not parametric");
89
9
90
9
    /* Use an existing parameterization if we have it. */
91
9
    found = MVM_6model_parametric_try_find_parameterization(tc, st, params);
92
9
    if (found) {
93
2
        result->o = found;
94
2
        return;
95
2
    }
96
9
97
9
    /* It wasn't found; run parameterizer. */
98
7
    code = MVM_frame_find_invokee(tc, st->paramet.ric.parameterizer, NULL);
99
7
    prd  = MVM_malloc(sizeof(ParameterizeReturnData));
100
7
    prd->parametric_type                    = type;
101
7
    prd->parameters                         = params;
102
7
    prd->result                             = result;
103
7
    MVM_frame_special_return(tc, tc->cur_frame, finish_parameterizing, NULL,
104
7
        prd, mark_parameterize_sr_data);
105
7
    MVM_args_setup_thunk(tc, result, MVM_RETURN_OBJ, MVM_callsite_get_common(tc, MVM_CALLSITE_ID_TWO_OBJ));
106
7
    tc->cur_frame->args[0].o = st->WHAT;
107
7
    tc->cur_frame->args[1].o = params;
108
7
    STABLE(code)->invoke(tc, code, MVM_callsite_get_common(tc, MVM_CALLSITE_ID_TWO_OBJ), tc->cur_frame->args);
109
7
}
110
111
/* Try to find an existing parameterization of the specified type and
112
 * parameters. If none is found, returns NULL. */
113
18
MVMObject * MVM_6model_parametric_try_find_parameterization(MVMThreadContext *tc, MVMSTable *st, MVMObject *params) {
114
18
    MVMint64 i, j, num_lookups, params_elems;
115
18
    num_lookups  = MVM_repr_elems(tc, st->paramet.ric.lookup);
116
18
    params_elems = MVM_repr_elems(tc, params);
117
30
    for (i = 0; i < num_lookups; i += 2) {
118
16
        MVMObject *compare       = MVM_repr_at_pos_o(tc, st->paramet.ric.lookup, i);
119
16
        MVMint64   compare_elems = MVM_repr_elems(tc, compare);
120
16
        if (params_elems == compare_elems) {
121
16
            MVMint64 match = 1;
122
23
            for (j = 0; j < params_elems; j++) {
123
19
                MVMObject *want = MVM_repr_at_pos_o(tc, params, j);
124
19
                MVMObject *got  = MVM_repr_at_pos_o(tc, compare, j);
125
19
                if (want != got) {
126
12
                    match = 0;
127
12
                    break;
128
12
                }
129
19
            }
130
16
            if (match)
131
4
                return MVM_repr_at_pos_o(tc, st->paramet.ric.lookup, i + 1);
132
16
        }
133
16
    }
134
14
    return NULL;
135
18
}
136
137
/* If the passed type is a parameterized type, then returns the parametric
138
 * type it is based on. Otherwise, returns null. */
139
4
MVMObject * MVM_6model_parametric_type_parameterized(MVMThreadContext *tc, MVMObject *type) {
140
4
    MVMSTable *st = STABLE(type);
141
4
    if (st->mode_flags & MVM_PARAMETERIZED_TYPE)
142
3
        return st->paramet.erized.parametric_type;
143
4
    else
144
1
        return tc->instance->VMNull;
145
4
}
146
147
/* Provided this is a parameterized type, returns the array of type parameters. */
148
4
MVMObject * MVM_6model_parametric_type_parameters(MVMThreadContext *tc, MVMObject *type) {
149
4
    MVMSTable *st = STABLE(type);
150
4
    if (!(st->mode_flags & MVM_PARAMETERIZED_TYPE))
151
0
        MVM_exception_throw_adhoc(tc, "This type is not parameterized");
152
4
    return st->paramet.erized.parameters;
153
4
}
154
155
/* Provided this is a parameterized type, returns the type parameter at the specified index. */
156
5
MVMObject * MVM_6model_parametric_type_parameter_at(MVMThreadContext *tc, MVMObject *type, MVMint64 idx) {
157
5
    MVMSTable *st = STABLE(type);
158
5
    if (!(st->mode_flags & MVM_PARAMETERIZED_TYPE))
159
0
        MVM_exception_throw_adhoc(tc, "This type is not parameterized");
160
5
    return MVM_repr_at_pos_o(tc, st->paramet.erized.parameters, idx);
161
5
}