Skip to content

Commit

Permalink
Auto-patch sub-classes with optimized seq/map getsets at runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
GrieferAtWork committed Dec 8, 2024
1 parent d61997c commit 99e0367
Show file tree
Hide file tree
Showing 4 changed files with 521 additions and 93 deletions.
81 changes: 60 additions & 21 deletions include/deemon/mro.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,32 +211,36 @@ INTDEF NONNULL((1)) void DCALL Dee_membercache_fini(struct Dee_membercache *__re

/* Try to insert a new caching point into the given Dee_membercache `self'.
* @param: self: The cache to insert into.
* @param: decl: The type providing the declaration. */
INTDEF NONNULL((1, 2, 4)) bool DCALL Dee_membercache_addmethod(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method);
INTDEF NONNULL((1, 2, 4)) bool DCALL Dee_membercache_addgetset(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset);
INTDEF NONNULL((1, 2, 4)) bool DCALL Dee_membercache_addmember(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member);
INTDEF NONNULL((1, 2, 4)) bool DCALL Dee_membercache_addattrib(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute *attrib);
INTDEF NONNULL((1, 2, 4)) bool DCALL Dee_membercache_addinstancemethod(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method);
INTDEF NONNULL((1, 2, 4)) bool DCALL Dee_membercache_addinstancegetset(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset);
INTDEF NONNULL((1, 2, 4)) bool DCALL Dee_membercache_addinstancemember(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member);
INTDEF NONNULL((1, 2, 4)) bool DCALL Dee_membercache_addinstanceattrib(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute *attrib);
* @param: decl: The type providing the declaration.
* @return: 2: Slot is being initialized by a different thread (not added)
* @return: 1: Slot already exists in cache (not added)
* @return: 0: Success
* @return: -1: OOM (NO error was thrown) */
INTDEF NONNULL((1, 2, 4)) int DCALL Dee_membercache_addmethod(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method);
INTDEF NONNULL((1, 2, 4)) int DCALL Dee_membercache_addgetset(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset);
INTDEF NONNULL((1, 2, 4)) int DCALL Dee_membercache_addmember(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member);
INTDEF NONNULL((1, 2, 4)) int DCALL Dee_membercache_addattrib(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute *attrib);
INTDEF NONNULL((1, 2, 4)) int DCALL Dee_membercache_addinstancemethod(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method);
INTDEF NONNULL((1, 2, 4)) int DCALL Dee_membercache_addinstancegetset(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset);
INTDEF NONNULL((1, 2, 4)) int DCALL Dee_membercache_addinstancemember(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member);
INTDEF NONNULL((1, 2, 4)) int DCALL Dee_membercache_addinstanceattrib(struct Dee_membercache *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute *attrib);

#ifdef __INTELLISENSE__
/* Cache an instance member (e.g. `tp_methods') in `tp_cache'. */
INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheMethod(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method);
INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheGetSet(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset);
INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheMember(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member);
INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheAttrib(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute const *__restrict attrib);
INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheMethod(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method);
INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheGetSet(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset);
INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheMember(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member);
INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheAttrib(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute const *__restrict attrib);
/* Cache a class member (e.g. `tp_class_methods') in `tp_class_cache'. */
INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheClassMethod(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method);
INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheClassGetSet(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset);
INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheClassMember(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member);
INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheClassAttrib(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute const *__restrict attrib);
INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheClassMethod(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method);
INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheClassGetSet(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset);
INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheClassMember(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member);
INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheClassAttrib(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute const *__restrict attrib);
/* Cache an instance member (e.g. `tp_methods') in `tp_class_cache'. */
INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheInstanceMethod(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method);
INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheInstanceGetSet(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset);
INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheInstanceMember(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member);
INTDEF NONNULL((1, 2, 4)) bool DCALL DeeType_CacheInstanceAttrib(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute const *__restrict attrib);
INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheInstanceMethod(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *method);
INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheInstanceGetSet(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *getset);
INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheInstanceMember(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_member const *member);
INTDEF NONNULL((1, 2, 4)) int DCALL DeeType_CacheInstanceAttrib(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct Dee_class_attribute const *__restrict attrib);
#else /* __INTELLISENSE__ */
#define DeeType_CacheMethod(self, decl, hash, method) Dee_membercache_addmethod(&(self)->tp_cache, decl, hash, method)
#define DeeType_CacheGetSet(self, decl, hash, getset) Dee_membercache_addgetset(&(self)->tp_cache, decl, hash, getset)
Expand Down Expand Up @@ -1732,6 +1736,41 @@ DFUNDEF WUNUSED NONNULL((1, 2)) bool DCALL Dee_type_member_bound(struct Dee_type
DFUNDEF WUNUSED NONNULL((1, 2, 3)) int DCALL Dee_type_member_set(struct Dee_type_member const *desc, DeeObject *self, DeeObject *value);
#define Dee_type_member_del(desc, self) Dee_type_member_set(desc, self, Dee_None)


/* Try to add the specified attribute to the cache of "self".
* - If this fails due to OOM, return `-1', but DON'T throw an exception
* If the MRO cache already contains an entry for the named attribute:
* - Verify that the existing entry is for the same type of attribute (`MEMBERCACHE_*'),
* such that it can be patched without having to alter `mcs_type' (since having to do
* so would result in a non-resolvable race condition where another thread is currently
* dereferencing the function pointers from the existing entry).
* If this verification fails, return `1'.
* - For `DeeTypeMRO_Patch*Method', it is also verified that both the
* old and new function pointers share a common `TYPE_METHOD_FKWDS'.
* - If the type matches, the pre-existing cache entries pointers are patched such that
* they will now reference those from the given parameters.
* Note that for this purpose, this exchange is atomic for each individual function
* pointer (but not all pointers as a whole) -- in the case of `DeeTypeMRO_Patch*GetSet',
* another thread may invoke (e.g.) an out-dated `gs_del' after `gs_get' was already
* patched.
*
* NOTE: Generally, only use these functions for self-optimizing methods in base-classes
* that wish to skip certain type-dependent verification steps during future calls.
* (e.g. `Sequence.first', `Mapping.keys')
*
* @param: old_*: [0..1] When non-NULL, use these values for compare-exchange operations.
* But also note that when there are many function pointers, some may
* be set, while others cannot be -- here, an attempt to exchange
* pointers is made for *all* pointers, and success is indicated if
* at least 1 pointer could be exchanged.
* @return: 1: Failure (cache entry cannot be patched like this)
* @return: 0: Success
* @return: -1: Patching failed due to OOM (but no error was thrown!) */
DFUNDEF NONNULL((1, 2, 4)) int DCALL DeeTypeMRO_PatchMethod(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *new_method, /*0..1*/ struct type_method const *old_method);
DFUNDEF NONNULL((1, 2, 4)) int DCALL DeeTypeMRO_PatchGetSet(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *new_getset, /*0..1*/ struct type_getset const *old_getset);
DFUNDEF NONNULL((1, 2, 4)) int DCALL DeeTypeMRO_PatchClassMethod(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_method const *new_method, /*0..1*/ struct type_method const *old_method);
DFUNDEF NONNULL((1, 2, 4)) int DCALL DeeTypeMRO_PatchClassGetSet(DeeTypeObject *self, DeeTypeObject *decl, Dee_hash_t hash, struct type_getset const *new_getset, /*0..1*/ struct type_getset const *old_getset);

DECL_END

#endif /* !GUARD_DEEMON_MRO_H */
152 changes: 141 additions & 11 deletions src/deemon/objects/seq/default-api-methods.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <deemon/format.h>
#include <deemon/int.h>
#include <deemon/kwds.h>
#include <deemon/map.h>
#include <deemon/mro.h>
#include <deemon/none.h>
#include <deemon/object.h>
Expand Down Expand Up @@ -6213,57 +6214,182 @@ DeeMap_DefaultIterValuesWithError(DeeObject *self) {
/* Deemon user-code wrappers */
/************************************************************************/

/*[[[deemon
import define_Dee_HashStr from rt.gen.hash;
print define_Dee_HashStr("first");
print define_Dee_HashStr("last");
print define_Dee_HashStr("keys");
print define_Dee_HashStr("values");
print define_Dee_HashStr("iterkeys");
print define_Dee_HashStr("itervalues");
print define_Dee_HashStr("cb");
print define_Dee_HashStr("start");
print define_Dee_HashStr("end");
]]]*/
#define Dee_HashStr__first _Dee_HashSelectC(0xa9f0e818, 0x9d12a485470a29a7)
#define Dee_HashStr__last _Dee_HashSelectC(0x185a4f9a, 0x760894ca6d41e4dc)
#define Dee_HashStr__keys _Dee_HashSelectC(0x97e36be1, 0x654d31bc4825131c)
#define Dee_HashStr__values _Dee_HashSelectC(0x33b551c8, 0xf6e3e991b86d1574)
#define Dee_HashStr__iterkeys _Dee_HashSelectC(0x62bd6adc, 0x535ac8ab28094ab3)
#define Dee_HashStr__itervalues _Dee_HashSelectC(0xcb00bab3, 0xe9a89082a994930a)
#define Dee_HashStr__cb _Dee_HashSelectC(0x75ffadba, 0x2501dbb50208b92e)
#define Dee_HashStr__start _Dee_HashSelectC(0xa2ed6890, 0x80b621ce3c3982d5)
#define Dee_HashStr__end _Dee_HashSelectC(0x37fb4a05, 0x6de935c204dc3d01)
/*[[[end]]]*/


/* Helper functions that (ab-)use the attribute cache system of types
* to inject the most optimized version of getsets into top-level objects:
* - Sequence.first
* - Sequence.last
* - Mapping.keys
* - Mapping.values
* - Mapping.iterkeys
* - Mapping.itervalues
*/
#ifdef __OPTIMIZE_SIZE__
#define maybe_cache_optimized_seq_first_in_membercache(self) (void)0
#define maybe_cache_optimized_seq_last_in_membercache(self) (void)0
#define maybe_cache_optimized_map_keys_in_membercache(self) (void)0
#define maybe_cache_optimized_map_values_in_membercache(self) (void)0
#define maybe_cache_optimized_map_iterkeys_in_membercache(self) (void)0
#define maybe_cache_optimized_map_itervalues_in_membercache(self) (void)0
#else /* __OPTIMIZE_SIZE__ */
PRIVATE struct type_getset tpconst gs_default_seq_first =
TYPE_GETSET_BOUND_NODOC(NULL, &default_seq_getfirst, &default_seq_delfirst,
&default_seq_setfirst, &default_seq_boundfirst);
PRIVATE NONNULL((1)) void DCALL
maybe_cache_optimized_seq_first_in_membercache(DeeTypeObject *__restrict self) {
struct type_getset new_getset;
new_getset.gs_name = STR_first;
new_getset.gs_get = DeeType_RequireSeqGetFirst(self);
new_getset.gs_del = DeeType_RequireSeqDelFirst(self);
new_getset.gs_set = DeeType_RequireSeqSetFirst(self);
new_getset.gs_bound = DeeType_RequireSeqBoundFirst(self);
DeeTypeMRO_PatchGetSet(self, &DeeSeq_Type, Dee_HashStr__first,
&new_getset, &gs_default_seq_first);
}

PRIVATE struct type_getset tpconst gs_default_seq_last =
TYPE_GETSET_BOUND_NODOC(NULL, &default_seq_getlast, &default_seq_dellast,
&default_seq_setlast, &default_seq_boundlast);
PRIVATE NONNULL((1)) void DCALL
maybe_cache_optimized_seq_last_in_membercache(DeeTypeObject *__restrict self) {
struct type_getset new_getset;
new_getset.gs_name = STR_last;
new_getset.gs_get = DeeType_RequireSeqGetLast(self);
new_getset.gs_del = DeeType_RequireSeqDelLast(self);
new_getset.gs_set = DeeType_RequireSeqSetLast(self);
new_getset.gs_bound = DeeType_RequireSeqBoundLast(self);
DeeTypeMRO_PatchGetSet(self, &DeeSeq_Type, Dee_HashStr__last,
&new_getset, &gs_default_seq_last);
}

PRIVATE struct type_getset tpconst gs_default_map_keys =
TYPE_GETTER_NODOC(NULL, &default_map_keys);
PRIVATE NONNULL((1)) void DCALL
maybe_cache_optimized_map_keys_in_membercache(DeeTypeObject *__restrict self) {
struct type_getset new_getset;
new_getset.gs_name = STR_keys;
new_getset.gs_get = DeeType_RequireMapKeys(self);
new_getset.gs_del = NULL;
new_getset.gs_set = NULL;
new_getset.gs_bound = NULL;
DeeTypeMRO_PatchGetSet(self, &DeeMapping_Type, Dee_HashStr__keys,
&new_getset, &gs_default_map_keys);
}

PRIVATE struct type_getset tpconst gs_default_map_values =
TYPE_GETTER_NODOC(NULL, &default_map_values);
PRIVATE NONNULL((1)) void DCALL
maybe_cache_optimized_map_values_in_membercache(DeeTypeObject *__restrict self) {
struct type_getset new_getset;
new_getset.gs_name = STR_values;
new_getset.gs_get = DeeType_RequireMapValues(self);
new_getset.gs_del = NULL;
new_getset.gs_set = NULL;
new_getset.gs_bound = NULL;
DeeTypeMRO_PatchGetSet(self, &DeeMapping_Type, Dee_HashStr__values,
&new_getset, &gs_default_map_values);
}

PRIVATE struct type_getset tpconst gs_default_map_iterkeys =
TYPE_GETTER_NODOC(NULL, &default_map_iterkeys);
PRIVATE NONNULL((1)) void DCALL
maybe_cache_optimized_map_iterkeys_in_membercache(DeeTypeObject *__restrict self) {
struct type_getset new_getset;
new_getset.gs_name = STR_iterkeys;
new_getset.gs_get = DeeType_RequireMapIterKeys(self);
new_getset.gs_del = NULL;
new_getset.gs_set = NULL;
new_getset.gs_bound = NULL;
DeeTypeMRO_PatchGetSet(self, &DeeMapping_Type, Dee_HashStr__iterkeys,
&new_getset, &gs_default_map_iterkeys);
}

PRIVATE struct type_getset tpconst gs_default_map_itervalues =
TYPE_GETTER_NODOC(NULL, &default_map_itervalues);
PRIVATE NONNULL((1)) void DCALL
maybe_cache_optimized_map_itervalues_in_membercache(DeeTypeObject *__restrict self) {
struct type_getset new_getset;
new_getset.gs_name = STR_itervalues;
new_getset.gs_get = DeeType_RequireMapIterValues(self);
new_getset.gs_del = NULL;
new_getset.gs_set = NULL;
new_getset.gs_bound = NULL;
DeeTypeMRO_PatchGetSet(self, &DeeMapping_Type, Dee_HashStr__itervalues,
&new_getset, &gs_default_map_itervalues);
}
#endif /* !__OPTIMIZE_SIZE__ */

INTERN WUNUSED NONNULL((1)) DREF DeeObject *DCALL
default_seq_getfirst(DeeObject *__restrict self) {
maybe_cache_optimized_seq_first_in_membercache(Dee_TYPE(self));
return DeeSeq_InvokeGetFirst(self);
}

INTERN WUNUSED NONNULL((1)) int DCALL
default_seq_boundfirst(DeeObject *__restrict self) {
maybe_cache_optimized_seq_first_in_membercache(Dee_TYPE(self));
return DeeSeq_InvokeBoundFirst(self);
}

INTERN WUNUSED NONNULL((1)) int DCALL
default_seq_delfirst(DeeObject *__restrict self) {
maybe_cache_optimized_seq_first_in_membercache(Dee_TYPE(self));
return DeeSeq_InvokeDelFirst(self);
}

INTERN WUNUSED NONNULL((1, 2)) int DCALL
default_seq_setfirst(DeeObject *self, DeeObject *value) {
maybe_cache_optimized_seq_first_in_membercache(Dee_TYPE(self));
return DeeSeq_InvokeSetFirst(self, value);
}

INTERN WUNUSED NONNULL((1)) DREF DeeObject *DCALL
default_seq_getlast(DeeObject *__restrict self) {
maybe_cache_optimized_seq_last_in_membercache(Dee_TYPE(self));
return DeeSeq_InvokeGetLast(self);
}

INTERN WUNUSED NONNULL((1)) int DCALL
default_seq_boundlast(DeeObject *__restrict self) {
maybe_cache_optimized_seq_last_in_membercache(Dee_TYPE(self));
return DeeSeq_InvokeBoundLast(self);
}

INTERN WUNUSED NONNULL((1)) int DCALL
default_seq_dellast(DeeObject *__restrict self) {
maybe_cache_optimized_seq_last_in_membercache(Dee_TYPE(self));
return DeeSeq_InvokeDelLast(self);
}

INTERN WUNUSED NONNULL((1, 2)) int DCALL
default_seq_setlast(DeeObject *self, DeeObject *value) {
maybe_cache_optimized_seq_last_in_membercache(Dee_TYPE(self));
return DeeSeq_InvokeSetLast(self, value);
}

/*[[[deemon
import define_Dee_HashStr from rt.gen.hash;
print define_Dee_HashStr("cb");
print define_Dee_HashStr("start");
print define_Dee_HashStr("end");
]]]*/
#define Dee_HashStr__cb _Dee_HashSelectC(0x75ffadba, 0x2501dbb50208b92e)
#define Dee_HashStr__start _Dee_HashSelectC(0xa2ed6890, 0x80b621ce3c3982d5)
#define Dee_HashStr__end _Dee_HashSelectC(0x37fb4a05, 0x6de935c204dc3d01)
/*[[[end]]]*/

PRIVATE ATTR_NOINLINE WUNUSED NONNULL((1)) DREF DeeObject *DCALL
do_seq_enumerate_with_kw(DeeObject *self, size_t argc,
DeeObject *const *argv, DeeObject *kw) {
Expand Down Expand Up @@ -7366,21 +7492,25 @@ DeeMH_map_popitem(DeeObject *self, size_t argc, DeeObject *const *argv) {

INTERN WUNUSED NONNULL((1)) DREF DeeObject *
(DCALL default_map_keys)(DeeObject *self) {
maybe_cache_optimized_map_keys_in_membercache(Dee_TYPE(self));
return DeeMap_InvokeKeys(self);
}

INTERN WUNUSED NONNULL((1)) DREF DeeObject *
(DCALL default_map_values)(DeeObject *self) {
maybe_cache_optimized_map_values_in_membercache(Dee_TYPE(self));
return DeeMap_InvokeValues(self);
}

INTERN WUNUSED NONNULL((1)) DREF DeeObject *
(DCALL default_map_iterkeys)(DeeObject *self) {
maybe_cache_optimized_map_iterkeys_in_membercache(Dee_TYPE(self));
return DeeMap_InvokeIterKeys(self);
}

INTERN WUNUSED NONNULL((1)) DREF DeeObject *
(DCALL default_map_itervalues)(DeeObject *self) {
maybe_cache_optimized_map_itervalues_in_membercache(Dee_TYPE(self));
return DeeMap_InvokeIterValues(self);
}

Expand Down
Loading

0 comments on commit 99e0367

Please sign in to comment.