/home/travis/build/MoarVM/MoarVM/src/6model/reprs/MVMIter.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | |
3 | | /* This representation's function pointer table. */ |
4 | | static const MVMREPROps MVMIter_this_repr; |
5 | | |
6 | | /* Creates a new type object of this representation, and associates it with |
7 | | * the given HOW. */ |
8 | 130 | static MVMObject * type_object_for(MVMThreadContext *tc, MVMObject *HOW) { |
9 | 130 | MVMSTable *st = MVM_gc_allocate_stable(tc, &MVMIter_this_repr, HOW); |
10 | 130 | |
11 | 130 | MVMROOT(tc, st, { |
12 | 130 | MVMObject *obj = MVM_gc_allocate_type_object(tc, st); |
13 | 130 | MVM_ASSIGN_REF(tc, &(st->header), st->WHAT, obj); |
14 | 130 | st->size = sizeof(MVMIter); |
15 | 130 | }); |
16 | 130 | |
17 | 130 | return st->WHAT; |
18 | 130 | } |
19 | | |
20 | | /* Copies the body of one object to another. */ |
21 | 0 | static void copy_to(MVMThreadContext *tc, MVMSTable *st, void *src, MVMObject *dest_root, void *dest) { |
22 | 0 | MVM_exception_throw_adhoc(tc, "Cannot copy object with representation VMIter"); |
23 | 0 | } |
24 | | |
25 | | /* Adds held objects to the GC worklist. */ |
26 | 524 | static void gc_mark(MVMThreadContext *tc, MVMSTable *st, void *data, MVMGCWorklist *worklist) { |
27 | 524 | MVMIterBody *body = (MVMIterBody *)data; |
28 | 524 | MVM_gc_worklist_add(tc, worklist, &body->target); |
29 | 524 | } |
30 | | |
31 | | /* Called by the VM in order to free memory associated with this object. */ |
32 | 384k | static void gc_free(MVMThreadContext *tc, MVMObject *obj) { |
33 | 384k | } |
34 | | |
35 | | static const MVMStorageSpec storage_spec = { |
36 | | MVM_STORAGE_SPEC_REFERENCE, /* inlineable */ |
37 | | 0, /* bits */ |
38 | | 0, /* align */ |
39 | | MVM_STORAGE_SPEC_BP_NONE, /* boxed_primitive */ |
40 | | 0, /* can_box */ |
41 | | 0, /* is_unsigned */ |
42 | | }; |
43 | | |
44 | | /* Gets the storage specification for this representation. */ |
45 | 6.12k | static const MVMStorageSpec * get_storage_spec(MVMThreadContext *tc, MVMSTable *st) { |
46 | 6.12k | return &storage_spec; |
47 | 6.12k | } |
48 | | |
49 | 1.09M | static void shift(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMRegister *value, MVMuint16 kind) { |
50 | 1.09M | MVMIterBody *body = (MVMIterBody *)data; |
51 | 1.09M | MVMObject *target = body->target; |
52 | 1.09M | switch (body->mode) { |
53 | 386k | case MVM_ITER_MODE_ARRAY: |
54 | 386k | body->array_state.index++; |
55 | 386k | if (body->array_state.index >= body->array_state.limit) |
56 | 0 | MVM_exception_throw_adhoc(tc, "Iteration past end of iterator"); |
57 | 386k | REPR(target)->pos_funcs.at_pos(tc, STABLE(target), target, OBJECT_BODY(target), body->array_state.index, value, kind); |
58 | 386k | return; |
59 | 0 | case MVM_ITER_MODE_ARRAY_INT: |
60 | 0 | body->array_state.index++; |
61 | 0 | if (body->array_state.index >= body->array_state.limit) |
62 | 0 | MVM_exception_throw_adhoc(tc, "Iteration past end of iterator"); |
63 | 0 | if (kind == MVM_reg_int64) { |
64 | 0 | REPR(target)->pos_funcs.at_pos(tc, STABLE(target), target, OBJECT_BODY(target), body->array_state.index, value, kind); |
65 | 0 | } |
66 | 0 | else if (kind == MVM_reg_obj) { |
67 | 0 | MVMRegister tmp; |
68 | 0 | REPR(target)->pos_funcs.at_pos(tc, STABLE(target), target, OBJECT_BODY(target), body->array_state.index, &tmp, MVM_reg_int64); |
69 | 0 | value->o = MVM_repr_box_int(tc, MVM_hll_current(tc)->int_box_type, tmp.i64); |
70 | 0 | } |
71 | 0 | else { |
72 | 0 | MVM_exception_throw_adhoc(tc, "Wrong register kind in iteration"); |
73 | 0 | } |
74 | 0 | return; |
75 | 0 | case MVM_ITER_MODE_ARRAY_NUM: |
76 | 0 | body->array_state.index++; |
77 | 0 | if (body->array_state.index >= body->array_state.limit) |
78 | 0 | MVM_exception_throw_adhoc(tc, "Iteration past end of iterator"); |
79 | 0 | if (kind == MVM_reg_num64) { |
80 | 0 | REPR(target)->pos_funcs.at_pos(tc, STABLE(target), target, OBJECT_BODY(target), body->array_state.index, value, kind); |
81 | 0 | } |
82 | 0 | else if (kind == MVM_reg_obj) { |
83 | 0 | MVMRegister tmp; |
84 | 0 | REPR(target)->pos_funcs.at_pos(tc, STABLE(target), target, OBJECT_BODY(target), body->array_state.index, &tmp, MVM_reg_num64); |
85 | 0 | value->o = MVM_repr_box_num(tc, MVM_hll_current(tc)->num_box_type, tmp.n64); |
86 | 0 | } |
87 | 0 | else { |
88 | 0 | MVM_exception_throw_adhoc(tc, "Wrong register kind in iteration"); |
89 | 0 | } |
90 | 0 | return; |
91 | 0 | case MVM_ITER_MODE_ARRAY_STR: |
92 | 0 | body->array_state.index++; |
93 | 0 | if (body->array_state.index >= body->array_state.limit) |
94 | 0 | MVM_exception_throw_adhoc(tc, "Iteration past end of iterator"); |
95 | 0 | if (kind == MVM_reg_str) { |
96 | 0 | REPR(target)->pos_funcs.at_pos(tc, STABLE(target), target, OBJECT_BODY(target), body->array_state.index, value, kind); |
97 | 0 | } |
98 | 0 | else if (kind == MVM_reg_obj) { |
99 | 0 | MVMRegister tmp; |
100 | 0 | REPR(target)->pos_funcs.at_pos(tc, STABLE(target), target, OBJECT_BODY(target), body->array_state.index, &tmp, MVM_reg_str); |
101 | 0 | value->o = MVM_repr_box_str(tc, MVM_hll_current(tc)->str_box_type, tmp.s); |
102 | 0 | } |
103 | 0 | else { |
104 | 0 | MVM_exception_throw_adhoc(tc, "Wrong register kind in iteration"); |
105 | 0 | } |
106 | 0 | return; |
107 | 709k | case MVM_ITER_MODE_HASH: |
108 | 709k | body->hash_state.curr = body->hash_state.next; |
109 | 709k | if (!body->hash_state.curr) |
110 | 0 | MVM_exception_throw_adhoc(tc, "Iteration past end of iterator"); |
111 | 709k | body->hash_state.next = HASH_ITER_NEXT_ITEM( |
112 | 709k | &(body->hash_state.next->hash_handle), |
113 | 709k | &(body->hash_state.bucket_state)); |
114 | 709k | value->o = root; |
115 | 709k | return; |
116 | 0 | default: |
117 | 0 | MVM_exception_throw_adhoc(tc, "Unknown iteration mode"); |
118 | 1.09M | } |
119 | 1.09M | } |
120 | | |
121 | | /* This whole splice optimization can be optimized for the case we have two |
122 | | * MVMIter representation objects. */ |
123 | 0 | static void isplice(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMObject *from, MVMint64 offset, MVMuint64 count) { |
124 | 0 | } |
125 | | |
126 | 0 | static MVMStorageSpec get_elem_storage_spec(MVMThreadContext *tc, MVMSTable *st) { |
127 | 0 | MVMStorageSpec spec; |
128 | 0 | spec.inlineable = MVM_STORAGE_SPEC_REFERENCE; |
129 | 0 | spec.boxed_primitive = MVM_STORAGE_SPEC_BP_NONE; |
130 | 0 | spec.can_box = 0; |
131 | 0 | spec.bits = 0; |
132 | 0 | spec.align = 0; |
133 | 0 | spec.is_unsigned = 0; |
134 | 0 | return spec; |
135 | 0 | } |
136 | | |
137 | | /* Compose the representation. */ |
138 | 0 | static void compose(MVMThreadContext *tc, MVMSTable *st, MVMObject *info) { |
139 | 0 | /* XXX element type supplied through this... */ |
140 | 0 | } |
141 | | |
142 | | /* Set the size of the STable. */ |
143 | 260 | static void deserialize_stable_size(MVMThreadContext *tc, MVMSTable *st, MVMSerializationReader *reader) { |
144 | 260 | st->size = sizeof(MVMIter); |
145 | 260 | } |
146 | | |
147 | | /* Initializes the representation. */ |
148 | 130 | const MVMREPROps * MVMIter_initialize(MVMThreadContext *tc) { |
149 | 130 | return &MVMIter_this_repr; |
150 | 130 | } |
151 | | |
152 | | static const MVMREPROps MVMIter_this_repr = { |
153 | | type_object_for, |
154 | | MVM_gc_allocate_object, |
155 | | NULL, /* initialize */ |
156 | | copy_to, |
157 | | MVM_REPR_DEFAULT_ATTR_FUNCS, |
158 | | MVM_REPR_DEFAULT_BOX_FUNCS, |
159 | | { |
160 | | MVM_REPR_DEFAULT_AT_POS, |
161 | | MVM_REPR_DEFAULT_BIND_POS, |
162 | | MVM_REPR_DEFAULT_SET_ELEMS, |
163 | | MVM_REPR_DEFAULT_PUSH, |
164 | | MVM_REPR_DEFAULT_POP, |
165 | | MVM_REPR_DEFAULT_UNSHIFT, |
166 | | shift, |
167 | | isplice, |
168 | | MVM_REPR_DEFAULT_AT_POS_MULTIDIM, |
169 | | MVM_REPR_DEFAULT_BIND_POS_MULTIDIM, |
170 | | MVM_REPR_DEFAULT_DIMENSIONS, |
171 | | MVM_REPR_DEFAULT_SET_DIMENSIONS, |
172 | | get_elem_storage_spec |
173 | | }, /* pos_funcs */ |
174 | | MVM_REPR_DEFAULT_ASS_FUNCS, |
175 | | MVM_REPR_DEFAULT_ELEMS, |
176 | | get_storage_spec, |
177 | | NULL, /* change_type */ |
178 | | NULL, /* serialize */ |
179 | | NULL, /* deserialize */ |
180 | | NULL, /* serialize_repr_data */ |
181 | | NULL, /* deserialize_repr_data */ |
182 | | deserialize_stable_size, |
183 | | gc_mark, |
184 | | gc_free, |
185 | | NULL, /* gc_cleanup */ |
186 | | NULL, /* gc_mark_repr_data */ |
187 | | NULL, /* gc_free_repr_data */ |
188 | | compose, |
189 | | NULL, /* spesh */ |
190 | | "VMIter", /* name */ |
191 | | MVM_REPR_ID_MVMIter, |
192 | | NULL, /* unmanaged_size */ |
193 | | NULL, /* describe_refs */ |
194 | | }; |
195 | | |
196 | 546k | MVMObject * MVM_iter(MVMThreadContext *tc, MVMObject *target) { |
197 | 546k | MVMIter *iterator; |
198 | 546k | MVMROOT(tc, target, { |
199 | 546k | if (REPR(target)->ID == MVM_REPR_ID_VMArray) { |
200 | 546k | iterator = (MVMIter *)MVM_repr_alloc_init(tc, |
201 | 546k | MVM_hll_current(tc)->array_iterator_type); |
202 | 546k | iterator->body.array_state.index = -1; |
203 | 546k | iterator->body.array_state.limit = REPR(target)->elems(tc, STABLE(target), target, OBJECT_BODY(target)); |
204 | 546k | MVM_ASSIGN_REF(tc, &(iterator->common.header), iterator->body.target, target); |
205 | 546k | switch (REPR(target)->pos_funcs.get_elem_storage_spec(tc, STABLE(target)).boxed_primitive) { |
206 | 546k | case MVM_STORAGE_SPEC_BP_INT: iterator->body.mode = MVM_ITER_MODE_ARRAY_INT; break; |
207 | 546k | case MVM_STORAGE_SPEC_BP_NUM: iterator->body.mode = MVM_ITER_MODE_ARRAY_NUM; break; |
208 | 546k | case MVM_STORAGE_SPEC_BP_STR: iterator->body.mode = MVM_ITER_MODE_ARRAY_STR; break; |
209 | 546k | default: iterator->body.mode = MVM_ITER_MODE_ARRAY; break; |
210 | 546k | } |
211 | 546k | } |
212 | 546k | else if (REPR(target)->ID == MVM_REPR_ID_MVMHash) { |
213 | 546k | iterator = (MVMIter *)MVM_repr_alloc_init(tc, |
214 | 546k | MVM_hll_current(tc)->hash_iterator_type); |
215 | 546k | iterator->body.mode = MVM_ITER_MODE_HASH; |
216 | 546k | iterator->body.hash_state.bucket_state = 0; |
217 | 546k | iterator->body.hash_state.curr = NULL; |
218 | 546k | iterator->body.hash_state.next = HASH_ITER_FIRST_ITEM( |
219 | 546k | ((MVMHash *)target)->body.hash_head |
220 | 546k | ? ((MVMHash *)target)->body.hash_head->hash_handle.tbl |
221 | 546k | : NULL, |
222 | 546k | &(iterator->body.hash_state.bucket_state)); |
223 | 546k | MVM_ASSIGN_REF(tc, &(iterator->common.header), iterator->body.target, target); |
224 | 546k | } |
225 | 546k | else if (REPR(target)->ID == MVM_REPR_ID_MVMContext) { |
226 | 546k | /* Turn the context into a VMHash and then iterate that. */ |
227 | 546k | MVMHLLConfig *hll = MVM_hll_current(tc); |
228 | 546k | MVMObject *ctx_hash = MVM_repr_alloc_init(tc, hll->slurpy_hash_type); |
229 | 546k | MVMROOT(tc, ctx_hash, { |
230 | 546k | MVMContext *ctx = (MVMContext *)target; |
231 | 546k | MVMFrame *frame = ctx->body.context; |
232 | 546k | MVMStaticFrame *sf = frame->static_info; |
233 | 546k | MVMLexicalRegistry **lexreg = sf->body.lexical_names_list; |
234 | 546k | MVMuint32 i; |
235 | 546k | MVMROOT(tc, frame, { |
236 | 546k | MVMROOT(tc, sf, { |
237 | 546k | for (i = 0; i < sf->body.num_lexicals; i++) { |
238 | 546k | MVMuint32 idx = lexreg[i]->value; |
239 | 546k | MVMuint16 type = sf->body.lexical_types[idx]; |
240 | 546k | switch (type) { |
241 | 546k | case MVM_reg_obj: { |
242 | 546k | MVMObject *obj = frame->env[idx].o; |
243 | 546k | if (!obj) |
244 | 546k | obj = MVM_frame_vivify_lexical(tc, frame, idx); |
245 | 546k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, obj); |
246 | 546k | break; |
247 | 546k | } |
248 | 546k | case MVM_reg_str: { |
249 | 546k | MVMObject *bs = MVM_repr_box_str(tc, hll->str_box_type, |
250 | 546k | frame->env[idx].s); |
251 | 546k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, bs); |
252 | 546k | break; |
253 | 546k | } |
254 | 546k | case MVM_reg_int8: { |
255 | 546k | MVMObject *bi = MVM_repr_box_int(tc, hll->int_box_type, |
256 | 546k | frame->env[idx].i8); |
257 | 546k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, bi); |
258 | 546k | break; |
259 | 546k | } |
260 | 546k | case MVM_reg_int16: { |
261 | 546k | MVMObject *bi = MVM_repr_box_int(tc, hll->int_box_type, |
262 | 546k | frame->env[idx].i16); |
263 | 546k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, bi); |
264 | 546k | break; |
265 | 546k | } |
266 | 546k | case MVM_reg_int32: { |
267 | 546k | MVMObject *bi = MVM_repr_box_int(tc, hll->int_box_type, |
268 | 546k | frame->env[idx].i32); |
269 | 546k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, bi); |
270 | 546k | break; |
271 | 546k | } |
272 | 546k | case MVM_reg_int64: { |
273 | 546k | MVMObject *bi = MVM_repr_box_int(tc, hll->int_box_type, |
274 | 546k | frame->env[idx].i64); |
275 | 546k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, bi); |
276 | 546k | break; |
277 | 546k | } |
278 | 546k | case MVM_reg_num32: { |
279 | 546k | MVMObject *bn = MVM_repr_box_num(tc, hll->num_box_type, |
280 | 546k | frame->env[idx].n32); |
281 | 546k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, bn); |
282 | 546k | break; |
283 | 546k | } |
284 | 546k | case MVM_reg_num64: { |
285 | 546k | MVMObject *bn = MVM_repr_box_num(tc, hll->num_box_type, |
286 | 546k | frame->env[idx].n64); |
287 | 546k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, bn); |
288 | 546k | break; |
289 | 546k | } |
290 | 546k | default: |
291 | 546k | MVM_exception_throw_adhoc(tc, |
292 | 546k | "Unknown lexical type encountered while building context iterator"); |
293 | 546k | } |
294 | 546k | } |
295 | 546k | }); |
296 | 546k | }); |
297 | 546k | }); |
298 | 546k | |
299 | 546k | /* Call ourselves recursively to get the iterator for this |
300 | 546k | * hash. */ |
301 | 546k | iterator = (MVMIter *)MVM_iter(tc, ctx_hash); |
302 | 546k | } |
303 | 546k | else { |
304 | 546k | MVM_exception_throw_adhoc(tc, "Cannot iterate object with %s representation (%s)", |
305 | 546k | REPR(target)->name, STABLE(target)->debug_name); |
306 | 546k | } |
307 | 546k | }); |
308 | 546k | return (MVMObject *)iterator; |
309 | 546k | } |
310 | | |
311 | 1.62M | MVMint64 MVM_iter_istrue(MVMThreadContext *tc, MVMIter *iter) { |
312 | 1.62M | switch (iter->body.mode) { |
313 | 605k | case MVM_ITER_MODE_ARRAY: |
314 | 605k | case MVM_ITER_MODE_ARRAY_INT: |
315 | 605k | case MVM_ITER_MODE_ARRAY_NUM: |
316 | 605k | case MVM_ITER_MODE_ARRAY_STR: |
317 | 384k | return iter->body.array_state.index + 1 < iter->body.array_state.limit ? 1 : 0; |
318 | 0 | break; |
319 | 1.02M | case MVM_ITER_MODE_HASH: |
320 | 709k | return iter->body.hash_state.next != NULL ? 1 : 0; |
321 | 0 | break; |
322 | 0 | default: |
323 | 0 | MVM_exception_throw_adhoc(tc, "Invalid iteration mode used"); |
324 | 1.62M | } |
325 | 1.62M | } |
326 | | |
327 | 710k | MVMString * MVM_iterkey_s(MVMThreadContext *tc, MVMIter *iterator) { |
328 | 710k | if (REPR(iterator)->ID != MVM_REPR_ID_MVMIter |
329 | 710k | || iterator->body.mode != MVM_ITER_MODE_HASH) |
330 | 0 | MVM_exception_throw_adhoc(tc, "This is not a hash iterator, it's a %s (%s)", REPR(iterator)->name, STABLE(iterator)->debug_name); |
331 | 710k | if (!iterator->body.hash_state.curr) |
332 | 0 | MVM_exception_throw_adhoc(tc, "You have not advanced to the first item of the hash iterator, or have gone past the end"); |
333 | 710k | return MVM_HASH_KEY(iterator->body.hash_state.curr); |
334 | 710k | } |
335 | | |
336 | 699k | MVMObject * MVM_iterval(MVMThreadContext *tc, MVMIter *iterator) { |
337 | 699k | MVMIterBody *body; |
338 | 699k | MVMObject *target; |
339 | 699k | MVMRegister result; |
340 | 699k | if (REPR(iterator)->ID != MVM_REPR_ID_MVMIter) |
341 | 0 | MVM_exception_throw_adhoc(tc, "This is not an iterator, it's a %s (%s)", REPR(iterator)->name, STABLE(iterator)->debug_name); |
342 | 699k | if (iterator->body.mode == MVM_ITER_MODE_ARRAY) { |
343 | 0 | body = &iterator->body; |
344 | 0 | if (body->array_state.index == -1) |
345 | 0 | MVM_exception_throw_adhoc(tc, "You have not yet advanced in the array iterator"); |
346 | 0 | target = body->target; |
347 | 0 | REPR(target)->pos_funcs.at_pos(tc, STABLE(target), target, OBJECT_BODY(target), body->array_state.index, &result, MVM_reg_obj); |
348 | 0 | } |
349 | 699k | else if (iterator->body.mode == MVM_ITER_MODE_HASH) { |
350 | 699k | if (!iterator->body.hash_state.curr) |
351 | 0 | MVM_exception_throw_adhoc(tc, "You have not advanced to the first item of the hash iterator, or have gone past the end"); |
352 | 699k | result.o = iterator->body.hash_state.curr->value; |
353 | 699k | if (!result.o) |
354 | 0 | result.o = tc->instance->VMNull; |
355 | 699k | } |
356 | 0 | else { |
357 | 0 | MVM_exception_throw_adhoc(tc, "Unknown iterator mode in iterval"); |
358 | 0 | } |
359 | 699k | return result.o; |
360 | 699k | } |