py: Combine duplicated code that converts members from a lookup.
Despite initial guess, this code factoring does not hamper performance. In fact it seems to improve speed by a little: running pystone(1.2) on pyboard (which gives a very stable result) this patch takes pystones from 1729.51 up to 1742.16. Also, pystones on x64 increase by around the same proportion (but it's much noisier). Taking a look at the generated machine code, stack usage with this patch is unchanged, and call is tail-optimised with all arguments in registers. Code size decreases by about 50 bytes on Thumb2 archs.
This commit is contained in:
parent
59f6831336
commit
55b74d1ff5
38
py/objtype.c
38
py/objtype.c
|
@ -51,7 +51,6 @@ STATIC mp_obj_t static_class_method_make_new(mp_obj_t self_in, mp_uint_t n_args,
|
|||
#define is_instance_type(type) ((type)->make_new == instance_make_new)
|
||||
#define is_native_type(type) ((type)->make_new != instance_make_new)
|
||||
mp_obj_t instance_make_new(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args);
|
||||
STATIC void instance_convert_return_attr(mp_obj_t self, const mp_obj_type_t *type, mp_obj_t member, mp_obj_t *dest);
|
||||
|
||||
STATIC mp_obj_t mp_obj_new_instance(mp_obj_t class, uint subobjs) {
|
||||
mp_obj_instance_t *o = m_new_obj_var(mp_obj_instance_t, mp_obj_t, subobjs);
|
||||
|
@ -129,19 +128,18 @@ STATIC void mp_obj_class_lookup(struct class_lookup_data *lookup, const mp_obj_
|
|||
mp_map_t *locals_map = mp_obj_dict_get_map(type->locals_dict);
|
||||
mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(lookup->attr), MP_MAP_LOOKUP);
|
||||
if (elem != NULL) {
|
||||
lookup->dest[0] = elem->value;
|
||||
if (lookup->is_type) {
|
||||
// If we look up a class method, we need to return original type for which we
|
||||
// do a lookup, not a (base) type in which we found the class method.
|
||||
const mp_obj_type_t *org_type = (const mp_obj_type_t*)lookup->obj;
|
||||
instance_convert_return_attr(NULL, org_type, elem->value, lookup->dest);
|
||||
mp_convert_member_lookup(NULL, org_type, elem->value, lookup->dest);
|
||||
} else {
|
||||
mp_obj_instance_t *obj = lookup->obj;
|
||||
if (obj != MP_OBJ_NULL && is_native_type(type) && type != &mp_type_object /* object is not a real type */) {
|
||||
// If we're dealing with native base class, then it applies to native sub-object
|
||||
obj = obj->subobj[0];
|
||||
}
|
||||
instance_convert_return_attr(obj, type, elem->value, lookup->dest);
|
||||
mp_convert_member_lookup(obj, type, elem->value, lookup->dest);
|
||||
}
|
||||
#if DEBUG_PRINT
|
||||
printf("mp_obj_class_lookup: Returning: ");
|
||||
|
@ -411,32 +409,6 @@ const qstr mp_binary_op_method_name[] = {
|
|||
[MP_BINARY_OP_EXCEPTION_MATCH] = MP_QSTR_, // not implemented, used to make sure array has full size
|
||||
};
|
||||
|
||||
// Given a member that was extracted from an instance, convert it correctly
|
||||
// and put the result in the dest[] array for a possible method call.
|
||||
// Conversion means dealing with static/class methods, callables, and values.
|
||||
// see http://docs.python.org/3/howto/descriptor.html
|
||||
STATIC void instance_convert_return_attr(mp_obj_t self, const mp_obj_type_t *type, mp_obj_t member, mp_obj_t *dest) {
|
||||
assert(dest[1] == NULL);
|
||||
if (MP_OBJ_IS_TYPE(member, &mp_type_staticmethod)) {
|
||||
// return just the function
|
||||
dest[0] = ((mp_obj_static_class_method_t*)member)->fun;
|
||||
} else if (MP_OBJ_IS_TYPE(member, &mp_type_classmethod)) {
|
||||
// return a bound method, with self being the type of this object
|
||||
dest[0] = ((mp_obj_static_class_method_t*)member)->fun;
|
||||
dest[1] = (mp_obj_t)type;
|
||||
} else if (MP_OBJ_IS_TYPE(member, &mp_type_type)) {
|
||||
// Don't try to bind types (even though they're callable)
|
||||
dest[0] = member;
|
||||
} else if (mp_obj_is_callable(member)) {
|
||||
// return a bound method, with self being this object
|
||||
dest[0] = member;
|
||||
dest[1] = self;
|
||||
} else {
|
||||
// class member is a value, so just return that value
|
||||
dest[0] = member;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t instance_binary_op(mp_uint_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
|
||||
// Note: For ducktyping, CPython does not look in the instance members or use
|
||||
// __getattr__ or __getattribute__. It only looks in the class dictionary.
|
||||
|
@ -493,13 +465,13 @@ void mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
|||
if (MP_OBJ_IS_TYPE(member, &mp_type_property)) {
|
||||
// object member is a property
|
||||
// delegate the store to the property
|
||||
// TODO should this be part of instance_convert_return_attr?
|
||||
// TODO should this be part of mp_convert_member_lookup?
|
||||
const mp_obj_t *proxy = mp_obj_property_get(member);
|
||||
if (proxy[0] == mp_const_none) {
|
||||
// TODO
|
||||
} else {
|
||||
dest[0] = mp_call_function_n_kw(proxy[0], 1, 0, &self_in);
|
||||
// TODO should we convert the returned value using instance_convert_return_attr?
|
||||
// TODO should we convert the returned value using mp_convert_member_lookup?
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -591,7 +563,7 @@ STATIC mp_obj_t instance_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value
|
|||
return mp_obj_subscr(self->subobj[0], index, value);
|
||||
} else if (member[0] != MP_OBJ_NULL) {
|
||||
mp_obj_t args[3] = {self_in, index, value};
|
||||
// TODO probably need to call instance_convert_return_attr, and use mp_call_method_n_kw
|
||||
// TODO probably need to call mp_convert_member_lookup, and use mp_call_method_n_kw
|
||||
mp_obj_t ret = mp_call_function_n_kw(member[0], meth_args, 0, args);
|
||||
if (value == MP_OBJ_SENTINEL) {
|
||||
return ret;
|
||||
|
|
46
py/runtime.c
46
py/runtime.c
|
@ -854,6 +854,31 @@ mp_obj_t mp_load_attr(mp_obj_t base, qstr attr) {
|
|||
}
|
||||
}
|
||||
|
||||
// Given a member that was extracted from an instance, convert it correctly
|
||||
// and put the result in the dest[] array for a possible method call.
|
||||
// Conversion means dealing with static/class methods, callables, and values.
|
||||
// see http://docs.python.org/3/howto/descriptor.html
|
||||
void mp_convert_member_lookup(mp_obj_t self, const mp_obj_type_t *type, mp_obj_t member, mp_obj_t *dest) {
|
||||
if (MP_OBJ_IS_TYPE(member, &mp_type_staticmethod)) {
|
||||
// return just the function
|
||||
dest[0] = ((mp_obj_static_class_method_t*)member)->fun;
|
||||
} else if (MP_OBJ_IS_TYPE(member, &mp_type_classmethod)) {
|
||||
// return a bound method, with self being the type of this object
|
||||
dest[0] = ((mp_obj_static_class_method_t*)member)->fun;
|
||||
dest[1] = (mp_obj_t)type;
|
||||
} else if (MP_OBJ_IS_TYPE(member, &mp_type_type)) {
|
||||
// Don't try to bind types (even though they're callable)
|
||||
dest[0] = member;
|
||||
} else if (mp_obj_is_callable(member)) {
|
||||
// return a bound method, with self being this object
|
||||
dest[0] = member;
|
||||
dest[1] = self;
|
||||
} else {
|
||||
// class member is a value, so just return that value
|
||||
dest[0] = member;
|
||||
}
|
||||
}
|
||||
|
||||
// no attribute found, returns: dest[0] == MP_OBJ_NULL, dest[1] == MP_OBJ_NULL
|
||||
// normal attribute found, returns: dest[0] == <attribute>, dest[1] == MP_OBJ_NULL
|
||||
// method attribute found, returns: dest[0] == <method>, dest[1] == <self>
|
||||
|
@ -888,26 +913,7 @@ void mp_load_method_maybe(mp_obj_t obj, qstr attr, mp_obj_t *dest) {
|
|||
mp_map_t *locals_map = mp_obj_dict_get_map(type->locals_dict);
|
||||
mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP);
|
||||
if (elem != NULL) {
|
||||
// check if the methods are functions, static or class methods
|
||||
// see http://docs.python.org/3/howto/descriptor.html
|
||||
if (MP_OBJ_IS_TYPE(elem->value, &mp_type_staticmethod)) {
|
||||
// return just the function
|
||||
dest[0] = ((mp_obj_static_class_method_t*)elem->value)->fun;
|
||||
} else if (MP_OBJ_IS_TYPE(elem->value, &mp_type_classmethod)) {
|
||||
// return a bound method, with self being the type of this object
|
||||
dest[0] = ((mp_obj_static_class_method_t*)elem->value)->fun;
|
||||
dest[1] = type;
|
||||
} else if (MP_OBJ_IS_TYPE(elem->value, &mp_type_type)) {
|
||||
// Don't try to bind types
|
||||
dest[0] = elem->value;
|
||||
} else if (mp_obj_is_callable(elem->value)) {
|
||||
// return a bound method, with self being this object
|
||||
dest[0] = elem->value;
|
||||
dest[1] = obj;
|
||||
} else {
|
||||
// class member is a value, so just return that value
|
||||
dest[0] = elem->value;
|
||||
}
|
||||
mp_convert_member_lookup(obj, type, elem->value, dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,6 +101,7 @@ void mp_unpack_sequence(mp_obj_t seq, mp_uint_t num, mp_obj_t *items);
|
|||
void mp_unpack_ex(mp_obj_t seq, mp_uint_t num, mp_obj_t *items);
|
||||
mp_obj_t mp_store_map(mp_obj_t map, mp_obj_t key, mp_obj_t value);
|
||||
mp_obj_t mp_load_attr(mp_obj_t base, qstr attr);
|
||||
void mp_convert_member_lookup(mp_obj_t obj, const mp_obj_type_t *type, mp_obj_t member, mp_obj_t *dest);
|
||||
void mp_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest);
|
||||
void mp_load_method_maybe(mp_obj_t base, qstr attr, mp_obj_t *dest);
|
||||
void mp_store_attr(mp_obj_t base, qstr attr, mp_obj_t val);
|
||||
|
|
Loading…
Reference in New Issue