tagged release 0.6.4
[parrot.git] / src / stacks.c
blob2d2de40f519d7df18589b6137d86ac9391cb40a7
1 /*
2 Copyright (C) 2001-2008, The Perl Foundation.
3 $Id$
5 =head1 NAME
7 src/stacks.c - Stack handling routines for Parrot
9 =head1 DESCRIPTION
11 The stack is stored as a linked list of chunks (C<Stack_Chunk>),
12 where each chunk has room for one entry.
14 =head2 Functions
16 =over 4
18 =cut
22 #include "parrot/parrot.h"
23 #include "parrot/stacks.h"
25 /* HEADERIZER HFILE: include/parrot/stacks.h */
29 =item C<void stack_system_init>
31 Called from C<make_interpreter()> to initialize the interpreter's
32 register stacks.
34 =cut
38 PARROT_API
39 void
40 stack_system_init(SHIM_INTERP)
46 =item C<Stack_Chunk_t * cst_new_stack_chunk>
48 Get a new chunk either from the freelist or allocate one.
50 =cut
54 PARROT_API
55 PARROT_WARN_UNUSED_RESULT
56 PARROT_CANNOT_RETURN_NULL
57 Stack_Chunk_t *
58 cst_new_stack_chunk(PARROT_INTERP, ARGIN(const Stack_Chunk_t *chunk))
60 Small_Object_Pool * const pool = chunk->pool;
61 Stack_Chunk_t * const new_chunk = (Stack_Chunk_t *)pool->get_free_object(interp, pool);
63 PObj_bufstart(new_chunk) = NULL;
64 PObj_buflen(new_chunk) = 0;
66 new_chunk->pool = chunk->pool;
67 new_chunk->name = chunk->name;
69 return new_chunk;
74 =item C<Stack_Chunk_t * new_stack>
76 Create a new stack and name it. C<< stack->name >> is used for
77 debugging/error reporting.
79 =cut
83 PARROT_API
84 PARROT_CANNOT_RETURN_NULL
85 PARROT_WARN_UNUSED_RESULT
86 Stack_Chunk_t *
87 new_stack(PARROT_INTERP, ARGIN(const char *name))
89 Small_Object_Pool * const pool = make_bufferlike_pool(interp, sizeof (Stack_Chunk_t));
90 Stack_Chunk_t * const chunk = (Stack_Chunk_t *)(pool->get_free_object)(interp, pool);
92 chunk->prev = chunk; /* mark the top of the stack */
93 chunk->name = name;
94 chunk->pool = pool; /* cache the pool pointer, for ease */
96 return chunk;
102 =item C<void mark_stack>
104 Mark entries in a stack structure during DOD.
106 =cut
110 PARROT_API
111 void
112 mark_stack(PARROT_INTERP, ARGMOD(Stack_Chunk_t *chunk))
114 for (; ; chunk = chunk->prev) {
115 Stack_Entry_t *entry;
117 pobject_lives(interp, (PObj *)chunk);
119 if (chunk == chunk->prev)
120 break;
122 entry = STACK_DATAP(chunk);
124 if (entry->entry_type == STACK_ENTRY_PMC && UVal_pmc(entry->entry))
125 pobject_lives(interp, (PObj *)UVal_pmc(entry->entry));
131 =item C<void stack_destroy>
133 stack_destroy() doesn't need to do anything, since GC does it all.
135 =cut
139 PARROT_API
140 void
141 stack_destroy(SHIM(Stack_Chunk_t *top))
143 /* GC does it all */
148 =item C<size_t stack_height>
150 Returns the height of the stack. The maximum "depth" is height - 1.
152 =cut
156 PARROT_API
157 PARROT_WARN_UNUSED_RESULT
158 size_t
159 stack_height(SHIM_INTERP, ARGIN(const Stack_Chunk_t *chunk))
161 size_t height = 0;
163 for (; ; chunk = chunk->prev) {
164 if (chunk == chunk->prev)
165 break;
166 ++height;
169 return height;
175 =item C<Stack_Entry_t * stack_entry>
177 If C<< depth >= 0 >>, return the entry at that depth from the top of the
178 stack, with 0 being the top entry. If C<depth < 0>, then return the
179 entry C<|depth|> entries from the bottom of the stack. Returns C<NULL>
180 if C<|depth| > number> of entries in stack.
182 =cut
186 PARROT_API
187 PARROT_CAN_RETURN_NULL
188 PARROT_WARN_UNUSED_RESULT
189 Stack_Entry_t *
190 stack_entry(PARROT_INTERP, ARGIN(Stack_Chunk_t *stack), INTVAL depth)
192 Stack_Chunk_t *chunk;
193 size_t offset = (size_t)depth;
195 if (depth < 0)
196 return NULL;
198 /* Start at top */
199 chunk = stack;
201 while (offset) {
202 if (chunk == chunk->prev)
203 break;
204 --offset;
205 chunk = chunk->prev;
208 if (chunk == chunk->prev)
209 return NULL;
211 return STACK_DATAP(chunk);
216 =item C<void rotate_entries>
218 Rotate the top N entries by one. If C<< N > 0 >>, the rotation is bubble
219 up, so the top most element becomes the Nth element. If C<< N < 0 >>, the
220 rotation is bubble down, so that the Nth element becomes the top most
221 element.
225 PARROT_API
226 void
227 rotate_entries(PARROT_INTERP, ARGMOD(Stack_Chunk_t **stack_p), INTVAL num_entries)
229 Stack_Chunk_t * const stack = *stack_p;
231 if (num_entries >= -1 && num_entries <= 1) {
232 return;
235 if (num_entries < 0) {
236 INTVAL i;
237 Stack_Entry_t temp;
238 INTVAL depth;
240 num_entries = -num_entries;
241 depth = num_entries - 1;
243 if (stack_height(interp, stack) < (size_t)num_entries) {
244 real_exception(interp, NULL, ERROR_STACK_SHALLOW, "Stack too shallow!");
247 /* XXX Dereferencing stack_entry here is a cavalcade of danger */
248 temp = *stack_entry(interp, stack, depth);
249 for (i = depth; i > 0; i--) {
250 *stack_entry(interp, stack, i) =
251 *stack_entry(interp, stack, i - 1);
254 *stack_entry(interp, stack, 0) = temp;
256 else {
257 INTVAL i;
258 Stack_Entry_t temp;
259 INTVAL depth = num_entries - 1;
261 if (stack_height(interp, stack) < (size_t)num_entries) {
262 real_exception(interp, NULL, ERROR_STACK_SHALLOW, "Stack too shallow!");
264 /* XXX Dereferencing stack_entry here is a cavalcade of danger */
265 temp = *stack_entry(interp, stack, 0);
266 for (i = 0; i < depth; i++) {
267 *stack_entry(interp, stack, i) =
268 *stack_entry(interp, stack, i + 1);
271 *stack_entry(interp, stack, depth) = temp;
277 =item C<Stack_Entry_t* stack_prepare_push>
279 Return a pointer, where new entries go for push.
281 =cut
285 PARROT_API
286 PARROT_WARN_UNUSED_RESULT
287 PARROT_CANNOT_RETURN_NULL
288 Stack_Entry_t*
289 stack_prepare_push(PARROT_INTERP, ARGMOD(Stack_Chunk_t **stack_p))
291 Stack_Chunk_t * const chunk = *stack_p;
292 Stack_Chunk_t * const new_chunk = cst_new_stack_chunk(interp, chunk);
294 new_chunk->prev = chunk;
295 *stack_p = new_chunk;
297 return STACK_DATAP(new_chunk);
302 =item C<void stack_push>
304 Push something on the generic stack.
306 Note that the cleanup pointer, if non-C<NULL>, points to a routine
307 that'll be called when the entry is removed from the stack. This is
308 handy for those cases where you need some sort of activity to take place
309 when an entry is removed, such as when you push a lexical lock onto the
310 call stack, or localize (or tempify, or whatever we're calling it)
311 variable or something.
315 PARROT_API
316 void
317 stack_push(PARROT_INTERP, ARGMOD(Stack_Chunk_t **stack_p),
318 ARGIN(void *thing), Stack_entry_type type, NULLOK(Stack_cleanup_method cleanup))
320 Stack_Entry_t * const entry = (Stack_Entry_t *)stack_prepare_push(interp, stack_p);
322 /* Remember the type */
323 entry->entry_type = type;
325 /* Remember the cleanup function */
326 entry->cleanup = cleanup;
328 /* Store our thing */
329 switch (type) {
330 case STACK_ENTRY_MARK:
331 UVal_int(entry->entry) = *(INTVAL *)thing;
332 break;
333 case STACK_ENTRY_DESTINATION:
334 UVal_ptr(entry->entry) = thing;
335 break;
336 case STACK_ENTRY_ACTION:
337 case STACK_ENTRY_PMC:
338 UVal_pmc(entry->entry) = (PMC *)thing;
339 break;
340 default:
341 real_exception(interp, NULL, ERROR_BAD_STACK_TYPE,
342 "Invalid Stack_Entry_type!");
348 =item C<Stack_Entry_t* stack_prepare_pop>
350 Return a pointer, where new entries are popped off.
352 =cut
356 PARROT_API
357 PARROT_WARN_UNUSED_RESULT
358 PARROT_CANNOT_RETURN_NULL
359 Stack_Entry_t*
360 stack_prepare_pop(PARROT_INTERP, ARGMOD(Stack_Chunk_t **stack_p))
362 Stack_Chunk_t * const chunk = *stack_p;
364 /* the first entry (initial top) refers to itself */
365 if (chunk == chunk->prev)
366 real_exception(interp, NULL, ERROR_STACK_EMPTY,
367 "No entries on %s Stack!", chunk->name);
369 *stack_p = chunk->prev;
371 return STACK_DATAP(chunk);
376 =item C<void * stack_pop>
378 Pop off an entry and return a pointer to the contents.
382 PARROT_API
383 PARROT_CAN_RETURN_NULL
384 void *
385 stack_pop(PARROT_INTERP, ARGMOD(Stack_Chunk_t **stack_p),
386 ARGOUT_NULLOK(void *where), Stack_entry_type type)
388 Stack_Chunk_t *cur_chunk = *stack_p;
389 Stack_Entry_t * const entry =
390 (Stack_Entry_t *)stack_prepare_pop(interp, stack_p);
392 /* Types of 0 mean we don't care */
393 if (type && entry->entry_type != type) {
394 real_exception(interp, NULL, ERROR_BAD_STACK_TYPE,
395 "Wrong type on top of stack!\n");
398 /* Cleanup routine? */
399 if (entry->cleanup != STACK_CLEANUP_NULL)
400 (*entry->cleanup) (interp, entry);
402 /* Sometimes the caller cares what the value was */
403 if (where) {
404 /* Snag the value */
405 switch (type) {
406 case STACK_ENTRY_MARK:
407 *(INTVAL *)where = UVal_int(entry->entry);
408 break;
409 case STACK_ENTRY_DESTINATION:
410 *(void **)where = UVal_ptr(entry->entry);
411 break;
412 case STACK_ENTRY_ACTION:
413 case STACK_ENTRY_PMC:
414 *(PMC **)where = UVal_pmc(entry->entry);
415 break;
416 default:
417 real_exception(interp, NULL, ERROR_BAD_STACK_TYPE,
418 "Wrong type on top of stack!\n");
422 /* recycle this chunk to the free list if it's otherwise unreferenced */
423 if (cur_chunk->refcount <= 0) {
424 Small_Object_Pool * const pool = cur_chunk->pool;
426 pool->dod_object(interp, pool, (PObj *)cur_chunk);
427 pool->add_free_object(interp, pool, (PObj *)cur_chunk);
430 return where;
435 =item C<void * pop_dest>
437 Pop off a destination entry and return a pointer to the contents.
439 =cut
443 PARROT_API
444 PARROT_WARN_UNUSED_RESULT
445 PARROT_CANNOT_RETURN_NULL
446 void *
447 pop_dest(PARROT_INTERP)
449 /* We don't mind the extra call, so we do this: (previous comment
450 * said we *do* mind, but I say let the compiler decide) */
451 void *dest;
452 (void)stack_pop(interp, &interp->dynamic_env,
453 &dest, STACK_ENTRY_DESTINATION);
454 return dest;
459 =item C<void * stack_peek>
461 Peek at stack and return pointer to entry and the type of the entry.
463 =cut
467 PARROT_API
468 PARROT_CAN_RETURN_NULL
469 PARROT_WARN_UNUSED_RESULT
470 void *
471 stack_peek(PARROT_INTERP, ARGIN(Stack_Chunk_t *stack_base),
472 ARGMOD_NULLOK(Stack_entry_type *type))
474 const Stack_Entry_t * const entry = stack_entry(interp, stack_base, 0);
475 if (entry == NULL)
476 return NULL;
478 if (type != NULL)
479 *type = entry->entry_type;
481 if (entry->entry_type == STACK_ENTRY_DESTINATION)
482 return UVal_ptr(entry->entry);
484 return (void *) UVal_pmc(entry->entry);
489 =item C<Stack_entry_type get_entry_type>
491 Returns the stack entry type of C<entry>.
493 =cut
497 PARROT_WARN_UNUSED_RESULT
498 PARROT_PURE_FUNCTION
499 Stack_entry_type
500 get_entry_type(ARGIN(const Stack_Entry_t *entry))
502 return entry->entry_type;
507 =item C<void Parrot_dump_dynamic_environment>
509 Print a representation of the dynamic stack to the standard error (using
510 C<PIO_eprintf>). This is used only temporarily for debugging.
512 =cut
516 PARROT_API
517 void
518 Parrot_dump_dynamic_environment(PARROT_INTERP, ARGIN(Stack_Chunk_t *dynamic_env))
520 int height = (int) stack_height(interp, dynamic_env);
522 while (dynamic_env->prev != dynamic_env) {
523 const Stack_Entry_t * const e = stack_entry(interp, dynamic_env, 0);
524 if (! e) {
525 real_exception(interp, NULL, 1, "Control stack damaged");
528 PIO_eprintf(interp, "[%4d: chunk %p entry %p "
529 "type %d cleanup %p]\n",
530 height, dynamic_env, e,
531 e->entry_type, e->cleanup);
532 if (e->entry_type == STACK_ENTRY_PMC
533 || e->entry_type == STACK_ENTRY_ACTION) {
534 PMC * const thing = UVal_pmc(e->entry);
536 PIO_eprintf(interp, "[ PMC %p type %d => %Ss]\n",
537 thing, thing->vtable->base_type,
538 VTABLE_get_string(interp, thing));
540 else if (e->entry_type == STACK_ENTRY_MARK) {
541 PIO_eprintf(interp, "[ mark %d]\n",
542 UVal_int(e->entry));
544 dynamic_env = dynamic_env->prev;
545 height--;
547 PIO_eprintf(interp, "[%4d: chunk %p %s base]\n",
548 height, dynamic_env, dynamic_env->name);
553 =back
555 =head1 SEE ALSO
557 F<include/parrot/stacks.h> and F<include/parrot/enums.h>.
559 =cut
565 * Local variables:
566 * c-file-style: "parrot"
567 * End:
568 * vim: expandtab shiftwidth=4: