coro.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /* Definitions for the coroutine type.
  2. This file is part of khipu.
  3. khipu is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Lesser General Public License as published by
  5. the Free Software Foundation; either version 3 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License
  12. along with this program. If not, see <https://www.gnu.org/licenses/>. */
  13. #include <cstdio>
  14. #include "khipu.hpp"
  15. KP_DECLS_BEGIN
  16. static result<object>
  17. make_argv (interpreter *interp, uint32_t bp, coroutine *outp)
  18. {
  19. uint32_t max_sp = as_fct(interp->stack[bp - 1])->max_sp;
  20. uint32_t nsp = interp->stklen () - (bp - 1);
  21. KP_VTRY (alloc_array (interp, nsp + max_sp));
  22. array *ap = as_array (interp->alval);
  23. copy_objs (ap->data, &interp->stack[bp - 1], nsp);
  24. uint32_t nargs = as_int (interp->stack[interp->cur_frame - 3]);
  25. uint32_t start = interp->cur_frame - nargs - interpreter::frame_size - 1;
  26. outp->frame = interpreter::frame_size + 1 + nargs;
  27. ap->data[outp->frame - 4] = fixint (0);
  28. if (interp->exc_offset <= start)
  29. outp->exc_off = 0;
  30. else
  31. { // Adjust exception handler offsets.
  32. for (uint32_t eoff = interp->exc_offset ; ; )
  33. {
  34. uint32_t ipos = eoff - start;
  35. uint32_t prev = as_int (interp->stack[eoff]);
  36. if (prev < start)
  37. {
  38. ap->data[ipos] = fixint (0);
  39. break;
  40. }
  41. ap->data[ipos] = fixint (prev - start);
  42. eoff = prev;
  43. }
  44. outp->exc_off = interp->exc_offset - start;
  45. }
  46. return (ap->as_obj ());
  47. }
  48. struct coro_state : call_guard
  49. {
  50. valref stkobj;
  51. uint32_t saved_objlen;
  52. coro_state (interpreter *ip) : call_guard (ip),
  53. stkobj (ip, ip->stkobj), saved_objlen (len_a (ip->stkobj))
  54. { // Make sure the GC doesn't scan past the interpreter's stack end.
  55. as_array(ip->stkobj)->len = this->sp;
  56. }
  57. ~coro_state ()
  58. {
  59. as_array(*this->stkobj)->len = saved_objlen;
  60. this->interp->stkobj = *this->stkobj;
  61. this->interp->stack = &xaref(*this->stkobj, 0);
  62. }
  63. };
  64. result<object> coro_next (interpreter *interp, object *argv, int argc)
  65. {
  66. auto crp = as<coroutine> (*argv);
  67. if (!crp)
  68. return (interp->raise ("type-error", "argument must be a coroutine"));
  69. else if (!crp->valid_p ())
  70. return (interp->raise ("arg-error", "coroutine exhausted"));
  71. coro_state state (interp);
  72. object value = argc == 2 ? argv[1] : crp->value;
  73. interp->stkobj = crp->argv;
  74. interp->stack = &xaref(interp->stkobj, 0);
  75. interp->stkend = interp->stack + crp->frame + crp->sp_diff;
  76. interp->cur_frame = 0;
  77. interp->exc_offset = crp->exc_off;
  78. if (kp_unlikely (array_p (crp->dbinds)))
  79. // Rebind the dynamic variables.
  80. for (uint32_t i = 0; i < len_a (crp->dbinds); i += 2)
  81. interp->tl_syms[symtlidx (xaref (crp->dbinds, i)) - 1] =
  82. xaref (crp->dbinds, i + 1);
  83. auto ret = call_coroutine (interp, crp, value);
  84. if (ret.error_p ())
  85. { // Generate the stacktrace from the coroutine and rethrow.
  86. KP_VTRY (interp->stacktrace (interp->throw_frame));
  87. return (exception ());
  88. }
  89. else if (!coro_p (*ret))
  90. {
  91. crp->mark_invalid ();
  92. kp_return (NIL);
  93. }
  94. kp_return (*ret);
  95. }
  96. coroutine* coroutine::alloc_raw ()
  97. {
  98. coroutine *ret = alloch<coroutine> ();
  99. ret->value = ret->argv = ret->dbinds = NIL;
  100. ret->frame = 0u;
  101. ret->ip_offset = ret->sp_diff = 0;
  102. return (ret);
  103. }
  104. object alloc_coroutine (interpreter *interp)
  105. {
  106. auto ret = coroutine::alloc_raw ();
  107. interp->alval = ret->as_obj ();
  108. gc_register (interp, ret);
  109. return (interp->alval);
  110. }
  111. result<object> coroutine::make (interpreter *interp, uint32_t bp)
  112. {
  113. auto eg = KP_TRY (evh_guard::make (interp));
  114. coroutine *ret = coroutine::alloc_raw ();
  115. ret->value = UNBOUND;
  116. ret->argv = KP_TRY (make_argv (interp, bp, ret));
  117. interp->alval = ret->as_obj ();
  118. gc_register (interp, ret);
  119. return (interp->alval);
  120. }
  121. result<int64_t> pack_G (interpreter *interp, stream *strm,
  122. object obj, pack_info& info)
  123. {
  124. coroutine *crp = as_coro (obj);
  125. int64_t ret = KP_TRY (strm->write (interp, &crp->ip_offset));
  126. ret += KP_TRY (strm->write (interp, &crp->sp_diff));
  127. ret += KP_TRY (strm->write (interp, &crp->exc_off));
  128. ret += KP_TRY (strm->write (interp, &crp->frame));
  129. ret += KP_TRY (xpack (interp, strm, crp->value, info));
  130. ret += KP_TRY (xpack (interp, strm, crp->argv, info));
  131. return (ret);
  132. }
  133. result<object> unpack_G (interpreter *interp, stream *strm,
  134. pack_info& info, bool save)
  135. {
  136. valref ret (interp, alloc_coroutine (interp));
  137. coroutine *crp = as_coro (*ret);
  138. bool rv = KP_TRY (strm->sread (interp, &crp->ip_offset));
  139. rv = rv && KP_TRY (strm->sread (interp, &crp->sp_diff));
  140. rv = rv && KP_TRY (strm->sread (interp, &crp->exc_off));
  141. rv = rv && KP_TRY (strm->sread (interp, &crp->frame));
  142. if (!rv)
  143. return (info.error ("invalid offsets read"));
  144. crp->value = KP_TRY (xunpack (interp, strm, info));
  145. crp->argv = KP_TRY (xunpack (interp, strm, info));
  146. if (!array_p (crp->argv))
  147. return (info.error ("coroutine stack must be an array"));
  148. else if (save)
  149. KP_VTRY (info.add_mapping (interp, *info.offset, *ret));
  150. kp_return (*ret);
  151. }
  152. KP_DECLS_END