@@ -8,7 +8,7 @@ use super::{ |
8 | 8 | #[cfg(feature = "jit")] |
9 | 9 | use crate::common::lock::OnceCell; |
10 | 10 | use crate::common::lock::PyMutex; |
11 | | -use crate::convert::ToPyObject; |
| 11 | +use crate::convert::{ToPyObject, TryFromObject}; |
12 | 12 | use crate::function::ArgMapping; |
13 | 13 | use crate::object::{Traverse, TraverseFn}; |
14 | 14 | use crate::{ |
@@ -31,6 +31,7 @@ use rustpython_jit::CompiledCode; |
31 | 31 | pub struct PyFunction { |
32 | 32 | code: PyRef<PyCode>, |
33 | 33 | globals: PyDictRef, |
| 34 | + builtins: PyObjectRef, |
34 | 35 | closure: Option<PyTupleTyped<PyCellRef>>, |
35 | 36 | defaults_and_kwdefaults: PyMutex<(Option<PyTupleRef>, Option<PyDictRef>)>, |
36 | 37 | name: PyMutex<PyStrRef>, |
@@ -53,6 +54,7 @@ unsafe impl Traverse for PyFunction { |
53 | 54 |
|
54 | 55 | impl PyFunction { |
55 | 56 | #[allow(clippy::too_many_arguments)] |
| 57 | + #[inline] |
56 | 58 | pub(crate) fn new( |
57 | 59 | code: PyRef<PyCode>, |
58 | 60 | globals: PyDictRef, |
@@ -62,24 +64,42 @@ impl PyFunction { |
62 | 64 | qualname: PyStrRef, |
63 | 65 | type_params: PyTupleRef, |
64 | 66 | annotations: PyDictRef, |
65 | | - module: PyObjectRef, |
66 | 67 | doc: PyObjectRef, |
67 | | - ) -> Self { |
| 68 | + vm: &VirtualMachine, |
| 69 | + ) -> PyResult<Self> { |
68 | 70 | let name = PyMutex::new(code.obj_name.to_owned()); |
69 | | - PyFunction { |
| 71 | + let module = vm.unwrap_or_none(globals.get_item_opt(identifier!(vm, __name__), vm)?); |
| 72 | + let builtins = globals.get_item("__builtins__", vm).unwrap_or_else(|_| { |
| 73 | + // If not in globals, inherit from current execution context |
| 74 | + if let Some(frame) = vm.current_frame() { |
| 75 | + frame.builtins.clone().into() |
| 76 | + } else { |
| 77 | + vm.builtins.clone().into() |
| 78 | + } |
| 79 | + }); |
| 80 | + |
| 81 | + let func = PyFunction { |
70 | 82 | code, |
71 | 83 | globals, |
| 84 | + builtins, |
72 | 85 | closure, |
73 | 86 | defaults_and_kwdefaults: PyMutex::new((defaults, kw_only_defaults)), |
74 | 87 | name, |
75 | 88 | qualname: PyMutex::new(qualname), |
76 | 89 | type_params: PyMutex::new(type_params), |
77 | | - #[cfg(feature = "jit")] |
78 | | - jitted_code: OnceCell::new(), |
79 | 90 | annotations: PyMutex::new(annotations), |
80 | 91 | module: PyMutex::new(module), |
81 | 92 | doc: PyMutex::new(doc), |
82 | | - } |
| 93 | + #[cfg(feature = "jit")] |
| 94 | + jitted_code: OnceCell::new(), |
| 95 | + }; |
| 96 | + |
| 97 | + // let name = qualname.as_str().split('.').next_back().unwrap(); |
| 98 | + // func.set_attr(identifier!(vm, __name__), vm.new_pyobj(name), vm)?; |
| 99 | + // func.set_attr(identifier!(vm, __qualname__), qualname, vm)?; |
| 100 | + // func.set_attr(identifier!(vm, __doc__), doc, vm)?; |
| 101 | + |
| 102 | + Ok(func) |
83 | 103 | } |
84 | 104 |
|
85 | 105 | fn fill_locals_from_args( |
@@ -362,7 +382,7 @@ impl PyPayload for PyFunction { |
362 | 382 | } |
363 | 383 |
|
364 | 384 | #[pyclass( |
365 | | - with(GetDescriptor, Callable, Representable), |
| 385 | + with(GetDescriptor, Callable, Representable, Constructor), |
366 | 386 | flags(HAS_DICT, METHOD_DESCRIPTOR) |
367 | 387 | )] |
368 | 388 | impl PyFunction { |
@@ -406,6 +426,12 @@ impl PyFunction { |
406 | 426 | Ok(vm.unwrap_or_none(zelf.closure.clone().map(|x| x.to_pyobject(vm)))) |
407 | 427 | } |
408 | 428 |
|
| 429 | + #[pymember(magic)] |
| 430 | + fn builtins(vm: &VirtualMachine, zelf: PyObjectRef) -> PyResult { |
| 431 | + let zelf = Self::_as_pyref(&zelf, vm)?; |
| 432 | + Ok(zelf.builtins.clone()) |
| 433 | + } |
| 434 | + |
409 | 435 | #[pygetset(magic)] |
410 | 436 | fn name(&self) -> PyStrRef { |
411 | 437 | self.name.lock().clone() |
@@ -555,6 +581,81 @@ impl Representable for PyFunction { |
555 | 581 | } |
556 | 582 | } |
557 | 583 |
|
| 584 | +#[derive(FromArgs)] |
| 585 | +pub struct PyFunctionNewArgs { |
| 586 | + #[pyarg(positional)] |
| 587 | + code: PyRef<PyCode>, |
| 588 | + #[pyarg(positional)] |
| 589 | + globals: PyDictRef, |
| 590 | + #[pyarg(any, optional)] |
| 591 | + name: OptionalArg<PyStrRef>, |
| 592 | + #[pyarg(any, optional)] |
| 593 | + defaults: OptionalArg<PyTupleRef>, |
| 594 | + #[pyarg(any, optional)] |
| 595 | + closure: OptionalArg<PyTupleRef>, |
| 596 | + #[pyarg(any, optional)] |
| 597 | + kwdefaults: OptionalArg<PyDictRef>, |
| 598 | +} |
| 599 | + |
| 600 | +impl Constructor for PyFunction { |
| 601 | + type Args = PyFunctionNewArgs; |
| 602 | + |
| 603 | + fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult { |
| 604 | + // Handle closure - must be a tuple of cells |
| 605 | + let closure = if let Some(closure_tuple) = args.closure.into_option() { |
| 606 | + // Check that closure length matches code's free variables |
| 607 | + if closure_tuple.len() != args.code.freevars.len() { |
| 608 | + return Err(vm.new_value_error(format!( |
| 609 | + "{} requires closure of length {}, not {}", |
| 610 | + args.code.obj_name, |
| 611 | + args.code.freevars.len(), |
| 612 | + closure_tuple.len() |
| 613 | + ))); |
| 614 | + } |
| 615 | + |
| 616 | + // Validate that all items are cells and create typed tuple |
| 617 | + let typed_closure = |
| 618 | + PyTupleTyped::<PyCellRef>::try_from_object(vm, closure_tuple.into())?; |
| 619 | + Some(typed_closure) |
| 620 | + } else if !args.code.freevars.is_empty() { |
| 621 | + return Err(vm.new_type_error("arg 5 (closure) must be tuple".to_owned())); |
| 622 | + } else { |
| 623 | + None |
| 624 | + }; |
| 625 | + |
| 626 | + // Get function name - use provided name or default to code object name |
| 627 | + let name = args |
| 628 | + .name |
| 629 | + .into_option() |
| 630 | + .unwrap_or_else(|| PyStr::from(args.code.obj_name.as_str()).into_ref(&vm.ctx)); |
| 631 | + |
| 632 | + // Get qualname - for now just use the name |
| 633 | + let qualname = name.clone(); |
| 634 | + |
| 635 | + // Create empty type_params and annotations |
| 636 | + let type_params = vm.ctx.new_tuple(vec![]); |
| 637 | + let annotations = vm.ctx.new_dict(); |
| 638 | + |
| 639 | + // Get doc from code object - for now just use None |
| 640 | + let doc = vm.ctx.none(); |
| 641 | + |
| 642 | + let func = PyFunction::new( |
| 643 | + args.code, |
| 644 | + args.globals, |
| 645 | + closure, |
| 646 | + args.defaults.into_option(), |
| 647 | + args.kwdefaults.into_option(), |
| 648 | + qualname, |
| 649 | + type_params, |
| 650 | + annotations, |
| 651 | + doc, |
| 652 | + vm, |
| 653 | + )?; |
| 654 | + |
| 655 | + func.into_ref_with_type(vm, cls).map(Into::into) |
| 656 | + } |
| 657 | +} |
| 658 | + |
558 | 659 | #[pyclass(module = false, name = "method", traverse)] |
559 | 660 | #[derive(Debug)] |
560 | 661 | pub struct PyBoundMethod { |
|