1use std::cell::Cell;
19use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
20use std::sync::OnceLock;
21
22use crate::state_stub::{lua_CFunction, upvalue_index, LuaState, LuaStateStubExt as _};
23use lua_types::{error::LuaError, gc::GcRef, value::LuaValue, LuaStatus, LuaThreadClose, LuaType};
24
25thread_local! {
26 static THREAD_CLOSE_SUPPRESS: Cell<u32> = const { Cell::new(0) };
44}
45
46static CHAINING_HOOK_INSTALLED: OnceLock<()> = OnceLock::new();
48
49struct SuppressGuard;
57
58impl SuppressGuard {
59 fn new() -> Self {
60 THREAD_CLOSE_SUPPRESS.with(|c| c.set(c.get() + 1));
61 SuppressGuard
62 }
63}
64
65impl Drop for SuppressGuard {
66 fn drop(&mut self) {
67 THREAD_CLOSE_SUPPRESS.with(|c| c.set(c.get().saturating_sub(1)));
68 }
69}
70
71fn ensure_chaining_panic_hook() {
98 CHAINING_HOOK_INSTALLED.get_or_init(|| {
99 let previous = std::panic::take_hook();
100 std::panic::set_hook(Box::new(move |info| {
101 let suppress = info.payload().downcast_ref::<LuaThreadClose>().is_some()
102 && THREAD_CLOSE_SUPPRESS.with(|c| c.get()) > 0;
103 if !suppress {
104 previous(info);
105 }
106 }));
107 });
108}
109
110const COS_RUN: i32 = 0;
114
115const COS_DEAD: i32 = 1;
117
118const COS_YIELD: i32 = 2;
120
121const COS_NORM: i32 = 3;
123
124const STAT_NAMES: [&[u8]; 4] = [b"running", b"dead", b"suspended", b"normal"];
128
129pub const CO_FUNCS: &[(&[u8], lua_CFunction)] = &[
137 (b"create", co_create),
138 (b"resume", co_resume),
139 (b"running", co_running),
140 (b"status", co_status),
141 (b"wrap", co_wrap),
142 (b"yield", co_yield),
143 (b"isyieldable", co_isyieldable),
144 (b"close", co_close),
145];
146
147fn get_co(state: &mut LuaState) -> Result<GcRef<lua_types::value::LuaThread>, LuaError> {
153 let co = state.to_thread(1);
154 if co.is_none() {
155 let got = state.arg(1);
156 return Err(LuaError::type_arg_error(1, "thread", &got));
157 }
158 Ok(co.expect("checked above"))
159}
160
161fn get_opt_co(state: &mut LuaState) -> Result<GcRef<lua_types::value::LuaThread>, LuaError> {
162 if matches!(state.global().lua_version, lua_types::LuaVersion::V55)
163 && state.type_at(1) == LuaType::None
164 {
165 let id = state.global().current_thread_id;
166 return state
167 .global()
168 .thread_value_for(id)
169 .ok_or_else(|| LuaError::runtime(format_args!("current thread is not registered")));
170 }
171 get_co(state)
172}
173
174fn aux_status(state: &mut LuaState, co: &GcRef<lua_types::value::LuaThread>) -> i32 {
186 let co_id = co.id;
187 let entry_rc = {
188 let g = state.global();
189 if co_id == g.current_thread_id {
190 return COS_RUN;
191 }
192 if co_id == g.main_thread_id {
193 return COS_NORM;
194 }
195 match g.threads.get(&co_id) {
196 Some(e) => e.state.clone(),
197 None => return COS_DEAD,
198 }
199 };
200 let co_state = match entry_rc.try_borrow() {
201 Ok(state) => state,
202 Err(_) => {
203 return COS_NORM;
208 }
209 };
210 let raw_status = co_state.status;
211 if raw_status == LuaStatus::Yield as u8 {
212 return COS_YIELD;
213 }
214 if raw_status != LuaStatus::Ok as u8 {
215 return COS_DEAD;
216 }
217 let has_frames = co_state.ci.as_usize() > 0;
218 if has_frames {
219 return COS_NORM;
220 }
221 let ci_func = co_state.call_info[0].func.0;
222 let top = co_state.top.0;
223 let lua_gettop = top as i64 - ci_func as i64 - 1;
224 if lua_gettop == 0 {
225 COS_DEAD
226 } else {
227 COS_YIELD
228 }
229}
230
231fn aux_resume(state: &mut LuaState, co: GcRef<lua_types::value::LuaThread>, narg: i32) -> i32 {
248 let co_id = co.id;
249 let entry_rc = {
250 let g = state.global();
251 match g.threads.get(&co_id) {
252 Some(e) => e.state.clone(),
253 None => {
254 drop(g);
255 push_lit_or_nil(state, b"cannot resume dead coroutine");
256 return -1;
257 }
258 }
259 };
260 let parent_thread_id = state.global().current_thread_id;
261 let top_before = state.get_top();
262 if top_before < narg {
263 push_lit_or_nil(state, b"not enough arguments to resume");
264 return -1;
265 }
266 let first_arg_idx = top_before - narg + 1;
267 let mut args = pop_resume_value_buf(state);
268 args.extend((first_arg_idx..=top_before).map(|i| state.value_at(i)));
269 lua_vm::api::set_top(state, (top_before - narg) as i32).ok();
270
271 let mut parent_open_upval_slots = pop_resume_slot_buf(state);
272 parent_open_upval_slots.extend(state.openupval.iter().filter_map(|uv| {
273 uv.try_open_payload()
274 .map(|(thread_id, idx)| (thread_id as u64, idx))
275 }));
276 {
277 let mut g = state.global_mut();
278 for (tid, idx) in &parent_open_upval_slots {
279 let val = state.get_at(*idx);
280 g.cross_thread_upvals.insert((*tid, *idx), val);
281 }
282 }
283
284 push_parent_gc_snapshot(state);
285
286 let (status, results_or_err): (LuaStatus, Vec<LuaValue>) = {
287 let mut co_state = match entry_rc.try_borrow_mut() {
288 Ok(b) => b,
289 Err(_) => {
290 pop_parent_gc_snapshot(state);
291 let mut g = state.global_mut();
292 for (tid, idx) in &parent_open_upval_slots {
293 g.cross_thread_upvals.remove(&(*tid, *idx));
294 }
295 drop(g);
296 return_resume_slot_buf(state, parent_open_upval_slots);
297 return_resume_value_buf(state, args);
298 push_lit_or_nil(state, b"cannot resume non-suspended coroutine");
299 return -1;
300 }
301 };
302 if co_state.check_stack(narg + 1).is_err() {
303 drop(co_state);
304 pop_parent_gc_snapshot(state);
305 let mut g = state.global_mut();
306 for (tid, idx) in &parent_open_upval_slots {
307 g.cross_thread_upvals.remove(&(*tid, *idx));
308 }
309 drop(g);
310 return_resume_slot_buf(state, parent_open_upval_slots);
311 return_resume_value_buf(state, args);
312 push_lit_or_nil(state, b"too many arguments to resume");
313 return -1;
314 }
315 for v in args.drain(..) {
316 co_state.push(v);
317 }
318 return_resume_value_buf(state, args);
319 co_state.global_mut().current_thread_id = co_id;
320 let mut nres: i32 = 0;
321 ensure_chaining_panic_hook();
322 let resume_result = {
323 let _suppress = SuppressGuard::new();
324 catch_unwind(AssertUnwindSafe(|| {
325 lua_vm::do_::lua_resume(&mut *co_state, Some(state), narg, &mut nres)
326 }))
327 };
328 co_state.global_mut().current_thread_id = parent_thread_id;
329 let status = match resume_result {
330 Ok(status) => status,
331 Err(payload) => {
332 if let Some(close) = payload.downcast_ref::<LuaThreadClose>() {
333 close.0
334 } else {
335 resume_unwind(payload);
336 }
337 }
338 };
339 let co_top = co_state.top_idx().0 as i32;
340 let ci_func = co_state.current_call_info().func.0 as i32;
341 let count = if status == LuaStatus::Ok || status == LuaStatus::Yield {
342 nres
343 } else {
344 1
345 };
346 let start = co_top - count;
347 let mut vals = pop_resume_value_buf(state);
348 vals.extend((start..co_top).map(|i| co_state.get_at(lua_vm::state::StackIdx(i as u32))));
349 let new_co_top = if status == LuaStatus::Ok || status == LuaStatus::Yield {
350 (co_top - count).max(ci_func + 1)
351 } else {
352 co_top - count
353 };
354 co_state.set_top(lua_vm::state::StackIdx(new_co_top.max(0) as u32));
355 (status, vals)
356 };
357
358 pop_parent_gc_snapshot(state);
360
361 {
362 let mut flush = pop_resume_flush_buf(state);
363 let mut g = state.global_mut();
364 for (tid, idx) in &parent_open_upval_slots {
365 if let Some(v) = g.cross_thread_upvals.remove(&(*tid, *idx)) {
366 flush.push((*idx, v));
367 }
368 }
369 drop(g);
370 for (idx, v) in flush.drain(..) {
371 state.set_at(idx, v);
372 }
373 return_resume_flush_buf(state, flush);
374 }
375 return_resume_slot_buf(state, parent_open_upval_slots);
376
377 let mut results_or_err = results_or_err;
378 match status {
379 LuaStatus::Ok | LuaStatus::Yield => {
380 if state.check_stack(results_or_err.len() as i32 + 1).is_err() {
381 return_resume_value_buf(state, results_or_err);
382 push_lit_or_nil(state, b"too many results to resume");
383 return -1;
384 }
385 let n = results_or_err.len();
386 for v in results_or_err.drain(..) {
387 state.push(v);
388 }
389 return_resume_value_buf(state, results_or_err);
390 n as i32
391 }
392 _ => {
393 for v in results_or_err.drain(..) {
394 state.push(v);
395 }
396 return_resume_value_buf(state, results_or_err);
397 -1
398 }
399 }
400}
401
402fn push_parent_gc_snapshot(state: &mut LuaState) {
403 let top = (state.top_idx().0 as usize).min(state.stack.len());
404 let (mut stack_snapshot, mut upval_snapshot) = {
405 let mut g = state.global_mut();
406 (
407 g.snapshot_stack_pool.pop().unwrap_or_default(),
408 g.snapshot_upval_pool.pop().unwrap_or_default(),
409 )
410 };
411 stack_snapshot.extend(state.stack[..top].iter().map(|sv| sv.val));
412 upval_snapshot.extend(state.openupval.iter().cloned());
413 let mut g = state.global_mut();
414 g.suspended_parent_stacks.push(stack_snapshot);
415 g.suspended_parent_open_upvals.push(upval_snapshot);
416}
417
418fn pop_parent_gc_snapshot(state: &mut LuaState) {
419 let mut g = state.global_mut();
420 if let Some(mut v) = g.suspended_parent_open_upvals.pop() {
421 v.clear();
422 g.snapshot_upval_pool.push(v);
423 }
424 if let Some(mut v) = g.suspended_parent_stacks.pop() {
425 v.clear();
426 g.snapshot_stack_pool.push(v);
427 }
428}
429
430fn pop_resume_slot_buf(state: &mut LuaState) -> Vec<(u64, lua_vm::state::StackIdx)> {
435 state.global_mut().resume_upval_slot_pool.pop().unwrap_or_default()
436}
437
438fn return_resume_slot_buf(state: &mut LuaState, mut buf: Vec<(u64, lua_vm::state::StackIdx)>) {
441 buf.clear();
442 state.global_mut().resume_upval_slot_pool.push(buf);
443}
444
445fn pop_resume_value_buf(state: &mut LuaState) -> Vec<LuaValue> {
449 state.global_mut().resume_value_pool.pop().unwrap_or_default()
450}
451
452fn return_resume_value_buf(state: &mut LuaState, mut buf: Vec<LuaValue>) {
455 buf.clear();
456 state.global_mut().resume_value_pool.push(buf);
457}
458
459fn pop_resume_flush_buf(state: &mut LuaState) -> Vec<(lua_vm::state::StackIdx, LuaValue)> {
463 state.global_mut().resume_flush_pool.pop().unwrap_or_default()
464}
465
466fn return_resume_flush_buf(state: &mut LuaState, mut buf: Vec<(lua_vm::state::StackIdx, LuaValue)>) {
469 buf.clear();
470 state.global_mut().resume_flush_pool.push(buf);
471}
472
473pub(crate) struct RootedThreadBorrow<'a> {
491 inner: std::cell::RefMut<'a, LuaState>,
492}
493
494impl RootedThreadBorrow<'_> {
495 pub(crate) fn resnapshot(&mut self) {
499 let top = (self.inner.top_idx().0 as usize).min(self.inner.stack.len());
500 let stack_copy: Vec<LuaValue> = self.inner.stack[..top].iter().map(|sv| sv.val).collect();
501 let upval_copy: Vec<GcRef<lua_types::UpVal>> = self.inner.openupval.to_vec();
502 let mut g = self.inner.global_mut();
503 if let Some(slot) = g.suspended_parent_stacks.last_mut() {
504 slot.clear();
505 slot.extend(stack_copy);
506 }
507 if let Some(slot) = g.suspended_parent_open_upvals.last_mut() {
508 slot.clear();
509 slot.extend(upval_copy);
510 }
511 }
512}
513
514impl std::ops::Deref for RootedThreadBorrow<'_> {
515 type Target = LuaState;
516 fn deref(&self) -> &LuaState {
517 &self.inner
518 }
519}
520
521impl std::ops::DerefMut for RootedThreadBorrow<'_> {
522 fn deref_mut(&mut self) -> &mut LuaState {
523 &mut self.inner
524 }
525}
526
527impl Drop for RootedThreadBorrow<'_> {
528 fn drop(&mut self) {
529 let mut g = self.inner.global_mut();
530 if let Some(mut v) = g.suspended_parent_open_upvals.pop() {
531 v.clear();
532 g.snapshot_upval_pool.push(v);
533 }
534 if let Some(mut v) = g.suspended_parent_stacks.pop() {
535 v.clear();
536 g.snapshot_stack_pool.push(v);
537 }
538 }
539}
540
541pub(crate) fn borrow_thread_rooted<'a>(
545 state: &mut LuaState,
546 cell: &'a std::cell::RefCell<LuaState>,
547) -> RootedThreadBorrow<'a> {
548 let inner = cell.borrow_mut();
549 let top = (inner.top_idx().0 as usize).min(inner.stack.len());
550 let (mut stack_snapshot, mut upval_snapshot) = {
551 let mut g = state.global_mut();
552 (
553 g.snapshot_stack_pool.pop().unwrap_or_default(),
554 g.snapshot_upval_pool.pop().unwrap_or_default(),
555 )
556 };
557 stack_snapshot.extend(inner.stack[..top].iter().map(|sv| sv.val));
558 upval_snapshot.extend(inner.openupval.iter().cloned());
559 let mut g = state.global_mut();
560 g.suspended_parent_stacks.push(stack_snapshot);
561 g.suspended_parent_open_upvals.push(upval_snapshot);
562 drop(g);
563 RootedThreadBorrow { inner }
564}
565
566fn push_lit_or_nil(state: &mut LuaState, bytes: &[u8]) {
568 match state.intern_str(bytes) {
569 Ok(s) => state.push(LuaValue::Str(s)),
570 Err(_) => state.push(LuaValue::Nil),
571 }
572}
573
574pub fn co_resume(state: &mut LuaState) -> Result<usize, LuaError> {
582 let co = get_co(state)?;
583 let narg = state.get_top() - 1;
586 let r = aux_resume(state, co, narg);
587 if r < 0 {
588 if state.sandbox_aborting() {
592 let top = state.get_top();
593 let err_val = state.value_at(top);
594 return Err(LuaError::from_value(err_val));
595 }
596 state.push(LuaValue::Bool(false));
597 state.insert(-2)?;
598 Ok(2)
599 } else {
600 state.push(LuaValue::Bool(true));
601 state.insert(-(r + 1))?;
602 Ok((r + 1) as usize)
603 }
604}
605
606fn aux_wrap(state: &mut LuaState) -> Result<usize, LuaError> {
614 let up = state.value_at(upvalue_index(1));
615 let co = match up {
616 LuaValue::Thread(t) => t,
617 _ => {
618 return Err(LuaError::runtime(format_args!(
619 "coroutine.wrap: upvalue is not a thread"
620 )))
621 }
622 };
623 let narg = state.get_top();
624 let r = aux_resume(state, co.clone(), narg);
625 if r < 0 {
626 let top = state.get_top();
627 let mut err_val = state.value_at(top);
628 if aux_status(state, &co) == COS_DEAD {
629 let old_err = state.pop();
630 let nclose = close_suspended_or_dead(state, co)?;
631 err_val = if nclose >= 2 {
632 let top = state.get_top();
633 state.value_at(top)
634 } else {
635 old_err
636 };
637 state.pop_n(nclose);
638 }
639 Err(LuaError::from_value(err_val))
640 } else {
641 Ok(r as usize)
642 }
643}
644
645pub fn co_create(state: &mut LuaState) -> Result<usize, LuaError> {
658 state.check_arg_type(1, LuaType::Function)?;
659 let body = state.value_at(1);
660 let _nl = state.new_thread(Some(body))?;
661 Ok(1)
662}
663
664pub fn co_wrap(state: &mut LuaState) -> Result<usize, LuaError> {
672 co_create(state)?;
673 state.push_cclosure(aux_wrap, 1)?;
674 Ok(1)
675}
676
677pub fn co_yield(state: &mut LuaState) -> Result<usize, LuaError> {
684 let n = state.get_top();
685 let r = lua_vm::do_::lua_yieldk(state, n, 0, None)?;
686 Ok(r as usize)
687}
688
689pub fn co_status(state: &mut LuaState) -> Result<usize, LuaError> {
694 let co = get_co(state)?;
695 let idx = aux_status(state, &co) as usize;
696 let name: &[u8] = STAT_NAMES[idx];
697 let interned = state.intern_str(name)?;
698 state.push(LuaValue::Str(interned));
699 Ok(1)
700}
701
702pub fn co_isyieldable(state: &mut LuaState) -> Result<usize, LuaError> {
706 let is_yieldable = if matches!(state.type_at(1), LuaType::None) {
707 state.is_yieldable()
708 } else {
709 let co = get_co(state)?;
710 let co_id = co.id;
711 let (is_main, is_current) = {
712 let g = state.global();
713 (co_id == g.main_thread_id, co_id == g.current_thread_id)
714 };
715 if is_main {
716 false
717 } else if is_current {
718 state.is_yieldable()
719 } else {
720 let entry_rc = {
721 let g = state.global();
722 g.threads
723 .get(&co_id)
724 .expect("thread value carries an id that must resolve in GlobalState::threads")
725 .state
726 .clone()
727 };
728 let target_is_yieldable = match entry_rc.try_borrow() {
729 Ok(b) => b.is_yieldable(),
730 Err(_) => false,
731 };
732 target_is_yieldable
733 }
734 };
735 state.push(LuaValue::Bool(is_yieldable));
736 Ok(1)
737}
738
739pub fn co_running(state: &mut LuaState) -> Result<usize, LuaError> {
744 let is_main = state.push_thread()?;
747 if matches!(state.global().lua_version, lua_types::LuaVersion::V51) {
753 if is_main {
754 state.pop_n(1);
755 state.push(LuaValue::Nil);
756 }
757 return Ok(1);
758 }
759 state.push(LuaValue::Bool(is_main));
760 Ok(2)
761}
762
763pub fn co_close(state: &mut LuaState) -> Result<usize, LuaError> {
771 lua_vm::state::inc_c_stack(state)?;
772 let result = (|| {
773 let co = get_opt_co(state)?;
774 let status = aux_status(state, &co);
775 match status {
776 COS_DEAD | COS_YIELD => close_suspended_or_dead(state, co),
777 _ => {
778 if matches!(state.global().lua_version, lua_types::LuaVersion::V55)
779 && status == COS_RUN
780 && state.global().closing_thread_id == Some(co.id)
781 {
782 state.push(LuaValue::Bool(true));
783 return Ok(1);
784 }
785 if matches!(state.global().lua_version, lua_types::LuaVersion::V55)
786 && status == COS_RUN
787 && co.id == state.global().main_thread_id
788 {
789 return Err(LuaError::runtime(format_args!("cannot close main thread")));
790 }
791 if matches!(state.global().lua_version, lua_types::LuaVersion::V55)
792 && status == COS_RUN
793 && co.id == state.global().current_thread_id
794 {
795 state.global_mut().closing_thread_id = Some(co.id);
796 let in_status = state.status as i32;
797 let s = lua_vm::state::reset_thread(state, in_status);
798 state.global_mut().closing_thread_id = None;
799 state.n_ccalls = state.n_ccalls.saturating_sub(1);
800 std::panic::panic_any(LuaThreadClose(LuaStatus::from_raw(s)));
801 }
802 let name = if status == COS_RUN {
803 "running"
804 } else {
805 "normal"
806 };
807 Err(LuaError::runtime(format_args!(
808 "cannot close a {} coroutine",
809 name
810 )))
811 }
812 }
813 })();
814 state.n_ccalls -= 1;
815 result
816}
817
818fn close_suspended_or_dead(
820 state: &mut LuaState,
821 co: GcRef<lua_types::value::LuaThread>,
822) -> Result<usize, LuaError> {
823 let co_id = co.id;
824 let entry_rc_opt = {
825 let g = state.global();
826 g.threads.get(&co_id).map(|e| e.state.clone())
827 };
828 let entry_rc = match entry_rc_opt {
829 Some(rc) => rc,
830 None => {
831 state.push(LuaValue::Bool(true));
832 return Ok(1);
833 }
834 };
835 let parent_thread_id = state.global().current_thread_id;
836 let caller_c_calls = state.c_calls();
837
838 let mut parent_open_upval_slots = pop_resume_slot_buf(state);
839 parent_open_upval_slots.extend(state.openupval.iter().filter_map(|uv| {
840 uv.try_open_payload()
841 .map(|(thread_id, idx)| (thread_id as u64, idx))
842 }));
843 {
844 let mut g = state.global_mut();
845 for (tid, idx) in &parent_open_upval_slots {
846 let val = state.get_at(*idx);
847 g.cross_thread_upvals.insert((*tid, *idx), val);
848 }
849 }
850
851 push_parent_gc_snapshot(state);
852
853 let (status, err_value): (i32, Option<LuaValue>) = {
854 let mut co_state = entry_rc.borrow_mut();
855 co_state.global_mut().current_thread_id = co_id;
856 co_state.global_mut().closing_thread_id = Some(co_id);
857 co_state.n_ccalls = caller_c_calls;
858 let in_status = co_state.status as i32;
859 let s = lua_vm::state::reset_thread(&mut *co_state, in_status);
860 co_state.global_mut().closing_thread_id = None;
861 co_state.global_mut().current_thread_id = parent_thread_id;
862 if s == LuaStatus::Ok as i32 {
863 (s, None)
864 } else {
865 let top = co_state.top_idx().0;
866 if top > 0 {
867 let err = co_state.get_at(lua_vm::state::StackIdx(top - 1));
868 co_state.set_top(lua_vm::state::StackIdx(top - 1));
869 (s, Some(err))
870 } else {
871 (s, Some(LuaValue::Nil))
872 }
873 }
874 };
875
876 pop_parent_gc_snapshot(state);
877
878 {
879 let mut flush = pop_resume_flush_buf(state);
880 let mut g = state.global_mut();
881 for (tid, idx) in &parent_open_upval_slots {
882 if let Some(v) = g.cross_thread_upvals.remove(&(*tid, *idx)) {
883 flush.push((*idx, v));
884 }
885 }
886 drop(g);
887 for (idx, v) in flush.drain(..) {
888 state.set_at(idx, v);
889 }
890 return_resume_flush_buf(state, flush);
891 }
892 return_resume_slot_buf(state, parent_open_upval_slots);
893
894 if status == LuaStatus::Ok as i32 {
895 state.push(LuaValue::Bool(true));
896 Ok(1)
897 } else {
898 state.push(LuaValue::Bool(false));
899 if let Some(v) = err_value {
900 state.push(v);
901 } else {
902 state.push(LuaValue::Nil);
903 }
904 Ok(2)
905 }
906}
907
908pub fn open_coroutine(state: &mut LuaState) -> Result<usize, LuaError> {
914 use lua_types::LuaVersion;
918 let version = state.global().lua_version;
919 let has_close = !matches!(version, LuaVersion::V51 | LuaVersion::V52 | LuaVersion::V53);
920 let has_isyieldable = !matches!(version, LuaVersion::V51 | LuaVersion::V52);
924 if has_close && has_isyieldable {
925 state.new_lib(CO_FUNCS)?;
926 } else {
927 let filtered: Vec<(&[u8], lua_CFunction)> = CO_FUNCS
928 .iter()
929 .filter(|(name, _)| {
930 (has_close || *name != b"close".as_slice())
931 && (has_isyieldable || *name != b"isyieldable".as_slice())
932 })
933 .copied()
934 .collect();
935 state.new_lib(&filtered)?;
936 }
937 Ok(1)
938}
939
940