Skip to content

Commit 35e2a4e

Browse files
committed
py: Add built-in super.
1 parent e072349 commit 35e2a4e

10 files changed

Lines changed: 161 additions & 13 deletions

File tree

py/compile.c

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ typedef struct _compiler_t {
6161
int param_pass_num_dict_params;
6262
int param_pass_num_default_params;
6363

64+
bool func_arg_is_super; // used to compile special case of super() function call
65+
6466
scope_t *scope_head;
6567
scope_t *scope_cur;
6668

@@ -959,6 +961,7 @@ void compile_decorated(compiler_t *comp, mp_parse_node_struct_t *pns) {
959961
// nodes[1] contains arguments to the decorator function, if any
960962
if (!MP_PARSE_NODE_IS_NULL(pns_decorator->nodes[1])) {
961963
// call the decorator function with the arguments in nodes[1]
964+
comp->func_arg_is_super = false;
962965
compile_node(comp, pns_decorator->nodes[1]);
963966
}
964967
}
@@ -2062,9 +2065,38 @@ void compile_factor_2(compiler_t *comp, mp_parse_node_struct_t *pns) {
20622065
}
20632066
}
20642067

2068+
void compile_power(compiler_t *comp, mp_parse_node_struct_t *pns) {
2069+
// this is to handle special super() call
2070+
comp->func_arg_is_super = MP_PARSE_NODE_IS_ID(pns->nodes[0]) && MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]) == MP_QSTR_super;
2071+
2072+
compile_generic_all_nodes(comp, pns);
2073+
}
2074+
20652075
void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_struct_t *pns, bool is_method_call) {
20662076
// function to call is on top of stack
20672077

2078+
#if !MICROPY_EMIT_CPYTHON
2079+
// this is to handle special super() call
2080+
if (MP_PARSE_NODE_IS_NULL(pns->nodes[0]) && comp->func_arg_is_super && comp->scope_cur->kind == SCOPE_FUNCTION) {
2081+
EMIT_ARG(load_id, MP_QSTR___class__);
2082+
// get first argument to function
2083+
bool found = false;
2084+
for (int i = 0; i < comp->scope_cur->id_info_len; i++) {
2085+
if (comp->scope_cur->id_info[i].param) {
2086+
EMIT_ARG(load_fast, MP_QSTR_, comp->scope_cur->id_info[i].local_num);
2087+
found = true;
2088+
break;
2089+
}
2090+
}
2091+
if (!found) {
2092+
printf("TypeError: super() call cannot find self\n");
2093+
return;
2094+
}
2095+
EMIT_ARG(call_function, 2, 0, false, false);
2096+
return;
2097+
}
2098+
#endif
2099+
20682100
int old_n_arg_keyword = comp->n_arg_keyword;
20692101
bool old_have_star_arg = comp->have_star_arg;
20702102
bool old_have_dbl_star_arg = comp->have_dbl_star_arg;
@@ -2107,6 +2139,7 @@ void compile_power_trailers(compiler_t *comp, mp_parse_node_struct_t *pns) {
21072139
} else {
21082140
compile_node(comp, pns->nodes[i]);
21092141
}
2142+
comp->func_arg_is_super = false;
21102143
}
21112144
}
21122145

