Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Lib/test/test_xml_etree.py
Original file line number Diff line number Diff line change
Expand Up @@ -2301,7 +2301,6 @@ def test_bug_xmltoolkit62(self):
self.assertEqual(t.find('.//paragraph').text,
'A new cultivar of Begonia plant named \u2018BCT9801BEG\u2019.')

@unittest.expectedFailure # TODO: RUSTPYTHON
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

@unittest.skipIf(sys.gettrace(), "Skips under coverage.")
def test_bug_xmltoolkit63(self):
# Check reference leak.
Expand Down
127 changes: 64 additions & 63 deletions crates/codegen/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,6 @@ enum NameUsage {
Delete,
}

enum CallType {
Positional { nargs: u32 },
Keyword { nargs: u32 },
Ex { has_kwargs: bool },
}

fn is_forbidden_name(name: &str) -> bool {
// See https://docs.python.org/3/library/constants.html#built-in-constants
const BUILTIN_CONSTANTS: &[&str] = &["__debug__"];
Expand Down Expand Up @@ -1037,6 +1031,7 @@ impl Compiler {

// Call __exit__(None, None, None) - compiler_call_exit_with_nones
// Stack: [..., __exit__] or [..., return_value, __exit__]
emit!(self, Instruction::PushNull);
self.emit_load_const(ConstantData::None);
self.emit_load_const(ConstantData::None);
self.emit_load_const(ConstantData::None);
Expand Down Expand Up @@ -1875,6 +1870,7 @@ impl Compiler {

let assertion_error = self.name("AssertionError");
emit!(self, Instruction::LoadGlobal(assertion_error));
emit!(self, Instruction::PushNull);
match msg {
Some(e) => {
self.compile_expression(e)?;
Expand Down Expand Up @@ -2095,12 +2091,13 @@ impl Compiler {
fn prepare_decorators(&mut self, decorator_list: &[Decorator]) -> CompileResult<()> {
for decorator in decorator_list {
self.compile_expression(&decorator.expression)?;
emit!(self, Instruction::PushNull);
}
Ok(())
}

fn apply_decorators(&mut self, decorator_list: &[Decorator]) {
// Apply decorators:
// Apply decorators - each pops [decorator, NULL, arg] and pushes result
for _ in decorator_list {
emit!(self, Instruction::Call { nargs: 1 });
}
Expand Down Expand Up @@ -2142,6 +2139,7 @@ impl Compiler {

// Create type params function with closure
self.make_closure(code, bytecode::MakeFunctionFlags::empty())?;
emit!(self, Instruction::PushNull);

// Call the function immediately
emit!(self, Instruction::Call { nargs: 0 });
Expand Down Expand Up @@ -3224,11 +3222,6 @@ impl Compiler {
num_typeparam_args += 1;
}

// SWAP if we have both
if num_typeparam_args == 2 {
emit!(self, Instruction::Swap { index: 2 });
}

// Enter type params scope
let type_params_name = format!("<generic parameters of {name}>");
self.push_output(
Expand Down Expand Up @@ -3308,21 +3301,31 @@ impl Compiler {
self.make_closure(type_params_code, bytecode::MakeFunctionFlags::empty())?;

// Call the closure
// Call expects stack: [callable, self_or_null, arg1, ..., argN]
if num_typeparam_args > 0 {
// Stack: [arg1, ..., argN, closure]
// Need: [closure, NULL, arg1, ..., argN]
let reverse_amount = (num_typeparam_args + 1) as u32;
emit!(
self,
Instruction::Swap {
index: (num_typeparam_args + 1) as u32
Instruction::Reverse {
amount: reverse_amount
}
);
// Stack: [closure, argN, ..., arg1]
emit!(self, Instruction::PushNull);
// Stack: [closure, argN, ..., arg1, NULL]
emit!(
self,
Instruction::Call {
nargs: num_typeparam_args as u32
}
);
} else {
// No arguments, just call the closure
// Stack: [closure]
emit!(self, Instruction::PushNull);
// Stack: [closure, NULL]
// Call pops: args (0), then self_or_null (NULL), then callable (closure)
emit!(self, Instruction::Call { nargs: 0 });
}
}
Expand Down Expand Up @@ -3691,6 +3694,7 @@ impl Compiler {

// Generate class creation code
emit!(self, Instruction::LoadBuildClass);
emit!(self, Instruction::PushNull);

// Set up the class function with type params
let mut func_flags = bytecode::MakeFunctionFlags::empty();
Expand Down Expand Up @@ -3720,14 +3724,20 @@ impl Compiler {
if let Some(arguments) = arguments
&& !arguments.keywords.is_empty()
{
let mut kwarg_names = vec![];
for keyword in &arguments.keywords {
if let Some(name) = &keyword.arg {
self.emit_load_const(ConstantData::Str {
value: name.as_str().into(),
});
}
let name = keyword
.arg
.as_ref()
.expect("keyword argument name must be set");
kwarg_names.push(ConstantData::Str {
value: name.as_str().into(),
});
self.compile_expression(&keyword.value)?;
}
self.emit_load_const(ConstantData::Tuple {
elements: kwarg_names,
});
emit!(
self,
Instruction::CallKw {
Expand All @@ -3748,21 +3758,22 @@ impl Compiler {

// Execute the type params function
self.make_closure(type_params_code, bytecode::MakeFunctionFlags::empty())?;
emit!(self, Instruction::PushNull);
emit!(self, Instruction::Call { nargs: 0 });
} else {
// Non-generic class: standard path
emit!(self, Instruction::LoadBuildClass);
emit!(self, Instruction::PushNull);

// Create class function with closure
self.make_closure(class_code, bytecode::MakeFunctionFlags::empty())?;
self.emit_load_const(ConstantData::Str { value: name.into() });

let call = if let Some(arguments) = arguments {
self.compile_call_inner(2, arguments)?
if let Some(arguments) = arguments {
self.compile_call_helper(2, arguments)?;
} else {
CallType::Positional { nargs: 2 }
};
self.compile_normal_call(call);
emit!(self, Instruction::Call { nargs: 2 });
}
}

// Step 4: Apply decorators and store (common to both paths)
Expand Down Expand Up @@ -3912,6 +3923,7 @@ impl Compiler {
// Stack: [..., __exit__]
// Call __exit__(None, None, None)
self.set_source_range(with_range);
emit!(self, Instruction::PushNull);
self.emit_load_const(ConstantData::None);
self.emit_load_const(ConstantData::None);
self.emit_load_const(ConstantData::None);
Expand Down Expand Up @@ -6087,49 +6099,36 @@ impl Compiler {
}

fn compile_call(&mut self, func: &Expr, args: &Arguments) -> CompileResult<()> {
let method = if let Expr::Attribute(ExprAttribute { value, attr, .. }) = &func {
// Method call: obj → LOAD_ATTR_METHOD → [method, self_or_null] → args → CALL
// Regular call: func → PUSH_NULL → args → CALL
if let Expr::Attribute(ExprAttribute { value, attr, .. }) = &func {
// Method call: compile object, then LOAD_ATTR_METHOD
// LOAD_ATTR_METHOD pushes [method, self_or_null] on stack
self.compile_expression(value)?;
let idx = self.name(attr.as_str());
emit!(self, Instruction::LoadMethod { idx });
true
emit!(self, Instruction::LoadAttrMethod { idx });
self.compile_call_helper(0, args)?;
} else {
// Regular call: push func, then NULL for self_or_null slot
// Stack layout: [func, NULL, args...] - same as method call [func, self, args...]
self.compile_expression(func)?;
false
};
let call = self.compile_call_inner(0, args)?;
if method {
self.compile_method_call(call)
} else {
self.compile_normal_call(call)
emit!(self, Instruction::PushNull);
self.compile_call_helper(0, args)?;
}
Ok(())
}

fn compile_normal_call(&mut self, ty: CallType) {
match ty {
CallType::Positional { nargs } => {
emit!(self, Instruction::Call { nargs })
}
CallType::Keyword { nargs } => emit!(self, Instruction::CallKw { nargs }),
CallType::Ex { has_kwargs } => emit!(self, Instruction::CallFunctionEx { has_kwargs }),
}
}
fn compile_method_call(&mut self, ty: CallType) {
match ty {
CallType::Positional { nargs } => {
emit!(self, Instruction::CallMethodPositional { nargs })
}
CallType::Keyword { nargs } => emit!(self, Instruction::CallMethodKeyword { nargs }),
CallType::Ex { has_kwargs } => emit!(self, Instruction::CallMethodEx { has_kwargs }),
}
}

fn compile_call_inner(
/// Compile call arguments and emit the appropriate CALL instruction.
/// This is shared between compiler_call and compiler_class.
fn compile_call_helper(
&mut self,
additional_positional: u32,
arguments: &Arguments,
) -> CompileResult<CallType> {
let count = u32::try_from(arguments.len()).unwrap() + additional_positional;
) -> CompileResult<()> {
let args_count = u32::try_from(arguments.len()).expect("too many arguments");
let count = args_count
.checked_add(additional_positional)
.expect("too many arguments");

// Normal arguments:
let (size, unpack) = self.gather_elements(additional_positional, &arguments.args)?;
Expand All @@ -6141,7 +6140,7 @@ impl Compiler {
}
}

let call = if unpack || has_double_star {
if unpack || has_double_star {
// Create a tuple with positional args:
if unpack {
emit!(self, Instruction::BuildTupleFromTuples { size });
Expand All @@ -6154,7 +6153,7 @@ impl Compiler {
if has_kwargs {
self.compile_keywords(&arguments.keywords)?;
}
CallType::Ex { has_kwargs }
emit!(self, Instruction::CallFunctionEx { has_kwargs });
} else if !arguments.keywords.is_empty() {
let mut kwarg_names = vec![];
for keyword in &arguments.keywords {
Expand All @@ -6172,12 +6171,12 @@ impl Compiler {
self.emit_load_const(ConstantData::Tuple {
elements: kwarg_names,
});
CallType::Keyword { nargs: count }
emit!(self, Instruction::CallKw { nargs: count });
} else {
CallType::Positional { nargs: count }
};
emit!(self, Instruction::Call { nargs: count });
}

Ok(call)
Ok(())
}

// Given a vector of expr / star expr generate code which gives either
Expand Down Expand Up @@ -6409,6 +6408,7 @@ impl Compiler {

// Create comprehension function with closure
self.make_closure(code, bytecode::MakeFunctionFlags::empty())?;
emit!(self, Instruction::PushNull);

// Evaluate iterated item:
self.compile_expression(&generators[0].iter)?;
Expand Down Expand Up @@ -6838,6 +6838,7 @@ impl Compiler {
match action {
UnwindAction::With { is_async } => {
// compiler_call_exit_with_nones
emit!(self, Instruction::PushNull);
self.emit_load_const(ConstantData::None);
self.emit_load_const(ConstantData::None);
self.emit_load_const(ConstantData::None);
Expand Down
Loading
Loading