Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
14 changes: 7 additions & 7 deletions pyre/pyre-interpreter/src/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2869,7 +2869,7 @@ fn builtin_print(args: &[PyObjectRef]) -> Result<PyObjectRef, crate::PyError> {
&& unsafe {
let last = *args.last().unwrap();
is_dict(last)
&& pyre_object::w_dict_lookup(last, w_str_new("__pyre_kw__"))
&& pyre_object::w_dict_getitem_str(last, "__pyre_kw__")
.is_some_and(pyre_object::kw_marker::is_kw_marker_sentinel)
};
let (positional, end, sep, file, flush) = if is_kwargs {
Expand All @@ -2888,19 +2888,19 @@ fn builtin_print(args: &[PyObjectRef]) -> Result<PyObjectRef, crate::PyError> {
}
}
}
let end_val = unsafe { pyre_object::w_dict_lookup(kwargs, w_str_new("end")) };
let sep_val = unsafe { pyre_object::w_dict_lookup(kwargs, w_str_new("sep")) };
let end_val = unsafe { pyre_object::w_dict_getitem_str(kwargs, "end") };
let sep_val = unsafe { pyre_object::w_dict_getitem_str(kwargs, "sep") };
// The type check is up front; the str() rendering happens at write
// time so a raising `__str__` leaves the preceding output in place.
let end_obj = print_sep_check(end_val, "end")?;
let sep_obj = print_sep_check(sep_val, "sep")?;
// `file=None` (or absent) uses the native stdout path; any other
// object is written through its `write` / `flush` methods.
let file_obj = match unsafe { pyre_object::w_dict_lookup(kwargs, w_str_new("file")) } {
let file_obj = match unsafe { pyre_object::w_dict_getitem_str(kwargs, "file") } {
Some(f) if !unsafe { pyre_object::is_none(f) } => Some(f),
_ => None,
};
let flush = match unsafe { pyre_object::w_dict_lookup(kwargs, w_str_new("flush")) } {
let flush = match unsafe { pyre_object::w_dict_getitem_str(kwargs, "flush") } {
Some(f) => crate::baseobjspace::is_true(f)?,
None => false,
};
Expand Down Expand Up @@ -3242,7 +3242,7 @@ pub(crate) fn real_kwarg_count(kwargs: Option<PyObjectRef>) -> usize {
/// or the requested key is absent.
pub(crate) fn kwarg_get(kwargs: Option<PyObjectRef>, name: &str) -> Option<PyObjectRef> {
let dict = kwargs?;
unsafe { pyre_object::w_dict_lookup(dict, w_str_new(name)) }
unsafe { pyre_object::w_dict_getitem_str(dict, name) }
}

/// Reject any keyword argument whose name is not in `allowed`. Mirrors
Expand Down Expand Up @@ -3308,7 +3308,7 @@ pub(crate) fn bind_pos_or_kw(
pub(crate) fn has_builtin_kwargs(args: &[PyObjectRef]) -> bool {
matches!(args.last(), Some(&last) if unsafe {
is_dict(last)
&& pyre_object::w_dict_lookup(last, w_str_new("__pyre_kw__"))
&& pyre_object::w_dict_getitem_str(last, "__pyre_kw__")
.is_some_and(pyre_object::kw_marker::is_kw_marker_sentinel)
})
}
Expand Down
9 changes: 4 additions & 5 deletions pyre/pyre-interpreter/src/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2056,7 +2056,7 @@ pub fn call_with_kwargs(
unsafe {
pyre_object::w_dict_store(
kwargs_dict,
pyre_object::w_str_new("__pyre_kw__"),
pyre_object::kw_marker::w_kw_marker_key(),
pyre_object::kw_marker::w_kw_marker_sentinel(),
);
}
Expand Down Expand Up @@ -3247,12 +3247,11 @@ pub(crate) fn real_build_class(args: &[PyObjectRef]) -> Result<PyObjectRef, crat
let last = args[args.len() - 1];
if unsafe { pyre_object::is_dict(last) }
&& unsafe {
pyre_object::w_dict_lookup(last, pyre_object::w_str_new("__pyre_kw__"))
pyre_object::w_dict_getitem_str(last, "__pyre_kw__")
.is_some_and(pyre_object::kw_marker::is_kw_marker_sentinel)
}
{
let w_metaclass =
unsafe { pyre_object::w_dict_lookup(last, pyre_object::w_str_new("metaclass")) };
let w_metaclass = unsafe { pyre_object::w_dict_getitem_str(last, "metaclass") };
// Collect extra kwargs (not metaclass, not __pyre_kw__).
// `w_dict_items` already dispatches `is_module_dict` so a
// class statement with `**module_dict` (rare but valid)
Expand Down Expand Up @@ -4019,7 +4018,7 @@ fn pack_pyre_kwargs(kw_items: &[(PyObjectRef, PyObjectRef)]) -> PyObjectRef {
// overwrite the sentinel detection compares by identity.
pyre_object::w_dict_store(
kw_dict,
pyre_object::w_str_new("__pyre_kw__"),
pyre_object::kw_marker::w_kw_marker_key(),
pyre_object::kw_marker::w_kw_marker_sentinel(),
);
}
Expand Down
18 changes: 6 additions & 12 deletions pyre/pyre-interpreter/src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2063,20 +2063,14 @@ impl NamespaceOpcodeHandler for PyFrame {
/// because the empty dict is the picked builtin.
fn load_global_value(&mut self, name: &str, nameindex: usize) -> Result<Self::Value, PyError> {
// `pyopcode.py:958-960 _load_global_fallback` uses
// `space.finditem_str(self.get_w_globals(), varname)`. Keep the
// borrowed-string strategy fast path for real W_DictObject /
// W_ModuleDictObject layouts, but dispatch a dict subclass through
// the exact mapping object. In pyre a dict subclass is represented
// by an instance plus `__dict_data__`; treating the instance as a
// W_DictObject both loses `__missing__` and reads an invalid layout.
// `space.finditem_str(self.get_w_globals(), varname)`. finditem_str
// takes a borrowed-string fast path for real W_DictObject /
// W_ModuleDictObject layouts and dispatches a dict subclass through
// the general mapping object, so a raising key `__eq__` propagates
// instead of being swallowed as a miss.
let w_globals = self.get_w_globals();
if !w_globals.is_null() {
let value = if unsafe { pyre_object::is_dict(w_globals) } {
unsafe { pyre_object::dictmultiobject::w_dict_getitem_str(w_globals, name) }
} else {
crate::baseobjspace::finditem_str(w_globals, name)?
};
if let Some(value) = value {
if let Some(value) = crate::baseobjspace::finditem_str(w_globals, name)? {
return Ok(value);
}
}
Expand Down
4 changes: 1 addition & 3 deletions pyre/pyre-interpreter/src/module/_pickle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,9 +258,7 @@ pub(crate) fn compat_map(module: &str, name: &str, reverse: bool) -> (String, St
}
}
if let Ok(w_import_map) = crate::baseobjspace::getattr_str(compat, import_map_attr) {
if let Some(v) =
unsafe { pyre_object::w_dict_lookup(w_import_map, pyre_object::w_str_new(module)) }
{
if let Some(v) = unsafe { pyre_object::w_dict_getitem_str(w_import_map, module) } {
return (
unsafe { pyre_object::unicodeobject::w_str_get_value(v) }.to_string(),
name.to_string(),
Expand Down
8 changes: 4 additions & 4 deletions pyre/pyre-interpreter/src/module/math/interp_math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,7 @@ pub fn prod(args: &[PyObjectRef]) -> PyResult {
let is_kwargs = unsafe {
let last = *args.last().unwrap();
pyre_object::is_dict(last)
&& pyre_object::w_dict_lookup(last, pyre_object::w_str_new("__pyre_kw__"))
&& pyre_object::w_dict_getitem_str(last, "__pyre_kw__")
.is_some_and(pyre_object::kw_marker::is_kw_marker_sentinel)
};
let (positional, start) = if is_kwargs {
Expand Down Expand Up @@ -998,7 +998,8 @@ pub fn nextafter(args: &[PyObjectRef]) -> PyResult {
&& unsafe {
let last = *args.last().unwrap();
pyre_object::is_dict(last)
&& pyre_object::w_dict_lookup(last, pyre_object::w_str_new("__pyre_kw__")).is_some()
&& pyre_object::w_dict_getitem_str(last, "__pyre_kw__")
.is_some_and(pyre_object::kw_marker::is_kw_marker_sentinel)
};
let (pos, kwargs) = if is_kwargs {
(&args[..args.len() - 1], Some(*args.last().unwrap()))
Expand All @@ -1010,8 +1011,7 @@ pub fn nextafter(args: &[PyObjectRef]) -> PyResult {
"nextafter() takes exactly 2 positional arguments",
));
}
let steps = match kwargs
.and_then(|kw| unsafe { pyre_object::w_dict_lookup(kw, pyre_object::w_str_new("steps")) })
let steps = match kwargs.and_then(|kw| unsafe { pyre_object::w_dict_getitem_str(kw, "steps") })
{
Some(s) => {
use num_traits::ToPrimitive;
Expand Down
14 changes: 5 additions & 9 deletions pyre/pyre-interpreter/src/runtime_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,12 @@ pub extern "C" fn jit_load_name_from_namespace(
return 0;
};
// `pyopcode.py:959 _load_global`: `space.finditem_str(w_globals,
// varname)`. Real dict layouts retain the borrowed-string strategy
// path; dict subclasses must dispatch through their exact mapping object
// so `__missing__` and observable globals identity survive tracing too.
// varname)`. finditem_str takes the borrowed-string fast path for real
// dict layouts and dispatches a dict subclass through the general mapping
// object, so a raising key `__eq__` propagates through the traced path
// instead of being swallowed as a miss.
if !w_globals.is_null() {
let lookup = if unsafe { pyre_object::is_dict(w_globals) } {
Ok(unsafe { pyre_object::dictmultiobject::w_dict_getitem_str(w_globals, name) })
} else {
crate::baseobjspace::finditem_str(w_globals, name)
};
let value = match lookup {
let value = match crate::baseobjspace::finditem_str(w_globals, name) {
Ok(value) => value,
Err(mut error) => {
jit_publish_exception(error.to_exc_object());
Expand Down
2 changes: 1 addition & 1 deletion pyre/pyre-interpreter/src/type_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1420,7 +1420,7 @@ fn format_render(
return crate::baseobjspace::getitem(m, w_key).map(Some);
}
if let Some(dict) = kwargs_dict {
let v = unsafe { pyre_object::w_dict_lookup(dict, pyre_object::w_str_new(name)) };
let v = unsafe { pyre_object::w_dict_getitem_str(dict, name) };
return Ok(v);
}
Ok(None)
Expand Down
12 changes: 12 additions & 0 deletions pyre/pyre-object/src/kw_marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ pub fn w_kw_marker_sentinel() -> PyObjectRef {
&KW_MARKER_SENTINEL as *const KwMarkerSentinel as *mut PyObject
}

/// Cached immortal `"__pyre_kw__"` key `W_str`. Builtin-kwargs packing stores
/// the sentinel under this key on every keyworded call; minting a fresh
/// `w_str_new("__pyre_kw__")` per call allocated one never-freed immortal string
/// per call, so the constant key is allocated once and reused. Immortal by
/// design — it is shared across every (collectable) marker dict, which on
/// collection drops only the borrowed pointer.
pub fn w_kw_marker_key() -> PyObjectRef {
static KW_MARKER_KEY: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
*KW_MARKER_KEY.get_or_init(|| crate::unicodeobject::w_str_new("__pyre_kw__") as usize)
as PyObjectRef
}

/// `true` when `value` is the marker sentinel (pointer identity).
#[inline]
pub fn is_kw_marker_sentinel(value: PyObjectRef) -> bool {
Expand Down
Loading