py/compile: Refactor handling of special super() call.

This patch refactors the handling of the special super() call within the
compiler.  It removes the need for a global (to the compiler) state variable
which keeps track of whether the subject of an expression is super.  The
handling of super() is now done entirely within one function, which makes
the compiler a bit cleaner and allows to easily add more optimisations to
super calls.

Changes to the code size are:

   bare-arm: +12
    minimal:  +0
   unix x64: +48
unix nanbox: -16
     stmhal:  +4
     cc3200:  +0
    esp8266: -56
This commit is contained in:
Damien George 2017-04-18 22:52:18 +10:00
parent 0dd6a59c89
commit 5335942b59
3 changed files with 69 additions and 42 deletions

View File

@ -115,7 +115,6 @@ typedef struct _compiler_t {
uint8_t is_repl; uint8_t is_repl;
uint8_t pass; // holds enum type pass_kind_t uint8_t pass; // holds enum type pass_kind_t
uint8_t func_arg_is_super; // used to compile special case of super() function call
uint8_t have_star; uint8_t have_star;
// try to keep compiler clean from nlr // try to keep compiler clean from nlr
@ -762,7 +761,6 @@ STATIC qstr compile_classdef_helper(compiler_t *comp, mp_parse_node_struct_t *pn
if (MP_PARSE_NODE_IS_STRUCT_KIND(parents, PN_classdef_2)) { if (MP_PARSE_NODE_IS_STRUCT_KIND(parents, PN_classdef_2)) {
parents = MP_PARSE_NODE_NULL; parents = MP_PARSE_NODE_NULL;
} }
comp->func_arg_is_super = false;
compile_trailer_paren_helper(comp, parents, false, 2); compile_trailer_paren_helper(comp, parents, false, 2);
// return its name (the 'C' in class C(...):") // return its name (the 'C' in class C(...):")
@ -836,7 +834,6 @@ STATIC void compile_decorated(compiler_t *comp, mp_parse_node_struct_t *pns) {
// nodes[1] contains arguments to the decorator function, if any // nodes[1] contains arguments to the decorator function, if any
if (!MP_PARSE_NODE_IS_NULL(pns_decorator->nodes[1])) { if (!MP_PARSE_NODE_IS_NULL(pns_decorator->nodes[1])) {
// call the decorator function with the arguments in nodes[1] // call the decorator function with the arguments in nodes[1]
comp->func_arg_is_super = false;
compile_node(comp, pns_decorator->nodes[1]); compile_node(comp, pns_decorator->nodes[1]);
} }
} }
@ -2175,10 +2172,74 @@ STATIC void compile_factor_2(compiler_t *comp, mp_parse_node_struct_t *pns) {
} }
STATIC void compile_atom_expr_normal(compiler_t *comp, mp_parse_node_struct_t *pns) { STATIC void compile_atom_expr_normal(compiler_t *comp, mp_parse_node_struct_t *pns) {
// this is to handle special super() call // compile the subject of the expression
comp->func_arg_is_super = MP_PARSE_NODE_IS_ID(pns->nodes[0]) && MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]) == MP_QSTR_super; compile_node(comp, pns->nodes[0]);
compile_generic_all_nodes(comp, pns); // compile_atom_expr_await may call us with a NULL node
if (MP_PARSE_NODE_IS_NULL(pns->nodes[1])) {
return;
}
// get the array of trailers (known to be an array of PARSE_NODE_STRUCT)
size_t num_trail = 1;
mp_parse_node_struct_t **pns_trail = (mp_parse_node_struct_t**)&pns->nodes[1];
if (MP_PARSE_NODE_STRUCT_KIND(pns_trail[0]) == PN_atom_expr_trailers) {
num_trail = MP_PARSE_NODE_STRUCT_NUM_NODES(pns_trail[0]);
pns_trail = (mp_parse_node_struct_t**)&pns_trail[0]->nodes[0];
}
// the current index into the array of trailers
size_t i = 0;
// handle special super() call
if (comp->scope_cur->kind == SCOPE_FUNCTION
&& MP_PARSE_NODE_IS_ID(pns->nodes[0])
&& MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]) == MP_QSTR_super
&& MP_PARSE_NODE_STRUCT_KIND(pns_trail[0]) == PN_trailer_paren
&& MP_PARSE_NODE_IS_NULL(pns_trail[0]->nodes[0])) {
// at this point we have matched "super()" within a function
// load the class for super to search for a parent
compile_load_id(comp, MP_QSTR___class__);
// look for first argument to function (assumes it's "self")
bool found = false;
id_info_t *id = &comp->scope_cur->id_info[0];
for (size_t n = comp->scope_cur->id_info_len; n > 0; --n, ++id) {
if (id->flags & ID_FLAG_IS_PARAM) {
// first argument found; load it
compile_load_id(comp, id->qst);
found = true;
break;
}
}
if (!found) {
compile_syntax_error(comp, (mp_parse_node_t)pns_trail[0],
"super() can't find self"); // really a TypeError
return;
}
// a super() call
EMIT_ARG(call_function, 2, 0, 0);
i = 1;
}
// compile the remaining trailers
for (; i < num_trail; i++) {
if (i + 1 < num_trail
&& MP_PARSE_NODE_STRUCT_KIND(pns_trail[i]) == PN_trailer_period
&& MP_PARSE_NODE_STRUCT_KIND(pns_trail[i + 1]) == PN_trailer_paren) {
// optimisation for method calls a.f(...), following PyPy
mp_parse_node_struct_t *pns_period = pns_trail[i];
mp_parse_node_struct_t *pns_paren = pns_trail[i + 1];
EMIT_ARG(load_method, MP_PARSE_NODE_LEAF_ARG(pns_period->nodes[0]));
compile_trailer_paren_helper(comp, pns_paren->nodes[0], true, 0);
i += 1;
} else {
// node is one of: trailer_paren, trailer_bracket, trailer_period
compile_node(comp, (mp_parse_node_t)pns_trail[i]);
}
}
} }
STATIC void compile_power(compiler_t *comp, mp_parse_node_struct_t *pns) { STATIC void compile_power(compiler_t *comp, mp_parse_node_struct_t *pns) {
@ -2189,23 +2250,6 @@ STATIC void compile_power(compiler_t *comp, mp_parse_node_struct_t *pns) {
STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_arglist, bool is_method_call, int n_positional_extra) { STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_arglist, bool is_method_call, int n_positional_extra) {
// function to call is on top of stack // function to call is on top of stack
// this is to handle special super() call
if (MP_PARSE_NODE_IS_NULL(pn_arglist) && comp->func_arg_is_super && comp->scope_cur->kind == SCOPE_FUNCTION) {
compile_load_id(comp, MP_QSTR___class__);
// look for first argument to function (assumes it's "self")
for (int i = 0; i < comp->scope_cur->id_info_len; i++) {
id_info_t *id = &comp->scope_cur->id_info[i];
if (id->flags & ID_FLAG_IS_PARAM) {
// first argument found; load it and call super
compile_load_id(comp, id->qst);
EMIT_ARG(call_function, 2, 0, 0);
return;
}
}
compile_syntax_error(comp, MP_PARSE_NODE_NULL, "super() call cannot find self"); // really a TypeError
return;
}
// get the list of arguments // get the list of arguments
mp_parse_node_t *args; mp_parse_node_t *args;
int n_args = mp_parse_node_extract_list(&pn_arglist, PN_arglist, &args); int n_args = mp_parse_node_extract_list(&pn_arglist, PN_arglist, &args);
@ -2285,23 +2329,6 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar
} }
} }
STATIC void compile_atom_expr_trailers(compiler_t *comp, mp_parse_node_struct_t *pns) {
int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
for (int i = 0; i < num_nodes; i++) {
if (i + 1 < num_nodes && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[i], PN_trailer_period) && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[i + 1], PN_trailer_paren)) {
// optimisation for method calls a.f(...), following PyPy
mp_parse_node_struct_t *pns_period = (mp_parse_node_struct_t*)pns->nodes[i];
mp_parse_node_struct_t *pns_paren = (mp_parse_node_struct_t*)pns->nodes[i + 1];
EMIT_ARG(load_method, MP_PARSE_NODE_LEAF_ARG(pns_period->nodes[0])); // get the method
compile_trailer_paren_helper(comp, pns_paren->nodes[0], true, 0);
i += 1;
} else {
compile_node(comp, pns->nodes[i]);
}
comp->func_arg_is_super = false;
}
}
// pns needs to have 2 nodes, first is lhs of comprehension, second is PN_comp_for node // pns needs to have 2 nodes, first is lhs of comprehension, second is PN_comp_for node
STATIC void compile_comprehension(compiler_t *comp, mp_parse_node_struct_t *pns, scope_kind_t kind) { STATIC void compile_comprehension(compiler_t *comp, mp_parse_node_struct_t *pns, scope_kind_t kind) {
assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 2); assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 2);

