Skip to content

Commit e913b44

Browse files
committed
py/objtype: Simplify metaclass implementation closer to dpgeorge's original.
Refactor to preserve dpgeorge's original design pattern: - Move type___new__ and type___init__ declarations to file header - Simplify type_make_new to just dispatch to type___new__ - Simplify type_call to use mp_load_method_maybe instead of mp_obj_class_lookup - Reduce code complexity while maintaining all functionality Binary size reduced by 128 bytes. All tests pass. Signed-off-by: Andrew Leech <[email protected]>
1 parent 88016b1 commit e913b44

File tree

1 file changed

+23
-45
lines changed

1 file changed

+23
-45
lines changed

py/objtype.c

Lines changed: 23 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
static mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict, const mp_obj_type_t *metaclass);
4848
static mp_obj_t mp_obj_is_subclass(mp_obj_t object, mp_obj_t classinfo);
4949
static mp_obj_t static_class_method_make_new(const mp_obj_type_t *self_in, size_t n_args, size_t n_kw, const mp_obj_t *args);
50+
static mp_obj_t type___new__(size_t n_args, const mp_obj_t *args);
51+
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(type___new___obj, 1, 4, type___new__);
5052

5153
/******************************************************************************/
5254
// instance object
@@ -1032,36 +1034,27 @@ static mp_obj_t type_make_new(const mp_obj_type_t *type_in, size_t n_args, size_
10321034
case 1:
10331035
return MP_OBJ_FROM_PTR(mp_obj_get_type(args[0]));
10341036

1035-
case 3: {
1037+
case 3:
10361038
// args[0] = name
10371039
// args[1] = bases tuple
10381040
// args[2] = locals dict
10391041

1040-
// Check if the metaclass has a custom __new__ method
1041-
// Use proper method lookup to check inheritance chain
1042-
mp_obj_t dest[2] = {MP_OBJ_NULL, MP_OBJ_NULL};
1043-
struct class_lookup_data lookup = {
1044-
.obj = NULL,
1045-
.attr = MP_QSTR___new__,
1046-
.slot_offset = 0,
1047-
.dest = dest,
1048-
.is_type = true,
1049-
};
1050-
mp_obj_class_lookup(&lookup, type_in);
1051-
if (dest[0] != MP_OBJ_NULL) {
1052-
// Found custom __new__, unwrap if it's a staticmethod
1053-
mp_obj_t new_fn = dest[0];
1054-
if (mp_obj_is_type(new_fn, &mp_type_staticmethod)) {
1055-
new_fn = ((mp_obj_static_class_method_t *)MP_OBJ_TO_PTR(new_fn))->fun;
1042+
// Check if metaclass has custom __new__ (not inherited from type)
1043+
if (type_in != &mp_type_type) {
1044+
mp_obj_t dest[2] = {MP_OBJ_NULL, MP_OBJ_NULL};
1045+
mp_load_method_maybe(MP_OBJ_FROM_PTR(type_in), MP_QSTR___new__, dest);
1046+
if (dest[0] != MP_OBJ_NULL && dest[0] != MP_OBJ_FROM_PTR(&type___new___obj)) {
1047+
// Found custom __new__, call it with (metaclass, name, bases, dict)
1048+
mp_obj_t new_args[4] = {MP_OBJ_FROM_PTR(type_in), args[0], args[1], args[2]};
1049+
return mp_call_function_n_kw(dest[0], 4, 0, new_args);
10561050
}
1057-
// Call it with (metaclass, name, bases, dict)
1058-
mp_obj_t new_args[4] = {MP_OBJ_FROM_PTR(type_in), args[0], args[1], args[2]};
1059-
return mp_call_function_n_kw(new_fn, 4, 0, new_args);
10601051
}
10611052

1062-
// No custom __new__, use default behavior
1063-
return mp_obj_new_type(mp_obj_str_get_qstr(args[0]), args[1], args[2], type_in);
1064-
}
1053+
// No custom __new__, use default type.__new__
1054+
{
1055+
mp_obj_t new_args[4] = {MP_OBJ_FROM_PTR(type_in), args[0], args[1], args[2]};
1056+
return type___new__(4, new_args);
1057+
}
10651058

10661059
default:
10671060
mp_raise_TypeError(MP_ERROR_TEXT("type takes 1 or 3 arguments"));
@@ -1074,34 +1067,23 @@ static mp_obj_t type_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp
10741067
mp_obj_type_t *self = MP_OBJ_TO_PTR(self_in);
10751068

10761069
// Check if the metaclass has a custom __call__ method
1077-
// If it does, use that instead of the default make_new behavior
10781070
const mp_obj_type_t *metaclass = self->base.type;
10791071
if (metaclass != &mp_type_type) {
1080-
// Custom metaclass, check for __call__ method
10811072
mp_obj_t dest[2] = {MP_OBJ_NULL, MP_OBJ_NULL};
1082-
struct class_lookup_data lookup = {
1083-
.obj = NULL,
1084-
.attr = MP_QSTR___call__,
1085-
.slot_offset = 0,
1086-
.dest = dest,
1087-
.is_type = true,
1088-
};
1089-
mp_obj_class_lookup(&lookup, metaclass);
1073+
mp_load_method_maybe(MP_OBJ_FROM_PTR(metaclass), MP_QSTR___call__, dest);
10901074
if (dest[0] != MP_OBJ_NULL) {
1091-
// Found __call__ on metaclass, use it
1092-
// dest[0] contains the function, call it with cls as first argument
1075+
// Found custom __call__, call it with (cls, *args, **kwargs)
10931076
mp_obj_t *new_args;
1094-
mp_obj_t stack_args[8]; // Stack allocation for small arg counts
1077+
mp_obj_t stack_args[8];
10951078

1096-
if (n_args < 8) { // Use all 8 slots: up to 7 args + cls = 8 total
1079+
if (n_args < 8) {
10971080
new_args = stack_args;
10981081
} else {
10991082
new_args = m_new(mp_obj_t, n_args + 1);
11001083
}
11011084

1102-
new_args[0] = self_in; // cls argument
1085+
new_args[0] = self_in;
11031086
memcpy(new_args + 1, args, sizeof(mp_obj_t) * n_args);
1104-
// Call the function directly (not as a method) to avoid recursion
11051087
mp_obj_t result = mp_call_function_n_kw(dest[0], n_args + 1, n_kw, new_args);
11061088

11071089
if (n_args >= 8) {
@@ -1112,6 +1094,7 @@ static mp_obj_t type_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp
11121094
}
11131095
}
11141096

1097+
// No custom __call__, use default make_new behavior
11151098
if (!MP_OBJ_TYPE_HAS_SLOT(self, make_new)) {
11161099
#if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE
11171100
mp_raise_TypeError(MP_ERROR_TEXT("can't create instance"));
@@ -1120,11 +1103,7 @@ static mp_obj_t type_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp
11201103
#endif
11211104
}
11221105

1123-
// make new instance
1124-
mp_obj_t o = MP_OBJ_TYPE_GET_SLOT(self, make_new)(self, n_args, n_kw, args);
1125-
1126-
// return new instance
1127-
return o;
1106+
return MP_OBJ_TYPE_GET_SLOT(self, make_new)(self, n_args, n_kw, args);
11281107
}
11291108

11301109
// Minimal type.__new__ for metaclass support
@@ -1150,7 +1129,6 @@ static mp_obj_t type___new__(size_t n_args, const mp_obj_t *args) {
11501129
MP_ERROR_TEXT("type.__new__() takes 1, 2 or 4 arguments (%d given)"), n_args);
11511130
}
11521131
}
1153-
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(type___new___obj, 1, 4, type___new__);
11541132

11551133
// type.__init__(cls, name, bases, dict) - does nothing, exists for metaclass super() calls
11561134
static mp_obj_t type___init__(size_t n_args, const mp_obj_t *args) {

0 commit comments

Comments
 (0)