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