View File

@ -261,7 +261,7 @@ DEF_RULE(atom_expr_await, c(atom_expr_await), and(3), tok(KW_AWAIT), rule(atom),
DEF_RULE_NC(atom_expr, or(1), rule(atom_expr_normal)) DEF_RULE_NC(atom_expr, or(1), rule(atom_expr_normal))
#endif #endif
DEF_RULE(atom_expr_normal, c(atom_expr_normal), and_ident(2), rule(atom), opt_rule(atom_expr_trailers)) DEF_RULE(atom_expr_normal, c(atom_expr_normal), and_ident(2), rule(atom), opt_rule(atom_expr_trailers))
DEF_RULE(atom_expr_trailers, c(atom_expr_trailers), one_or_more, rule(trailer)) DEF_RULE_NC(atom_expr_trailers, one_or_more, rule(trailer))
DEF_RULE_NC(power_dbl_star, and_ident(2), tok(OP_DBL_STAR), rule(factor)) DEF_RULE_NC(power_dbl_star, and_ident(2), tok(OP_DBL_STAR), rule(factor))
// atom: '(' [yield_expr|testlist_comp] ')' | '[' [testlist_comp] ']' | '{' [dictorsetmaker] '}' | NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False' // atom: '(' [yield_expr|testlist_comp] ')' | '[' [testlist_comp] ']' | '{' [dictorsetmaker] '}' | NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False'

View File

@ -3,7 +3,7 @@
tok(4) tok(4)
[ 4] rule(22) (n=4) [ 4] rule(22) (n=4)
id(i) id(i)
[ 4] rule(45) (n=1) [ 4] rule(44) (n=1)
NULL NULL
[ 5] rule(8) (n=0) [ 5] rule(8) (n=0)
NULL NULL