From 5335942b599d85263d3c18eb99ff5ebd04a8bc98 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 18 Apr 2017 22:52:18 +1000 Subject: [PATCH] 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 --- py/compile.c | 107 ++++++++++++++++++----------- py/grammar.h | 2 +- tests/cmdline/cmd_parsetree.py.exp | 2 +- 3 files changed, 69 insertions(+), 42 deletions(-) diff --git a/py/compile.c b/py/compile.c index 2110867291..42c2cc3a24 100644 --- a/py/compile.c +++ b/py/compile.c @@ -115,7 +115,6 @@ typedef struct _compiler_t { uint8_t is_repl; 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; // 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)) { parents = MP_PARSE_NODE_NULL; } - comp->func_arg_is_super = false; compile_trailer_paren_helper(comp, parents, false, 2); // 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 if (!MP_PARSE_NODE_IS_NULL(pns_decorator->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]); } } @@ -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) { - // this is to handle special super() call - 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 the subject of the expression + 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) { @@ -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) { // 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 mp_parse_node_t *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 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); diff --git a/py/grammar.h b/py/grammar.h index e8041c0e08..930d96dc15 100644 --- a/py/grammar.h +++ b/py/grammar.h @@ -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)) #endif 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)) // atom: '(' [yield_expr|testlist_comp] ')' | '[' [testlist_comp] ']' | '{' [dictorsetmaker] '}' | NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False' diff --git a/tests/cmdline/cmd_parsetree.py.exp b/tests/cmdline/cmd_parsetree.py.exp index 17fecaf960..d9f81d8d4c 100644 --- a/tests/cmdline/cmd_parsetree.py.exp +++ b/tests/cmdline/cmd_parsetree.py.exp @@ -3,7 +3,7 @@ tok(4) [ 4] rule(22) (n=4) id(i) -[ 4] rule(45) (n=1) +[ 4] rule(44) (n=1) NULL [ 5] rule(8) (n=0) NULL