2 Copyright (C) 2001-2008, The Perl Foundation.
7 src/stacks.c - Stack handling routines for Parrot
11 The stack is stored as a linked list of chunks (C<Stack_Chunk>),
12 where each chunk has room for one entry.
22 #include "parrot/parrot.h"
23 #include "parrot/stacks.h"
26 /* HEADERIZER HFILE: include/parrot/stacks.h */
28 /* HEADERIZER BEGIN: static */
29 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
31 static void run_cleanup_action(PARROT_INTERP
, ARGIN(Stack_Entry_t
*e
))
32 __attribute__nonnull__(1)
33 __attribute__nonnull__(2);
35 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
36 /* HEADERIZER END: static */
40 =item C<void stack_system_init>
42 Called from C<make_interpreter()> to initialize the interpreter's
51 stack_system_init(SHIM_INTERP
)
57 =item C<Stack_Chunk_t * cst_new_stack_chunk>
59 Get a new chunk either from the freelist or allocate one.
66 PARROT_WARN_UNUSED_RESULT
67 PARROT_CANNOT_RETURN_NULL
69 cst_new_stack_chunk(PARROT_INTERP
, ARGIN(const Stack_Chunk_t
*chunk
))
71 Small_Object_Pool
* const pool
= chunk
->pool
;
72 Stack_Chunk_t
* const new_chunk
= (Stack_Chunk_t
*)pool
->get_free_object(interp
, pool
);
74 PObj_bufstart(new_chunk
) = NULL
;
75 PObj_buflen(new_chunk
) = 0;
77 new_chunk
->pool
= chunk
->pool
;
78 new_chunk
->name
= chunk
->name
;
85 =item C<Stack_Chunk_t * new_stack>
87 Create a new stack and name it. C<< stack->name >> is used for
88 debugging/error reporting.
95 PARROT_CANNOT_RETURN_NULL
96 PARROT_WARN_UNUSED_RESULT
98 new_stack(PARROT_INTERP
, ARGIN(const char *name
))
100 Small_Object_Pool
* const pool
= make_bufferlike_pool(interp
, sizeof (Stack_Chunk_t
));
101 Stack_Chunk_t
* const chunk
= (Stack_Chunk_t
*)(pool
->get_free_object
)(interp
, pool
);
103 chunk
->prev
= chunk
; /* mark the top of the stack */
105 chunk
->pool
= pool
; /* cache the pool pointer, for ease */
113 =item C<void mark_stack>
115 Mark entries in a stack structure during DOD.
123 mark_stack(PARROT_INTERP
, ARGMOD(Stack_Chunk_t
*chunk
))
125 for (; ; chunk
= chunk
->prev
) {
126 Stack_Entry_t
*entry
;
128 pobject_lives(interp
, (PObj
*)chunk
);
130 if (chunk
== chunk
->prev
)
133 entry
= STACK_DATAP(chunk
);
135 if (entry
->entry_type
== STACK_ENTRY_PMC
&& UVal_pmc(entry
->entry
))
136 pobject_lives(interp
, (PObj
*)UVal_pmc(entry
->entry
));
142 =item C<void stack_destroy>
144 stack_destroy() doesn't need to do anything, since GC does it all.
152 stack_destroy(SHIM(Stack_Chunk_t
*top
))
159 =item C<size_t stack_height>
161 Returns the height of the stack. The maximum "depth" is height - 1.
168 PARROT_WARN_UNUSED_RESULT
170 stack_height(SHIM_INTERP
, ARGIN(const Stack_Chunk_t
*chunk
))
174 for (; ; chunk
= chunk
->prev
) {
175 if (chunk
== chunk
->prev
)
186 =item C<Stack_Entry_t * stack_entry>
188 If C<< depth >= 0 >>, return the entry at that depth from the top of the
189 stack, with 0 being the top entry. If C<depth < 0>, then return the
190 entry C<|depth|> entries from the bottom of the stack. Returns C<NULL>
191 if C<|depth| > number> of entries in stack.
198 PARROT_CAN_RETURN_NULL
199 PARROT_WARN_UNUSED_RESULT
201 stack_entry(PARROT_INTERP
, ARGIN(Stack_Chunk_t
*stack
), INTVAL depth
)
203 Stack_Chunk_t
*chunk
;
204 size_t offset
= (size_t)depth
;
213 if (chunk
== chunk
->prev
)
219 if (chunk
== chunk
->prev
)
222 return STACK_DATAP(chunk
);
227 =item C<void rotate_entries>
229 Rotate the top N entries by one. If C<< N > 0 >>, the rotation is bubble
230 up, so the top most element becomes the Nth element. If C<< N < 0 >>, the
231 rotation is bubble down, so that the Nth element becomes the top most
238 rotate_entries(PARROT_INTERP
, ARGMOD(Stack_Chunk_t
**stack_p
), INTVAL num_entries
)
240 Stack_Chunk_t
* const stack
= *stack_p
;
242 if (num_entries
>= -1 && num_entries
<= 1) {
246 if (num_entries
< 0) {
251 num_entries
= -num_entries
;
252 depth
= num_entries
- 1;
254 if (stack_height(interp
, stack
) < (size_t)num_entries
)
255 Parrot_ex_throw_from_c_args(interp
, NULL
, ERROR_STACK_SHALLOW
,
256 "Stack too shallow!");
258 /* XXX Dereferencing stack_entry here is a cavalcade of danger */
259 temp
= *stack_entry(interp
, stack
, depth
);
260 for (i
= depth
; i
> 0; i
--) {
261 *stack_entry(interp
, stack
, i
) =
262 *stack_entry(interp
, stack
, i
- 1);
265 *stack_entry(interp
, stack
, 0) = temp
;
270 INTVAL depth
= num_entries
- 1;
272 if (stack_height(interp
, stack
) < (size_t)num_entries
)
273 Parrot_ex_throw_from_c_args(interp
, NULL
, ERROR_STACK_SHALLOW
,
274 "Stack too shallow!");
276 /* XXX Dereferencing stack_entry here is a cavalcade of danger */
277 temp
= *stack_entry(interp
, stack
, 0);
279 for (i
= 0; i
< depth
; i
++) {
280 *stack_entry(interp
, stack
, i
) =
281 *stack_entry(interp
, stack
, i
+ 1);
284 *stack_entry(interp
, stack
, depth
) = temp
;
290 =item C<Stack_Entry_t* stack_prepare_push>
292 Return a pointer, where new entries go for push.
299 PARROT_WARN_UNUSED_RESULT
300 PARROT_CANNOT_RETURN_NULL
302 stack_prepare_push(PARROT_INTERP
, ARGMOD(Stack_Chunk_t
**stack_p
))
304 Stack_Chunk_t
* const chunk
= *stack_p
;
305 Stack_Chunk_t
* const new_chunk
= cst_new_stack_chunk(interp
, chunk
);
307 new_chunk
->prev
= chunk
;
308 *stack_p
= new_chunk
;
310 return STACK_DATAP(new_chunk
);
315 =item C<void stack_push>
317 Push something on the generic stack.
319 Note that the cleanup pointer, if non-C<NULL>, points to a routine
320 that'll be called when the entry is removed from the stack. This is
321 handy for those cases where you need some sort of activity to take place
322 when an entry is removed, such as when you push a lexical lock onto the
323 call stack, or localize (or tempify, or whatever we're calling it)
324 variable or something.
330 stack_push(PARROT_INTERP
, ARGMOD(Stack_Chunk_t
**stack_p
),
331 ARGIN(void *thing
), Stack_entry_type type
, NULLOK(Stack_cleanup_method cleanup
))
333 Stack_Entry_t
* const entry
= (Stack_Entry_t
*)stack_prepare_push(interp
, stack_p
);
335 /* Remember the type */
336 entry
->entry_type
= type
;
338 /* Remember the cleanup function */
339 entry
->cleanup
= cleanup
;
341 /* Store our thing */
343 case STACK_ENTRY_MARK
:
344 UVal_int(entry
->entry
) = *(INTVAL
*)thing
;
346 case STACK_ENTRY_DESTINATION
:
347 UVal_ptr(entry
->entry
) = thing
;
349 case STACK_ENTRY_ACTION
:
350 case STACK_ENTRY_PMC
:
351 UVal_pmc(entry
->entry
) = (PMC
*)thing
;
354 Parrot_ex_throw_from_c_args(interp
, NULL
, ERROR_BAD_STACK_TYPE
,
355 "Invalid Stack_Entry_type!");
361 =item C<Stack_Entry_t* stack_prepare_pop>
363 Return a pointer, where new entries are popped off.
370 PARROT_WARN_UNUSED_RESULT
371 PARROT_CANNOT_RETURN_NULL
373 stack_prepare_pop(PARROT_INTERP
, ARGMOD(Stack_Chunk_t
**stack_p
))
375 Stack_Chunk_t
* const chunk
= *stack_p
;
377 /* the first entry (initial top) refers to itself */
378 if (chunk
== chunk
->prev
)
379 Parrot_ex_throw_from_c_args(interp
, NULL
, ERROR_STACK_EMPTY
,
380 "No entries on %s Stack!", chunk
->name
);
382 *stack_p
= chunk
->prev
;
384 return STACK_DATAP(chunk
);
389 =item C<void * stack_pop>
391 Pop off an entry and return a pointer to the contents.
396 PARROT_CAN_RETURN_NULL
398 stack_pop(PARROT_INTERP
, ARGMOD(Stack_Chunk_t
**stack_p
),
399 ARGOUT_NULLOK(void *where
), Stack_entry_type type
)
401 Stack_Chunk_t
*cur_chunk
= *stack_p
;
402 Stack_Entry_t
* const entry
=
403 (Stack_Entry_t
*)stack_prepare_pop(interp
, stack_p
);
405 /* Types of 0 mean we don't care */
406 if (type
&& entry
->entry_type
!= type
)
407 Parrot_ex_throw_from_c_args(interp
, NULL
, ERROR_BAD_STACK_TYPE
,
408 "Wrong type on top of stack!\n");
410 /* Cleanup routine? */
411 if (entry
->cleanup
!= STACK_CLEANUP_NULL
)
412 (*entry
->cleanup
) (interp
, entry
);
414 /* Sometimes the caller cares what the value was */
418 case STACK_ENTRY_MARK
:
419 *(INTVAL
*)where
= UVal_int(entry
->entry
);
421 case STACK_ENTRY_DESTINATION
:
422 *(void **)where
= UVal_ptr(entry
->entry
);
424 case STACK_ENTRY_ACTION
:
425 case STACK_ENTRY_PMC
:
426 *(PMC
**)where
= UVal_pmc(entry
->entry
);
429 Parrot_ex_throw_from_c_args(interp
, NULL
, ERROR_BAD_STACK_TYPE
,
430 "Wrong type on top of stack!\n");
434 /* recycle this chunk to the free list if it's otherwise unreferenced */
435 if (cur_chunk
->refcount
<= 0) {
436 Small_Object_Pool
* const pool
= cur_chunk
->pool
;
438 pool
->dod_object(interp
, pool
, (PObj
*)cur_chunk
);
439 pool
->add_free_object(interp
, pool
, (PObj
*)cur_chunk
);
447 =item C<void * pop_dest>
449 Pop off a destination entry and return a pointer to the contents.
456 PARROT_WARN_UNUSED_RESULT
457 PARROT_CANNOT_RETURN_NULL
459 pop_dest(PARROT_INTERP
)
461 /* We don't mind the extra call, so we do this: (previous comment
462 * said we *do* mind, but I say let the compiler decide) */
464 (void)stack_pop(interp
, &interp
->dynamic_env
,
465 &dest
, STACK_ENTRY_DESTINATION
);
471 =item C<void * stack_peek>
473 Peek at stack and return pointer to entry and the type of the entry.
480 PARROT_CAN_RETURN_NULL
481 PARROT_WARN_UNUSED_RESULT
483 stack_peek(PARROT_INTERP
, ARGIN(Stack_Chunk_t
*stack_base
),
484 ARGMOD_NULLOK(Stack_entry_type
*type
))
486 const Stack_Entry_t
* const entry
= stack_entry(interp
, stack_base
, 0);
491 *type
= entry
->entry_type
;
493 if (entry
->entry_type
== STACK_ENTRY_DESTINATION
)
494 return UVal_ptr(entry
->entry
);
496 return (void *) UVal_pmc(entry
->entry
);
501 =item C<Stack_entry_type get_entry_type>
503 Returns the stack entry type of C<entry>.
509 PARROT_WARN_UNUSED_RESULT
512 get_entry_type(ARGIN(const Stack_Entry_t
*entry
))
514 return entry
->entry_type
;
519 =item C<void Parrot_dump_dynamic_environment>
521 Print a representation of the dynamic stack to the standard error (using
522 C<PIO_eprintf>). This is used only temporarily for debugging.
530 Parrot_dump_dynamic_environment(PARROT_INTERP
, ARGIN(Stack_Chunk_t
*dynamic_env
))
532 int height
= (int) stack_height(interp
, dynamic_env
);
534 while (dynamic_env
->prev
!= dynamic_env
) {
535 const Stack_Entry_t
* const e
= stack_entry(interp
, dynamic_env
, 0);
538 Parrot_ex_throw_from_c_args(interp
, NULL
, 1, "Control stack damaged");
540 PIO_eprintf(interp
, "[%4d: chunk %p entry %p "
541 "type %d cleanup %p]\n",
542 height
, dynamic_env
, e
,
543 e
->entry_type
, e
->cleanup
);
544 if (e
->entry_type
== STACK_ENTRY_PMC
545 || e
->entry_type
== STACK_ENTRY_ACTION
) {
546 PMC
* const thing
= UVal_pmc(e
->entry
);
548 PIO_eprintf(interp
, "[ PMC %p type %d => %Ss]\n",
549 thing
, thing
->vtable
->base_type
,
550 VTABLE_get_string(interp
, thing
));
552 else if (e
->entry_type
== STACK_ENTRY_MARK
) {
553 PIO_eprintf(interp
, "[ mark %d]\n",
556 dynamic_env
= dynamic_env
->prev
;
559 PIO_eprintf(interp
, "[%4d: chunk %p %s base]\n",
560 height
, dynamic_env
, dynamic_env
->name
);
566 =item C<static void run_cleanup_action>
568 Runs the sub PMC from the Stack_Entry_t pointer with an INTVAL arg of 0. Used
569 in C<Parrot_push_action>.
576 run_cleanup_action(PARROT_INTERP
, ARGIN(Stack_Entry_t
*e
))
579 * this is called during normal stack_pop of the control
580 * stack - run the action subroutine with an INTVAL arg of 0
582 PMC
* const sub
= UVal_pmc(e
->entry
);
583 Parrot_runops_fromc_args(interp
, sub
, "vI", 0);
588 =item C<void Parrot_push_action>
590 Pushes an action handler onto the dynamic environment.
598 Parrot_push_action(PARROT_INTERP
, ARGIN(PMC
*sub
))
600 if (!VTABLE_isa(interp
, sub
, CONST_STRING(interp
, "Sub")))
601 Parrot_ex_throw_from_c_args(interp
, NULL
, 1,
602 "Tried to push a non Sub PMC action");
604 stack_push(interp
, &interp
->dynamic_env
, sub
,
605 STACK_ENTRY_ACTION
, run_cleanup_action
);
610 =item C<void Parrot_push_mark>
612 Push a cleanup mark onto the dynamic environment.
620 Parrot_push_mark(PARROT_INTERP
, INTVAL mark
)
622 stack_push(interp
, &interp
->dynamic_env
, &mark
,
623 STACK_ENTRY_MARK
, STACK_CLEANUP_NULL
);
628 =item C<void Parrot_pop_mark>
630 Pop items off the dynamic environment up to the mark.
638 Parrot_pop_mark(PARROT_INTERP
, INTVAL mark
)
641 const Stack_Entry_t
* const e
642 = stack_entry(interp
, interp
->dynamic_env
, 0);
644 Parrot_ex_throw_from_c_args(interp
, NULL
, 1, "Mark %ld not found.",
647 (void)stack_pop(interp
, &interp
->dynamic_env
,
648 NULL
, e
->entry_type
);
649 if (e
->entry_type
== STACK_ENTRY_MARK
) {
650 if (UVal_int(e
->entry
) == mark
)
663 F<include/parrot/stacks.h> and F<include/parrot/enums.h>.
672 * c-file-style: "parrot"
674 * vim: expandtab shiftwidth=4: