/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 | 144 | static MVMObject * type_object_for(MVMThreadContext *tc, MVMObject *HOW) { |
9 | 144 | MVMSTable *st = MVM_gc_allocate_stable(tc, &MVMIter_this_repr, HOW); |
10 | 144 | |
11 | 144 | MVMROOT(tc, st, { |
12 | 144 | MVMObject *obj = MVM_gc_allocate_type_object(tc, st); |
13 | 144 | MVM_ASSIGN_REF(tc, &(st->header), st->WHAT, obj); |
14 | 144 | st->size = sizeof(MVMIter); |
15 | 144 | }); |
16 | 144 | |
17 | 144 | return st->WHAT; |
18 | 144 | } |
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 | 597 | static void gc_mark(MVMThreadContext *tc, MVMSTable *st, void *data, MVMGCWorklist *worklist) { |
27 | 597 | MVMIterBody *body = (MVMIterBody *)data; |
28 | 597 | MVM_gc_worklist_add(tc, worklist, &body->target); |
29 | 597 | } |
30 | | |
31 | | /* Called by the VM in order to free memory associated with this object. */ |
32 | 478k | static void gc_free(MVMThreadContext *tc, MVMObject *obj) { |
33 | 478k | } |
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 | 8.75k | static const MVMStorageSpec * get_storage_spec(MVMThreadContext *tc, MVMSTable *st) { |
46 | 8.75k | return &storage_spec; |
47 | 8.75k | } |
48 | | |
49 | 1.33M | static void shift(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMRegister *value, MVMuint16 kind) { |
50 | 1.33M | MVMIterBody *body = (MVMIterBody *)data; |
51 | 1.33M | MVMObject *target = body->target; |
52 | 1.33M | switch (body->mode) { |
53 | 463k | case MVM_ITER_MODE_ARRAY: |
54 | 463k | body->array_state.index++; |
55 | 463k | if (body->array_state.index >= body->array_state.limit) |
56 | 0 | MVM_exception_throw_adhoc(tc, "Iteration past end of iterator"); |
57 | 463k | REPR(target)->pos_funcs.at_pos(tc, STABLE(target), target, OBJECT_BODY(target), body->array_state.index, value, kind); |
58 | 463k | return; |
59 | 2 | case MVM_ITER_MODE_ARRAY_INT: |
60 | 2 | body->array_state.index++; |
61 | 2 | if (body->array_state.index >= body->array_state.limit) |
62 | 0 | MVM_exception_throw_adhoc(tc, "Iteration past end of iterator"); |
63 | 2 | 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 | 2 | else if (kind == MVM_reg_obj) { |
67 | 2 | MVMRegister tmp; |
68 | 2 | REPR(target)->pos_funcs.at_pos(tc, STABLE(target), target, OBJECT_BODY(target), body->array_state.index, &tmp, MVM_reg_int64); |
69 | 2 | value->o = MVM_repr_box_int(tc, MVM_hll_current(tc)->int_box_type, tmp.i64); |
70 | 2 | } |
71 | 0 | else { |
72 | 0 | MVM_exception_throw_adhoc(tc, "Wrong register kind in iteration"); |
73 | 0 | } |
74 | 2 | return; |
75 | 2 | case MVM_ITER_MODE_ARRAY_NUM: |
76 | 2 | body->array_state.index++; |
77 | 2 | if (body->array_state.index >= body->array_state.limit) |
78 | 0 | MVM_exception_throw_adhoc(tc, "Iteration past end of iterator"); |
79 | 2 | 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 | 2 | else if (kind == MVM_reg_obj) { |
83 | 2 | MVMRegister tmp; |
84 | 2 | REPR(target)->pos_funcs.at_pos(tc, STABLE(target), target, OBJECT_BODY(target), body->array_state.index, &tmp, MVM_reg_num64); |
85 | 2 | value->o = MVM_repr_box_num(tc, MVM_hll_current(tc)->num_box_type, tmp.n64); |
86 | 2 | } |
87 | 0 | else { |
88 | 0 | MVM_exception_throw_adhoc(tc, "Wrong register kind in iteration"); |
89 | 0 | } |
90 | 2 | return; |
91 | 2 | case MVM_ITER_MODE_ARRAY_STR: |
92 | 2 | body->array_state.index++; |
93 | 2 | if (body->array_state.index >= body->array_state.limit) |
94 | 0 | MVM_exception_throw_adhoc(tc, "Iteration past end of iterator"); |
95 | 2 | 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 | 2 | else if (kind == MVM_reg_obj) { |
99 | 2 | MVMRegister tmp; |
100 | 2 | REPR(target)->pos_funcs.at_pos(tc, STABLE(target), target, OBJECT_BODY(target), body->array_state.index, &tmp, MVM_reg_str); |
101 | 2 | value->o = MVM_repr_box_str(tc, MVM_hll_current(tc)->str_box_type, tmp.s); |
102 | 2 | } |
103 | 0 | else { |
104 | 0 | MVM_exception_throw_adhoc(tc, "Wrong register kind in iteration"); |
105 | 0 | } |
106 | 2 | return; |
107 | 868k | case MVM_ITER_MODE_HASH: |
108 | 868k | body->hash_state.curr = body->hash_state.next; |
109 | 868k | if (!body->hash_state.curr) |
110 | 0 | MVM_exception_throw_adhoc(tc, "Iteration past end of iterator"); |
111 | 868k | body->hash_state.next = HASH_ITER_NEXT_ITEM( |
112 | 868k | &(body->hash_state.next->hash_handle), |
113 | 868k | &(body->hash_state.bucket_state)); |
114 | 868k | value->o = root; |
115 | 868k | return; |
116 | 0 | default: |
117 | 0 | MVM_exception_throw_adhoc(tc, "Unknown iteration mode"); |
118 | 1.33M | } |
119 | 1.33M | } |
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 | 288 | static void deserialize_stable_size(MVMThreadContext *tc, MVMSTable *st, MVMSerializationReader *reader) { |
144 | 288 | st->size = sizeof(MVMIter); |
145 | 288 | } |
146 | | |
147 | | /* Initializes the representation. */ |
148 | 144 | const MVMREPROps * MVMIter_initialize(MVMThreadContext *tc) { |
149 | 144 | return &MVMIter_this_repr; |
150 | 144 | } |
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 | | MVM_REPR_DEFAULT_SLICE, |
168 | | isplice, |
169 | | MVM_REPR_DEFAULT_AT_POS_MULTIDIM, |
170 | | MVM_REPR_DEFAULT_BIND_POS_MULTIDIM, |
171 | | MVM_REPR_DEFAULT_DIMENSIONS, |
172 | | MVM_REPR_DEFAULT_SET_DIMENSIONS, |
173 | | get_elem_storage_spec, |
174 | | MVM_REPR_DEFAULT_POS_AS_ATOMIC, |
175 | | MVM_REPR_DEFAULT_POS_AS_ATOMIC_MULTIDIM |
176 | | }, /* pos_funcs */ |
177 | | MVM_REPR_DEFAULT_ASS_FUNCS, |
178 | | MVM_REPR_DEFAULT_ELEMS, |
179 | | get_storage_spec, |
180 | | NULL, /* change_type */ |
181 | | NULL, /* serialize */ |
182 | | NULL, /* deserialize */ |
183 | | NULL, /* serialize_repr_data */ |
184 | | NULL, /* deserialize_repr_data */ |
185 | | deserialize_stable_size, |
186 | | gc_mark, |
187 | | gc_free, |
188 | | NULL, /* gc_cleanup */ |
189 | | NULL, /* gc_mark_repr_data */ |
190 | | NULL, /* gc_free_repr_data */ |
191 | | compose, |
192 | | NULL, /* spesh */ |
193 | | "VMIter", /* name */ |
194 | | MVM_REPR_ID_MVMIter, |
195 | | NULL, /* unmanaged_size */ |
196 | | NULL, /* describe_refs */ |
197 | | }; |
198 | | |
199 | 666k | MVMObject * MVM_iter(MVMThreadContext *tc, MVMObject *target) { |
200 | 666k | MVMIter *iterator; |
201 | 666k | if (!IS_CONCRETE(target)) { |
202 | 0 | MVM_exception_throw_adhoc(tc, "Cannot iterate over a %s type object", MVM_6model_get_debug_name(tc, target)); |
203 | 0 | } |
204 | 666k | MVMROOT(tc, target, { |
205 | 666k | if (REPR(target)->ID == MVM_REPR_ID_VMArray) { |
206 | 666k | iterator = (MVMIter *)MVM_repr_alloc_init(tc, |
207 | 666k | MVM_hll_current(tc)->array_iterator_type); |
208 | 666k | iterator->body.array_state.index = -1; |
209 | 666k | iterator->body.array_state.limit = REPR(target)->elems(tc, STABLE(target), target, OBJECT_BODY(target)); |
210 | 666k | MVM_ASSIGN_REF(tc, &(iterator->common.header), iterator->body.target, target); |
211 | 666k | switch (REPR(target)->pos_funcs.get_elem_storage_spec(tc, STABLE(target)).boxed_primitive) { |
212 | 666k | case MVM_STORAGE_SPEC_BP_INT: iterator->body.mode = MVM_ITER_MODE_ARRAY_INT; break; |
213 | 666k | case MVM_STORAGE_SPEC_BP_NUM: iterator->body.mode = MVM_ITER_MODE_ARRAY_NUM; break; |
214 | 666k | case MVM_STORAGE_SPEC_BP_STR: iterator->body.mode = MVM_ITER_MODE_ARRAY_STR; break; |
215 | 666k | default: iterator->body.mode = MVM_ITER_MODE_ARRAY; break; |
216 | 666k | } |
217 | 666k | } |
218 | 666k | else if (REPR(target)->ID == MVM_REPR_ID_MVMHash) { |
219 | 666k | iterator = (MVMIter *)MVM_repr_alloc_init(tc, |
220 | 666k | MVM_hll_current(tc)->hash_iterator_type); |
221 | 666k | iterator->body.mode = MVM_ITER_MODE_HASH; |
222 | 666k | iterator->body.hash_state.bucket_state = 0; |
223 | 666k | iterator->body.hash_state.curr = NULL; |
224 | 666k | iterator->body.hash_state.next = HASH_ITER_FIRST_ITEM( |
225 | 666k | ((MVMHash *)target)->body.hash_head |
226 | 666k | ? ((MVMHash *)target)->body.hash_head->hash_handle.tbl |
227 | 666k | : NULL, |
228 | 666k | &(iterator->body.hash_state.bucket_state)); |
229 | 666k | MVM_ASSIGN_REF(tc, &(iterator->common.header), iterator->body.target, target); |
230 | 666k | } |
231 | 666k | else if (REPR(target)->ID == MVM_REPR_ID_MVMContext) { |
232 | 666k | /* Turn the context into a VMHash and then iterate that. */ |
233 | 666k | MVMHLLConfig *hll = MVM_hll_current(tc); |
234 | 666k | MVMObject *ctx_hash = MVM_repr_alloc_init(tc, hll->slurpy_hash_type); |
235 | 666k | MVMROOT(tc, ctx_hash, { |
236 | 666k | MVMContext *ctx = (MVMContext *)target; |
237 | 666k | MVMFrame *frame = ctx->body.context; |
238 | 666k | MVMStaticFrame *sf = frame->static_info; |
239 | 666k | MVMLexicalRegistry **lexreg = sf->body.lexical_names_list; |
240 | 666k | MVMuint32 i; |
241 | 666k | MVMROOT2(tc, frame, sf, { |
242 | 666k | for (i = 0; i < sf->body.num_lexicals; i++) { |
243 | 666k | MVMuint32 idx = lexreg[i]->value; |
244 | 666k | MVMuint16 type = sf->body.lexical_types[idx]; |
245 | 666k | switch (type) { |
246 | 666k | case MVM_reg_obj: { |
247 | 666k | MVMObject *obj = frame->env[idx].o; |
248 | 666k | if (!obj) |
249 | 666k | obj = MVM_frame_vivify_lexical(tc, frame, idx); |
250 | 666k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, obj); |
251 | 666k | break; |
252 | 666k | } |
253 | 666k | case MVM_reg_str: { |
254 | 666k | MVMObject *bs = MVM_repr_box_str(tc, hll->str_box_type, |
255 | 666k | frame->env[idx].s); |
256 | 666k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, bs); |
257 | 666k | break; |
258 | 666k | } |
259 | 666k | case MVM_reg_int8: { |
260 | 666k | MVMObject *bi = MVM_repr_box_int(tc, hll->int_box_type, |
261 | 666k | frame->env[idx].i8); |
262 | 666k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, bi); |
263 | 666k | break; |
264 | 666k | } |
265 | 666k | case MVM_reg_uint8: { |
266 | 666k | MVMObject *bi = MVM_repr_box_int(tc, hll->int_box_type, |
267 | 666k | frame->env[idx].u8); |
268 | 666k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, bi); |
269 | 666k | break; |
270 | 666k | } |
271 | 666k | case MVM_reg_int16: { |
272 | 666k | MVMObject *bi = MVM_repr_box_int(tc, hll->int_box_type, |
273 | 666k | frame->env[idx].i16); |
274 | 666k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, bi); |
275 | 666k | break; |
276 | 666k | } |
277 | 666k | case MVM_reg_uint16: { |
278 | 666k | MVMObject *bi = MVM_repr_box_int(tc, hll->int_box_type, |
279 | 666k | frame->env[idx].u16); |
280 | 666k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, bi); |
281 | 666k | break; |
282 | 666k | } |
283 | 666k | case MVM_reg_int32: { |
284 | 666k | MVMObject *bi = MVM_repr_box_int(tc, hll->int_box_type, |
285 | 666k | frame->env[idx].i32); |
286 | 666k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, bi); |
287 | 666k | break; |
288 | 666k | } |
289 | 666k | case MVM_reg_uint32: { |
290 | 666k | MVMObject *bi = MVM_repr_box_int(tc, hll->int_box_type, |
291 | 666k | frame->env[idx].u32); |
292 | 666k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, bi); |
293 | 666k | break; |
294 | 666k | } |
295 | 666k | case MVM_reg_int64: { |
296 | 666k | MVMObject *bi = MVM_repr_box_int(tc, hll->int_box_type, |
297 | 666k | frame->env[idx].i64); |
298 | 666k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, bi); |
299 | 666k | break; |
300 | 666k | } |
301 | 666k | case MVM_reg_uint64: { |
302 | 666k | MVMObject *bi = MVM_repr_box_int(tc, hll->int_box_type, |
303 | 666k | frame->env[idx].u64); |
304 | 666k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, bi); |
305 | 666k | break; |
306 | 666k | } |
307 | 666k | case MVM_reg_num32: { |
308 | 666k | MVMObject *bn = MVM_repr_box_num(tc, hll->num_box_type, |
309 | 666k | frame->env[idx].n32); |
310 | 666k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, bn); |
311 | 666k | break; |
312 | 666k | } |
313 | 666k | case MVM_reg_num64: { |
314 | 666k | MVMObject *bn = MVM_repr_box_num(tc, hll->num_box_type, |
315 | 666k | frame->env[idx].n64); |
316 | 666k | MVM_repr_bind_key_o(tc, ctx_hash, lexreg[i]->key, bn); |
317 | 666k | break; |
318 | 666k | } |
319 | 666k | default: |
320 | 666k | MVM_exception_throw_adhoc(tc, |
321 | 666k | "%s lexical type encountered while building context iterator", MVM_reg_get_debug_name(tc, type)); |
322 | 666k | } |
323 | 666k | } |
324 | 666k | }); |
325 | 666k | }); |
326 | 666k | |
327 | 666k | /* Call ourselves recursively to get the iterator for this |
328 | 666k | * hash. */ |
329 | 666k | iterator = (MVMIter *)MVM_iter(tc, ctx_hash); |
330 | 666k | } |
331 | 666k | else { |
332 | 666k | MVM_exception_throw_adhoc(tc, "Cannot iterate object with %s representation (%s)", |
333 | 666k | REPR(target)->name, MVM_6model_get_debug_name(tc, target)); |
334 | 666k | } |
335 | 666k | }); |
336 | 666k | return (MVMObject *)iterator; |
337 | 666k | } |
338 | | |
339 | 1.98M | MVMint64 MVM_iter_istrue(MVMThreadContext *tc, MVMIter *iter) { |
340 | 1.98M | switch (iter->body.mode) { |
341 | 735k | case MVM_ITER_MODE_ARRAY: |
342 | 735k | case MVM_ITER_MODE_ARRAY_INT: |
343 | 735k | case MVM_ITER_MODE_ARRAY_NUM: |
344 | 735k | case MVM_ITER_MODE_ARRAY_STR: |
345 | 460k | return iter->body.array_state.index + 1 < iter->body.array_state.limit ? 1 : 0; |
346 | 0 | break; |
347 | 1.25M | case MVM_ITER_MODE_HASH: |
348 | 868k | return iter->body.hash_state.next != NULL ? 1 : 0; |
349 | 0 | break; |
350 | 0 | default: |
351 | 0 | MVM_exception_throw_adhoc(tc, "Invalid iteration mode used"); |
352 | 1.98M | } |
353 | 1.98M | } |
354 | | |
355 | 871k | MVMString * MVM_iterkey_s(MVMThreadContext *tc, MVMIter *iterator) { |
356 | 871k | if (REPR(iterator)->ID != MVM_REPR_ID_MVMIter |
357 | 871k | || iterator->body.mode != MVM_ITER_MODE_HASH) |
358 | 0 | MVM_exception_throw_adhoc(tc, "This is not a hash iterator, it's a %s (%s)", REPR(iterator)->name, MVM_6model_get_debug_name(tc, (MVMObject *)iterator)); |
359 | 871k | if (!iterator->body.hash_state.curr) |
360 | 0 | MVM_exception_throw_adhoc(tc, "You have not advanced to the first item of the hash iterator, or have gone past the end"); |
361 | 871k | return MVM_HASH_KEY(iterator->body.hash_state.curr); |
362 | 871k | } |
363 | | |
364 | 848k | MVMObject * MVM_iterval(MVMThreadContext *tc, MVMIter *iterator) { |
365 | 848k | MVMIterBody *body; |
366 | 848k | MVMObject *target; |
367 | 848k | MVMRegister result; |
368 | 848k | if (REPR(iterator)->ID != MVM_REPR_ID_MVMIter) |
369 | 0 | MVM_exception_throw_adhoc(tc, "This is not an iterator, it's a %s (%s)", REPR(iterator)->name, MVM_6model_get_debug_name(tc, (MVMObject *)iterator)); |
370 | 848k | if (iterator->body.mode == MVM_ITER_MODE_ARRAY) { |
371 | 0 | body = &iterator->body; |
372 | 0 | if (body->array_state.index == -1) |
373 | 0 | MVM_exception_throw_adhoc(tc, "You have not yet advanced in the array iterator"); |
374 | 0 | target = body->target; |
375 | 0 | REPR(target)->pos_funcs.at_pos(tc, STABLE(target), target, OBJECT_BODY(target), body->array_state.index, &result, MVM_reg_obj); |
376 | 0 | } |
377 | 848k | else if (iterator->body.mode == MVM_ITER_MODE_HASH) { |
378 | 848k | if (!iterator->body.hash_state.curr) |
379 | 0 | MVM_exception_throw_adhoc(tc, "You have not advanced to the first item of the hash iterator, or have gone past the end"); |
380 | 848k | result.o = iterator->body.hash_state.curr->value; |
381 | 848k | if (!result.o) |
382 | 0 | result.o = tc->instance->VMNull; |
383 | 848k | } |
384 | 0 | else { |
385 | 0 | MVM_exception_throw_adhoc(tc, "Unknown iterator mode in iterval"); |
386 | 0 | } |
387 | 848k | return result.o; |
388 | 848k | } |