@@ -2836,7 +2869,7 @@ void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
28362869
#if MICROPY_EMIT_CPYTHON
28372870
EMIT_ARG(load_closure, MP_QSTR___class__, 0); // XXX check this is the correct local num
28382871
#else
2839-
EMIT_ARG(load_fast, MP_QSTR___class__, 0); // XXX check this is the correct local num
2872+
EMIT_ARG(load_fast, MP_QSTR___class__, id->local_num);
28402873
#endif
28412874
}
28422875
EMIT(return_value);
@@ -3044,6 +3077,8 @@ mp_obj_t mp_compile(mp_parse_node_t pn, qstr source_file, bool is_repl) {
30443077
comp->break_continue_except_level = 0;
30453078
comp->cur_except_level = 0;
30463079

3080+
comp->func_arg_is_super = false;
3081+
30473082
comp->scope_head = NULL;
30483083
comp->scope_cur = NULL;
30493084

@@ -3054,7 +3089,7 @@ mp_obj_t mp_compile(mp_parse_node_t pn, qstr source_file, bool is_repl) {
30543089
scope_t *module_scope = scope_new_and_link(comp, SCOPE_MODULE, pn, EMIT_OPT_NONE);
30553090

30563091
// compile pass 1
3057-
comp->emit = emit_pass1_new(MP_QSTR___class__);
3092+
comp->emit = emit_pass1_new();
30583093
comp->emit_method_table = &emit_pass1_method_table;
30593094
comp->emit_inline_asm = NULL;
30603095
comp->emit_inline_asm_method_table = NULL;

py/emit.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ extern const emit_method_table_t emit_bc_method_table;
117117
extern const emit_method_table_t emit_native_x64_method_table;
118118
extern const emit_method_table_t emit_native_thumb_method_table;
119119

120-
emit_t *emit_pass1_new(qstr qstr___class__);
120+
emit_t *emit_pass1_new(void);
121121
emit_t *emit_cpython_new(uint max_num_labels);
122122
emit_t *emit_bc_new(uint max_num_labels);
123123
emit_t *emit_native_x64_new(uint max_num_labels);

py/emitpass1.c

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,11 @@
1515
#include "emit.h"
1616

1717
struct _emit_t {
18-
qstr qstr___class__;
1918
scope_t *scope;
2019
};
2120

22-
emit_t *emit_pass1_new(qstr qstr___class__) {
21+
emit_t *emit_pass1_new(void) {
2322
emit_t *emit = m_new(emit_t, 1);
24-
emit->qstr___class__ = qstr___class__;
2523
return emit;
2624
}
2725

@@ -45,18 +43,21 @@ static void emit_pass1_load_id(emit_t *emit, qstr qstr) {
4543
bool added;
4644
id_info_t *id = scope_find_or_add_id(emit->scope, qstr, &added);
4745
if (added) {
48-
if (strcmp(qstr_str(qstr), "super") == 0 && emit->scope->kind == SCOPE_FUNCTION) {
46+
#if MICROPY_EMIT_CPYTHON
47+
if (qstr == MP_QSTR_super && emit->scope->kind == SCOPE_FUNCTION) {
4948
// special case, super is a global, and also counts as use of __class__
5049
id->kind = ID_INFO_KIND_GLOBAL_EXPLICIT;
51-
id_info_t *id2 = scope_find_local_in_parent(emit->scope, emit->qstr___class__);
50+
id_info_t *id2 = scope_find_local_in_parent(emit->scope, MP_QSTR___class__);
5251
if (id2 != NULL) {
53-
id2 = scope_find_or_add_id(emit->scope, emit->qstr___class__, &added);
52+
id2 = scope_find_or_add_id(emit->scope, MP_QSTR___class__, &added);
5453
if (added) {
5554
id2->kind = ID_INFO_KIND_FREE;
56-
scope_close_over_in_parents(emit->scope, emit->qstr___class__);
55+
scope_close_over_in_parents(emit->scope, MP_QSTR___class__);
5756
}
5857
}
59-
} else {
58+
} else
59+
#endif
60+
{
6061
id_info_t *id2 = scope_find_local_in_parent(emit->scope, qstr);
6162
if (id2 != NULL && (id2->kind == ID_INFO_KIND_LOCAL || id2->kind == ID_INFO_KIND_CELL || id2->kind == ID_INFO_KIND_FREE)) {
6263
id->kind = ID_INFO_KIND_FREE;

py/grammar.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ DEF_RULE(term_op, nc, or(4), tok(OP_STAR), tok(OP_SLASH), tok(OP_PERCENT), tok(O
214214
DEF_RULE(factor, nc, or(2), rule(factor_2), rule(power))
215215
DEF_RULE(factor_2, c(factor_2), and(2), rule(factor_op), rule(factor))
216216
DEF_RULE(factor_op, nc, or(3), tok(OP_PLUS), tok(OP_MINUS), tok(OP_TILDE))
217-
DEF_RULE(power, c(generic_all_nodes), and(3), rule(atom), opt_rule(power_trailers), opt_rule(power_dbl_star))
217+
DEF_RULE(power, c(power), and(3), rule(atom), opt_rule(power_trailers), opt_rule(power_dbl_star))
218218
DEF_RULE(power_trailers, c(power_trailers), one_or_more, rule(trailer))
219219
DEF_RULE(power_dbl_star, c(power_dbl_star), and(2), tok(OP_DBL_STAR), rule(factor))
220220

py/obj.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ mp_obj_t mp_obj_new_list(uint n, mp_obj_t *items);
231231
mp_obj_t mp_obj_new_dict(int n_args);
232232
mp_obj_t mp_obj_new_set(int n_args, mp_obj_t *items);
233233
mp_obj_t mp_obj_new_slice(mp_obj_t start, mp_obj_t stop, mp_obj_t step);
234+
mp_obj_t mp_obj_new_super(mp_obj_t type, mp_obj_t obj);
234235
mp_obj_t mp_obj_new_bound_meth(mp_obj_t meth, mp_obj_t self);
235236
mp_obj_t mp_obj_new_getitem_iter(mp_obj_t *args);
236237
mp_obj_t mp_obj_new_module(qstr module_name);
@@ -371,6 +372,9 @@ void mp_obj_fun_bc_get(mp_obj_t self_in, int *n_args, uint *n_state, const byte
371372

372373
mp_obj_t mp_identity(mp_obj_t self);
373374

375+
// super
376+
extern const mp_obj_type_t super_type;
377+
374378
// generator
375379
extern const mp_obj_type_t gen_instance_type;
376380

py/objtype.c

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,96 @@ mp_obj_t mp_obj_new_type(const char *name, mp_obj_t bases_tuple, mp_obj_t locals
362362
return o;
363363
}
364364

365+
/******************************************************************************/
366+
// super object
367+
368+
typedef struct _mp_obj_super_t {
369+
mp_obj_base_t base;
370+
mp_obj_t type;
371+
mp_obj_t obj;
372+
} mp_obj_super_t;
373+
374+
static void super_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
375+
mp_obj_super_t *self = self_in;
376+
print(env, "<super: ");
377+
mp_obj_print_helper(print, env, self->type, PRINT_STR);
378+
print(env, ", ");
379+
mp_obj_print_helper(print, env, self->obj, PRINT_STR);
380+
print(env, ">");
381+
}
382+
383+
static mp_obj_t super_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
384+
if (n_args != 2 || n_kw != 0) {
385+
// 0 arguments are turned into 2 in the compiler
386+
// 1 argument is not yet implemented
387+
nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "super() requires 2 arguments"));
388+
}
389+
return mp_obj_new_super(args[0], args[1]);
390+
}
391+
392+
// for fail, do nothing; for attr, dest[0] = value; for method, dest[0] = method, dest[1] = self
393+
static void super_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
394+
assert(MP_OBJ_IS_TYPE(self_in, &super_type));
395+
mp_obj_super_t *self = self_in;
396+
397+
assert(MP_OBJ_IS_TYPE(self->type, &mp_const_type));
398+
399+
mp_obj_type_t *type = self->type;
400+
401+
// for a const struct, this entry might be NULL
402+
if (type->bases_tuple == MP_OBJ_NULL) {
403+
return;
404+
}
405+
406+
uint len;
407+
mp_obj_t *items;
408+
mp_obj_tuple_get(type->bases_tuple, &len, &items);
409+
for (uint i = 0; i < len; i++) {
410+
assert(MP_OBJ_IS_TYPE(items[i], &mp_const_type));
411+
mp_obj_t member = mp_obj_class_lookup((mp_obj_type_t*)items[i], attr);
412+
if (member != MP_OBJ_NULL) {
413+
// XXX this and the code in class_load_attr need to be factored out
414+
if (mp_obj_is_callable(member)) {
415+
// class member is callable so build a bound method
416+
// check if the methods are functions, static or class methods
417+
// see http://docs.python.org/3.3/howto/descriptor.html
418+
// TODO check that this is the correct place to have this logic
419+
if (MP_OBJ_IS_TYPE(member, &mp_type_staticmethod)) {
420+
// return just the function
421+
dest[0] = ((mp_obj_staticmethod_t*)member)->fun;
422+
} else if (MP_OBJ_IS_TYPE(member, &mp_type_classmethod)) {
423+
// return a bound method, with self being the type of this object
424+
dest[0] = ((mp_obj_classmethod_t*)member)->fun;
425+
dest[1] = mp_obj_get_type(self->obj);
426+
} else {
427+
// return a bound method, with self being this object
428+
dest[0] = member;
429+
dest[1] = self->obj;
430+
}
431+
return;
432+
} else {
433+
// class member is a value, so just return that value
434+
dest[0] = member;
435+
return;
436+
}
437+
}
438+
}
439+
}
440+
441+
const mp_obj_type_t super_type = {
442+
{ &mp_const_type },
443+
"super",
444+
.print = super_print,
445+
.make_new = super_make_new,
446+
.load_attr = super_load_attr,
447+
};
448+
449+
mp_obj_t mp_obj_new_super(mp_obj_t type, mp_obj_t obj) {
450+
mp_obj_super_t *o = m_new_obj(mp_obj_super_t);
451+
*o = (mp_obj_super_t){{&super_type}, type, obj};
452+
return o;
453+
}
454+
365455
/******************************************************************************/
366456
// built-ins specific to types
367457

py/qstrdefs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ Q(repr)
8080
Q(set)
8181
Q(sorted)
8282
Q(sum)
83+
Q(super)
8384
Q(str)
8485
Q(sys)
8586
Q(tuple)

py/runtime.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ void rt_init(void) {
129129
mp_map_add_qstr(&map_builtins, MP_QSTR_list, (mp_obj_t)&list_type);
130130
mp_map_add_qstr(&map_builtins, MP_QSTR_map, (mp_obj_t)&map_type);
131131
mp_map_add_qstr(&map_builtins, MP_QSTR_set, (mp_obj_t)&set_type);
132+
mp_map_add_qstr(&map_builtins, MP_QSTR_super, (mp_obj_t)&super_type);
132133
mp_map_add_qstr(&map_builtins, MP_QSTR_tuple, (mp_obj_t)&tuple_type);
133134
mp_map_add_qstr(&map_builtins, MP_QSTR_type, (mp_obj_t)&mp_const_type);
134135
mp_map_add_qstr(&map_builtins, MP_QSTR_zip, (mp_obj_t)&zip_type);
@@ -852,7 +853,10 @@ static void rt_load_method_maybe(mp_obj_t base, qstr attr, mp_obj_t *dest) {
852853

853854
// if nothing found yet, look for built-in and generic names
854855
if (dest[0] == MP_OBJ_NULL) {
855-
if (attr == MP_QSTR___next__ && type->iternext != NULL) {
856+
if (attr == MP_QSTR___class__) {
857+
// a.__class__ is equivalent to type(a)
858+
dest[0] = type;
859+
} else if (attr == MP_QSTR___next__ && type->iternext != NULL) {
856860
dest[0] = (mp_obj_t)&mp_builtin_next_obj;
857861
dest[1] = base;
858862
} else if (type->load_attr == NULL) {

tests/bytecode/mp-tests/class6.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class A:
2+
def f(self):
3+
pass
4+
5+
class B(A):
6+
def f(self):
7+
super().f()

tests/bytecode/mp-tests/class7.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# accessing super, but not as a function call
2+
3+
class A:
4+
def f():
5+
#x = super
6+
print(super)

0 commit comments

Comments
 (0)