diff options
author | Damien George <damien.p.george@gmail.com> | 2015-02-07 18:33:58 +0000 |
---|---|---|
committer | Damien George <damien.p.george@gmail.com> | 2015-02-07 18:33:58 +0000 |
commit | 0bfc7638baa4c5a4a2351364ab770a188dcab302 (patch) | |
tree | 0127fcea13a875d37dd9cfa07dc6921bf387c578 /py | |
parent | e1e359ff59d6bbf09441cc1f3965be63f1046182 (diff) | |
download | micropython-0bfc7638baa4c5a4a2351364ab770a188dcab302.tar.gz micropython-0bfc7638baa4c5a4a2351364ab770a188dcab302.zip |
py: Protect mp_parse and mp_compile with nlr push/pop block.
To enable parsing constants more efficiently, mp_parse should be allowed
to raise an exception, and mp_compile can already raise a MemoryError.
So these functions need to be protected by an nlr push/pop block.
This patch adds that feature in all places. This allows to simplify how
mp_parse and mp_compile are called: they now raise an exception if they
have an error and so explicit checking is not needed anymore.
Diffstat (limited to 'py')
-rw-r--r-- | py/compile.c | 2 | ||||
-rw-r--r-- | py/compile.h | 1 | ||||
-rw-r--r-- | py/parse.c | 37 | ||||
-rw-r--r-- | py/parse.h | 12 | ||||
-rw-r--r-- | py/parsehelper.c | 87 | ||||
-rw-r--r-- | py/parsehelper.h | 36 | ||||
-rw-r--r-- | py/py.mk | 1 | ||||
-rw-r--r-- | py/runtime.c | 54 |
8 files changed, 48 insertions, 182 deletions
diff --git a/py/compile.c b/py/compile.c index 8e4723f230..7023e9c403 100644 --- a/py/compile.c +++ b/py/compile.c @@ -3823,7 +3823,7 @@ mp_obj_t mp_compile(mp_parse_node_t pn, qstr source_file, uint emit_opt, bool is m_del_obj(compiler_t, comp); if (compile_error != MP_OBJ_NULL) { - return compile_error; + nlr_raise(compile_error); } else { #if MICROPY_EMIT_CPYTHON // can't create code, so just return true diff --git a/py/compile.h b/py/compile.h index 9d612da532..2d15de8b9a 100644 --- a/py/compile.h +++ b/py/compile.h @@ -39,6 +39,7 @@ enum { MP_EMIT_OPT_ASM_THUMB, }; +// the compiler will raise an exception if an error occurred // the compiler will free the parse tree (pn) before it returns mp_obj_t mp_compile(mp_parse_node_t pn, qstr source_file, uint emit_opt, bool is_repl); diff --git a/py/parse.c b/py/parse.c index 881a11e734..569cf257a7 100644 --- a/py/parse.c +++ b/py/parse.c @@ -30,6 +30,7 @@ #include <assert.h> #include <string.h> +#include "py/nlr.h" #include "py/lexer.h" #include "py/parse.h" #include "py/parsenum.h" @@ -382,7 +383,7 @@ STATIC void push_result_rule(parser_t *parser, mp_uint_t src_line, const rule_t push_result_node(parser, (mp_parse_node_t)pn); } -mp_parse_node_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, mp_parse_error_kind_t *parse_error_kind_out) { +mp_parse_node_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { // initialise parser and allocate memory for its stacks @@ -717,15 +718,15 @@ mp_parse_node_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, mp_p } } - mp_parse_node_t result; + mp_obj_t exc = MP_OBJ_NULL; + mp_parse_node_t result = MP_PARSE_NODE_NULL; // check if we had a memory error if (parser.had_memory_error) { memory_error: - *parse_error_kind_out = MP_PARSE_ERROR_MEMORY; - result = MP_PARSE_NODE_NULL; + exc = mp_obj_new_exception_msg(&mp_type_MemoryError, + "parser could not allocate enough memory"); goto finished; - } // check we are at the end of the token stream @@ -747,17 +748,30 @@ finished: // free the memory that we don't need anymore m_del(rule_stack_t, parser.rule_stack, parser.rule_stack_alloc); m_del(mp_parse_node_t, parser.result_stack, parser.result_stack_alloc); - - // return the result - return result; + // we also free the lexer on behalf of the caller (see below) + + if (exc != MP_OBJ_NULL) { + // had an error so raise the exception + // add traceback to give info about file name and location + // we don't have a 'block' name, so just pass the NULL qstr to indicate this + mp_obj_exception_add_traceback(exc, lex->source_name, lex->tok_line, MP_QSTR_NULL); + mp_lexer_free(lex); + nlr_raise(exc); + } else { + mp_lexer_free(lex); + return result; + } syntax_error: if (lex->tok_kind == MP_TOKEN_INDENT) { - *parse_error_kind_out = MP_PARSE_ERROR_UNEXPECTED_INDENT; + exc = mp_obj_new_exception_msg(&mp_type_IndentationError, + "unexpected indent"); } else if (lex->tok_kind == MP_TOKEN_DEDENT_MISMATCH) { - *parse_error_kind_out = MP_PARSE_ERROR_UNMATCHED_UNINDENT; + exc = mp_obj_new_exception_msg(&mp_type_IndentationError, + "unindent does not match any outer indentation level"); } else { - *parse_error_kind_out = MP_PARSE_ERROR_INVALID_SYNTAX; + exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, + "invalid syntax"); #ifdef USE_RULE_NAME // debugging: print the rule name that failed and the token printf("rule: %s\n", rule->rule_name); @@ -766,6 +780,5 @@ syntax_error: #endif #endif } - result = MP_PARSE_NODE_NULL; goto finished; } diff --git a/py/parse.h b/py/parse.h index 4e7f2b9d19..ee0025a7b2 100644 --- a/py/parse.h +++ b/py/parse.h @@ -90,14 +90,8 @@ typedef enum { MP_PARSE_EVAL_INPUT, } mp_parse_input_kind_t; -typedef enum { - MP_PARSE_ERROR_MEMORY, - MP_PARSE_ERROR_UNEXPECTED_INDENT, - MP_PARSE_ERROR_UNMATCHED_UNINDENT, - MP_PARSE_ERROR_INVALID_SYNTAX, -} mp_parse_error_kind_t; - -// returns MP_PARSE_NODE_NULL on error, and then parse_error_kind_out is valid -mp_parse_node_t mp_parse(struct _mp_lexer_t *lex, mp_parse_input_kind_t input_kind, mp_parse_error_kind_t *parse_error_kind_out); +// the parser will raise an exception if an error occurred +// the parser will free the lexer before it returns +mp_parse_node_t mp_parse(struct _mp_lexer_t *lex, mp_parse_input_kind_t input_kind); #endif // __MICROPY_INCLUDED_PY_PARSE_H__ diff --git a/py/parsehelper.c b/py/parsehelper.c deleted file mode 100644 index 904268109b..0000000000 --- a/py/parsehelper.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2013, 2014 Damien P. George - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -// these functions are separate from parse.c to keep parser independent of mp_obj_t - -#include <stdio.h> - -#include "py/parsehelper.h" - -#define STR_MEMORY "parser could not allocate enough memory" -#define STR_UNEXPECTED_INDENT "unexpected indent" -#define STR_UNMATCHED_UNINDENT "unindent does not match any outer indentation level" -#define STR_INVALID_SYNTAX "invalid syntax" - -void mp_parse_show_exception(mp_lexer_t *lex, mp_parse_error_kind_t parse_error_kind) { - printf(" File \"%s\", line " UINT_FMT ", column " UINT_FMT "\n", qstr_str(lex->source_name), lex->tok_line, lex->tok_column); - switch (parse_error_kind) { - case MP_PARSE_ERROR_MEMORY: - printf("MemoryError: %s\n", STR_MEMORY); - break; - - case MP_PARSE_ERROR_UNEXPECTED_INDENT: - printf("IndentationError: %s\n", STR_UNEXPECTED_INDENT); - break; - - case MP_PARSE_ERROR_UNMATCHED_UNINDENT: - printf("IndentationError: %s\n", STR_UNMATCHED_UNINDENT); - break; - - case MP_PARSE_ERROR_INVALID_SYNTAX: - default: - printf("SyntaxError: %s\n", STR_INVALID_SYNTAX); - break; - } -} - -mp_obj_t mp_parse_make_exception(mp_lexer_t *lex, mp_parse_error_kind_t parse_error_kind) { - // make exception object - mp_obj_t exc; - switch (parse_error_kind) { - case MP_PARSE_ERROR_MEMORY: - exc = mp_obj_new_exception_msg(&mp_type_MemoryError, STR_MEMORY); - break; - - case MP_PARSE_ERROR_UNEXPECTED_INDENT: - exc = mp_obj_new_exception_msg(&mp_type_IndentationError, STR_UNEXPECTED_INDENT); - break; - - case MP_PARSE_ERROR_UNMATCHED_UNINDENT: - exc = mp_obj_new_exception_msg(&mp_type_IndentationError, STR_UNMATCHED_UNINDENT); - break; - - case MP_PARSE_ERROR_INVALID_SYNTAX: - default: - exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, STR_INVALID_SYNTAX); - break; - } - - // add traceback to give info about file name and location - // we don't have a 'block' name, so just pass the NULL qstr to indicate this - mp_obj_exception_add_traceback(exc, lex->source_name, lex->tok_line, MP_QSTR_NULL); - - return exc; -} diff --git a/py/parsehelper.h b/py/parsehelper.h deleted file mode 100644 index 1763809bad..0000000000 --- a/py/parsehelper.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2013, 2014 Damien P. George - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef __MICROPY_INCLUDED_PY_PARSEHELPER_H__ -#define __MICROPY_INCLUDED_PY_PARSEHELPER_H__ - -#include "py/lexer.h" -#include "py/parse.h" -#include "py/obj.h" - -void mp_parse_show_exception(mp_lexer_t *lex, mp_parse_error_kind_t parse_error_kind); -mp_obj_t mp_parse_make_exception(mp_lexer_t *lex, mp_parse_error_kind_t parse_error_kind); - -#endif // __MICROPY_INCLUDED_PY_PARSEHELPER_H__ @@ -28,7 +28,6 @@ PY_O_BASENAME = \ lexerstr.o \ lexerunix.o \ parse.o \ - parsehelper.o \ scope.o \ compile.o \ emitcommon.o \ diff --git a/py/runtime.c b/py/runtime.c index fc8d128e77..080d061cd1 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -30,7 +30,6 @@ #include "py/mpstate.h" #include "py/nlr.h" -#include "py/parsehelper.h" #include "py/parsenum.h" #include "py/compile.h" #include "py/objtuple.h" @@ -1228,47 +1227,30 @@ void mp_import_all(mp_obj_t module) { // this is implemented in this file so it can optimise access to locals/globals mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_input_kind, mp_obj_dict_t *globals, mp_obj_dict_t *locals) { - // parse the string - mp_parse_error_kind_t parse_error_kind; - mp_parse_node_t pn = mp_parse(lex, parse_input_kind, &parse_error_kind); - - if (pn == MP_PARSE_NODE_NULL) { - // parse error; raise exception - mp_obj_t exc = mp_parse_make_exception(lex, parse_error_kind); - mp_lexer_free(lex); - nlr_raise(exc); - } - - qstr source_name = lex->source_name; - mp_lexer_free(lex); + // save context + mp_obj_dict_t *volatile old_globals = mp_globals_get(); + mp_obj_dict_t *volatile old_locals = mp_locals_get(); - // save context and set new context - mp_obj_dict_t *old_globals = mp_globals_get(); - mp_obj_dict_t *old_locals = mp_locals_get(); + // set new context mp_globals_set(globals); mp_locals_set(locals); - // compile the string - mp_obj_t module_fun = mp_compile(pn, source_name, MP_EMIT_OPT_NONE, false); - - // check if there was a compile error - if (mp_obj_is_exception_instance(module_fun)) { - mp_globals_set(old_globals); - mp_locals_set(old_locals); - nlr_raise(module_fun); - } - - // for compile only - if (MICROPY_PY_BUILTINS_COMPILE && globals == NULL) { - mp_globals_set(old_globals); - mp_locals_set(old_locals); - return module_fun; - } - - // complied successfully, execute it nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { - mp_obj_t ret = mp_call_function_0(module_fun); + qstr source_name = lex->source_name; + mp_parse_node_t pn = mp_parse(lex, parse_input_kind); + mp_obj_t module_fun = mp_compile(pn, source_name, MP_EMIT_OPT_NONE, false); + + mp_obj_t ret; + if (MICROPY_PY_BUILTINS_COMPILE && globals == NULL) { + // for compile only, return value is the module function + ret = module_fun; + } else { + // execute module function and get return value + ret = mp_call_function_0(module_fun); + } + + // finish nlr block, restore context and return value nlr_pop(); mp_globals_set(old_globals); mp_locals_set(old_locals); |