/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 | } |