1+ #include <stdio.h>
2+ #include <stddef.h>
13#include <string.h>
24#include <assert.h>
35
911#include "runtime0.h"
1012#include "runtime.h"
1113
14+ #if 0 // print debugging info
15+ #define DEBUG_PRINT (1)
16+ #define DEBUG_printf DEBUG_printf
17+ #else // don't print debugging info
18+ #define DEBUG_printf (...) (void)0
19+ #endif
20+
1221/******************************************************************************/
1322// class object
1423// creating an instance of a class makes one of these objects
1524
1625typedef struct _mp_obj_class_t {
1726 mp_obj_base_t base ;
1827 mp_map_t members ;
28+ mp_obj_t subobj [];
1929 // TODO maybe cache __getattr__ and __setattr__ for efficient lookup of them
2030} mp_obj_class_t ;
2131
22- STATIC mp_obj_t mp_obj_new_class (mp_obj_t class ) {
23- mp_obj_class_t * o = m_new_obj (mp_obj_class_t );
32+ #define is_native_type (type ) ((type)->make_new != class_make_new)
33+ STATIC mp_obj_t class_make_new (mp_obj_t self_in , uint n_args , uint n_kw , const mp_obj_t * args );
34+
35+ STATIC mp_obj_t mp_obj_new_class (mp_obj_t class , uint subobjs ) {
36+ mp_obj_class_t * o = m_new_obj_var (mp_obj_class_t , mp_obj_t , subobjs );
2437 o -> base .type = class ;
2538 mp_map_init (& o -> members , 0 );
39+ mp_seq_clear (o -> subobj , 0 , subobjs , sizeof (* o -> subobj ));
2640 return o ;
2741}
2842
43+ STATIC int class_count_native_bases (const mp_obj_type_t * type , const mp_obj_type_t * * last_native_base ) {
44+ uint len ;
45+ mp_obj_t * items ;
46+ mp_obj_tuple_get (type -> bases_tuple , & len , & items );
47+
48+ int count = 0 ;
49+ for (uint i = 0 ; i < len ; i ++ ) {
50+ assert (MP_OBJ_IS_TYPE (items [i ], & mp_type_type ));
51+ if (is_native_type ((const mp_obj_type_t * )items [i ])) {
52+ * last_native_base = items [i ];
53+ count ++ ;
54+ } else {
55+ count += class_count_native_bases (items [i ], last_native_base );
56+ }
57+ }
58+
59+ return count ;
60+ }
61+
62+ // TODO
63+ // This implements depth-first left-to-right MRO, which is not compliant with Python3 MRO
64+ // http://python-history.blogspot.com/2010/06/method-resolution-order.html
65+ // https://www.python.org/download/releases/2.3/mro/
66+ //
2967// will return MP_OBJ_NULL if not found
30- STATIC mp_obj_t mp_obj_class_lookup (const mp_obj_type_t * type , qstr attr ) {
68+ // will return MP_OBJ_SENTINEL if special method was found in a native type base
69+ // via slot id (meth_offset). As there can be only one native base, it's known that it
70+ // applies to instance->subobj[0]. In most cases, we also don't need to know which type
71+ // it was - because instance->subobj[0] is of that type. The only exception is when
72+ // object is not yet constructed, then we need to know base native type to construct
73+ // instance->subobj[0]. This case is handled via class_count_native_bases() though.
74+ STATIC mp_obj_t mp_obj_class_lookup (const mp_obj_type_t * type , qstr attr , machine_uint_t meth_offset ) {
3175 for (;;) {
76+ // Optimize special method lookup for native types
77+ // This avoids extra method_name => slot lookup. On the other hand,
78+ // this should not be applied to class types, as will result in extra
79+ // lookup either.
80+ if (meth_offset != 0 && is_native_type (type )) {
81+ if (* (void * * )((char * )type + meth_offset ) != NULL ) {
82+ DEBUG_printf ("mp_obj_class_lookup: matched special meth slot for %s\n" , qstr_str (attr ));
83+ return MP_OBJ_SENTINEL ;
84+ }
85+ }
86+
3287 if (type -> locals_dict != NULL ) {
3388 // search locals_dict (the set of methods/attributes)
3489 assert (MP_OBJ_IS_TYPE (type -> locals_dict , & mp_type_dict )); // Micro Python restriction, for now
@@ -54,7 +109,7 @@ STATIC mp_obj_t mp_obj_class_lookup(const mp_obj_type_t *type, qstr attr) {
54109 }
55110 for (uint i = 0 ; i < len - 1 ; i ++ ) {
56111 assert (MP_OBJ_IS_TYPE (items [i ], & mp_type_type ));
57- mp_obj_t obj = mp_obj_class_lookup ((mp_obj_type_t * )items [i ], attr );
112+ mp_obj_t obj = mp_obj_class_lookup ((mp_obj_type_t * )items [i ], attr , meth_offset );
58113 if (obj != MP_OBJ_NULL ) {
59114 return obj ;
60115 }
@@ -69,10 +124,15 @@ STATIC mp_obj_t mp_obj_class_lookup(const mp_obj_type_t *type, qstr attr) {
69124STATIC void class_print (void (* print )(void * env , const char * fmt , ...), void * env , mp_obj_t self_in , mp_print_kind_t kind ) {
70125 mp_obj_class_t * self = self_in ;
71126 qstr meth = (kind == PRINT_STR ) ? MP_QSTR___str__ : MP_QSTR___repr__ ;
72- mp_obj_t member = mp_obj_class_lookup (self -> base .type , meth );
127+ mp_obj_t member = mp_obj_class_lookup (self -> base .type , meth , offsetof( mp_obj_type_t , print ) );
73128 if (member == MP_OBJ_NULL && kind == PRINT_STR ) {
74129 // If there's no __str__, fall back to __repr__
75- member = mp_obj_class_lookup (self -> base .type , MP_QSTR___repr__ );
130+ member = mp_obj_class_lookup (self -> base .type , MP_QSTR___repr__ , 0 );
131+ }
132+
133+ if (member == MP_OBJ_SENTINEL ) {
134+ mp_obj_print_helper (print , env , self -> subobj [0 ], kind );
135+ return ;
76136 }
77137
78138 if (member != MP_OBJ_NULL ) {
@@ -89,13 +149,25 @@ STATIC mp_obj_t class_make_new(mp_obj_t self_in, uint n_args, uint n_kw, const m
89149 assert (MP_OBJ_IS_TYPE (self_in , & mp_type_type ));
90150 mp_obj_type_t * self = self_in ;
91151
92- mp_obj_t o = mp_obj_new_class (self_in );
152+ const mp_obj_type_t * native_base ;
153+ uint num_native_bases = class_count_native_bases (self , & native_base );
154+ assert (num_native_bases < 2 );
93155
94- // look for __init__ function
95- mp_obj_t init_fn = mp_obj_class_lookup (self , MP_QSTR___init__ );
156+ mp_obj_class_t * o = mp_obj_new_class (self_in , num_native_bases );
96157
97- if (init_fn != MP_OBJ_NULL ) {
98- // call __init__ function
158+ // look for __init__ function
159+ mp_obj_t init_fn = mp_obj_class_lookup (self , MP_QSTR___init__ , offsetof(mp_obj_type_t , make_new ));
160+
161+ if (init_fn == MP_OBJ_SENTINEL ) {
162+ // Native type's constructor is what wins - it gets all our arguments,
163+ // and none Python classes are initialized at all.
164+ o -> subobj [0 ] = native_base -> make_new ((mp_obj_type_t * )native_base , n_args , n_kw , args );
165+ } else if (init_fn != MP_OBJ_NULL ) {
166+ // We need to default-initialize any native subobjs first
167+ if (num_native_bases > 0 ) {
168+ o -> subobj [0 ] = native_base -> make_new ((mp_obj_type_t * )native_base , 0 , 0 , NULL );
169+ }
170+ // now call Python class __init__ function with all args
99171 mp_obj_t init_ret ;
100172 if (n_args == 0 && n_kw == 0 ) {
101173 init_ret = mp_call_function_n_kw (init_fn , 1 , 0 , (mp_obj_t * )& o );
@@ -111,9 +183,8 @@ STATIC mp_obj_t class_make_new(mp_obj_t self_in, uint n_args, uint n_kw, const m
111183 }
112184
113185 } else {
114- // TODO
115186 if (n_args != 0 ) {
116- nlr_raise (mp_obj_new_exception_msg_varg (& mp_type_TypeError , "function takes 0 positional arguments but %d were given" , n_args ));
187+ nlr_raise (mp_obj_new_exception_msg_varg (& mp_type_TypeError , "object() takes no parameters" ));
117188 }
118189 }
119190
@@ -132,11 +203,15 @@ STATIC const qstr unary_op_method_name[] = {
132203STATIC mp_obj_t class_unary_op (int op , mp_obj_t self_in ) {
133204 mp_obj_class_t * self = self_in ;
134205 qstr op_name = unary_op_method_name [op ];
206+ /* Still try to lookup native slot
135207 if (op_name == 0) {
136208 return MP_OBJ_NOT_SUPPORTED;
137209 }
138- mp_obj_t member = mp_obj_class_lookup (self -> base .type , op_name );
139- if (member != MP_OBJ_NULL ) {
210+ */
211+ mp_obj_t member = mp_obj_class_lookup (self -> base .type , op_name , offsetof(mp_obj_type_t , unary_op ));
212+ if (member == MP_OBJ_SENTINEL ) {
213+ return mp_unary_op (op , self -> subobj [0 ]);
214+ } else if (member != MP_OBJ_NULL ) {
140215 return mp_call_function_1 (member , self_in );
141216 } else {
142217 return MP_OBJ_NOT_SUPPORTED ;
@@ -210,11 +285,15 @@ STATIC mp_obj_t class_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
210285 // __getattr__ or __getattribute__. It only looks in the class dictionary.
211286 mp_obj_class_t * lhs = lhs_in ;
212287 qstr op_name = binary_op_method_name [op ];
288+ /* Still try to lookup native slot
213289 if (op_name == 0) {
214290 return MP_OBJ_NOT_SUPPORTED;
215291 }
216- mp_obj_t member = mp_obj_class_lookup (lhs -> base .type , op_name );
217- if (member != MP_OBJ_NULL ) {
292+ */
293+ mp_obj_t member = mp_obj_class_lookup (lhs -> base .type , op_name , offsetof(mp_obj_type_t , binary_op ));
294+ if (member == MP_OBJ_SENTINEL ) {
295+ return mp_binary_op (op , lhs -> subobj [0 ], rhs_in );
296+ } else if (member != MP_OBJ_NULL ) {
218297 mp_obj_t dest [3 ];
219298 dest [1 ] = MP_OBJ_NULL ;
220299 class_convert_return_attr (lhs_in , member , dest );
@@ -237,7 +316,7 @@ STATIC void class_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
237316 return ;
238317 }
239318
240- mp_obj_t member = mp_obj_class_lookup (self -> base .type , attr );
319+ mp_obj_t member = mp_obj_class_lookup (self -> base .type , attr , 0 );
241320 if (member != MP_OBJ_NULL ) {
242321 if (0 ) {
243322#if MICROPY_ENABLE_PROPERTY
@@ -280,7 +359,7 @@ STATIC bool class_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) {
280359#if MICROPY_ENABLE_PROPERTY
281360 // for property, we need to do a lookup first in the class dict
282361 // this makes all stores slow... how to fix?
283- mp_obj_t member = mp_obj_class_lookup (self -> base .type , attr );
362+ mp_obj_t member = mp_obj_class_lookup (self -> base .type , attr , 0 );
284363 if (member != MP_OBJ_NULL && MP_OBJ_IS_TYPE (member , & mp_type_property )) {
285364 // attribute already exists and is a property
286365 // delegate the store to the property
@@ -313,18 +392,20 @@ STATIC mp_obj_t class_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
313392 uint meth_args ;
314393 if (value == MP_OBJ_NULL ) {
315394 // delete item
316- member = mp_obj_class_lookup (self -> base .type , MP_QSTR___delitem__ );
395+ member = mp_obj_class_lookup (self -> base .type , MP_QSTR___delitem__ , offsetof( mp_obj_type_t , subscr ) );
317396 meth_args = 2 ;
318397 } else if (value == MP_OBJ_SENTINEL ) {
319398 // load item
320- member = mp_obj_class_lookup (self -> base .type , MP_QSTR___getitem__ );
399+ member = mp_obj_class_lookup (self -> base .type , MP_QSTR___getitem__ , offsetof( mp_obj_type_t , subscr ) );
321400 meth_args = 2 ;
322401 } else {
323402 // store item
324- member = mp_obj_class_lookup (self -> base .type , MP_QSTR___setitem__ );
403+ member = mp_obj_class_lookup (self -> base .type , MP_QSTR___setitem__ , offsetof( mp_obj_type_t , subscr ) );
325404 meth_args = 3 ;
326405 }
327- if (member != MP_OBJ_NULL ) {
406+ if (member == MP_OBJ_SENTINEL ) {
407+ return mp_obj_subscr (self -> subobj [0 ], index , value );
408+ } else if (member != MP_OBJ_NULL ) {
328409 mp_obj_t args [3 ] = {self_in , index , value };
329410 // TODO probably need to call class_convert_return_attr, and use mp_call_method_n_kw
330411 mp_obj_t ret = mp_call_function_n_kw (member , meth_args , 0 , args );
@@ -340,10 +421,13 @@ STATIC mp_obj_t class_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) {
340421
341422STATIC mp_obj_t class_call (mp_obj_t self_in , uint n_args , uint n_kw , const mp_obj_t * args ) {
342423 mp_obj_class_t * self = self_in ;
343- mp_obj_t member = mp_obj_class_lookup (self -> base .type , MP_QSTR___call__ );
424+ mp_obj_t member = mp_obj_class_lookup (self -> base .type , MP_QSTR___call__ , offsetof( mp_obj_type_t , call ) );
344425 if (member == MP_OBJ_NULL ) {
345426 return member ;
346427 }
428+ if (member == MP_OBJ_SENTINEL ) {
429+ return mp_call_function_n_kw (self -> subobj [0 ], n_args , n_kw , args );
430+ }
347431 mp_obj_t meth = mp_obj_new_bound_meth (member , self );
348432 return mp_call_function_n_kw (meth , n_args , n_kw , args );
349433}
@@ -403,7 +487,7 @@ STATIC void type_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
403487 return ;
404488 }
405489#endif
406- mp_obj_t member = mp_obj_class_lookup (self , attr );
490+ mp_obj_t member = mp_obj_class_lookup (self , attr , 0 );
407491 if (member != MP_OBJ_NULL ) {
408492 // check if the methods are functions, static or class methods
409493 // see http://docs.python.org/3.3/howto/descriptor.html
@@ -474,6 +558,20 @@ const mp_obj_type_t mp_type_type = {
474558mp_obj_t mp_obj_new_type (qstr name , mp_obj_t bases_tuple , mp_obj_t locals_dict ) {
475559 assert (MP_OBJ_IS_TYPE (bases_tuple , & mp_type_tuple )); // Micro Python restriction, for now
476560 assert (MP_OBJ_IS_TYPE (locals_dict , & mp_type_dict )); // Micro Python restriction, for now
561+
562+ // Basic validation of base classes
563+ uint len ;
564+ mp_obj_t * items ;
565+ mp_obj_tuple_get (bases_tuple , & len , & items );
566+ for (uint i = 0 ; i < len ; i ++ ) {
567+ assert (MP_OBJ_IS_TYPE (items [i ], & mp_type_type ));
568+ mp_obj_type_t * t = items [i ];
569+ // TODO: Verify with CPy, tested on function type
570+ if (t -> make_new == NULL ) {
571+ nlr_raise (mp_obj_new_exception_msg_varg (& mp_type_TypeError , "type '%s' is not an acceptable base type" , qstr_str (t -> name )));
572+ }
573+ }
574+
477575 mp_obj_type_t * o = m_new0 (mp_obj_type_t , 1 );
478576 o -> base .type = & mp_type_type ;
479577 o -> name = name ;
@@ -487,6 +585,13 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict)
487585 o -> call = class_call ;
488586 o -> bases_tuple = bases_tuple ;
489587 o -> locals_dict = locals_dict ;
588+
589+ const mp_obj_type_t * native_base ;
590+ uint num_native_bases = class_count_native_bases (o , & native_base );
591+ if (num_native_bases > 1 ) {
592+ nlr_raise (mp_obj_new_exception_msg (& mp_type_TypeError , "multiple bases have instance lay-out conflict" ));
593+ }
594+
490595 return o ;
491596}
492597
@@ -536,7 +641,7 @@ STATIC void super_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
536641 mp_obj_tuple_get (type -> bases_tuple , & len , & items );
537642 for (uint i = 0 ; i < len ; i ++ ) {
538643 assert (MP_OBJ_IS_TYPE (items [i ], & mp_type_type ));
539- mp_obj_t member = mp_obj_class_lookup ((mp_obj_type_t * )items [i ], attr );
644+ mp_obj_t member = mp_obj_class_lookup ((mp_obj_type_t * )items [i ], attr , 0 );
540645 if (member != MP_OBJ_NULL ) {
541646 class_convert_return_attr (self -> obj , member , dest );
542647 return ;
0 commit comments