Coverage Report

Created: 2017-04-15 07:07

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