1 /*
2  * This file is part of the MicroPython project, http://micropython.org/
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2013, 2014 Damien P. George
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24  * THE SOFTWARE.
25  */
26 
27 #include <assert.h>
28 
29 #include "py/scope.h"
30 
31 #if MICROPY_ENABLE_COMPILER
32 
33 // These low numbered qstrs should fit in 8 bits.  See assertions below.
34 STATIC const uint8_t scope_simple_name_table[] = {
35     [SCOPE_MODULE] = MP_QSTR__lt_module_gt_,
36     [SCOPE_LAMBDA] = MP_QSTR__lt_lambda_gt_,
37     [SCOPE_LIST_COMP] = MP_QSTR__lt_listcomp_gt_,
38     [SCOPE_DICT_COMP] = MP_QSTR__lt_dictcomp_gt_,
39     [SCOPE_SET_COMP] = MP_QSTR__lt_setcomp_gt_,
40     [SCOPE_GEN_EXPR] = MP_QSTR__lt_genexpr_gt_,
41 };
42 
scope_new(scope_kind_t kind,mp_parse_node_t pn,qstr source_file,mp_uint_t emit_options)43 scope_t *scope_new(scope_kind_t kind, mp_parse_node_t pn, qstr source_file, mp_uint_t emit_options) {
44     // Make sure those qstrs indeed fit in an uint8_t.
45     MP_STATIC_ASSERT(MP_QSTR__lt_module_gt_ <= UINT8_MAX);
46     MP_STATIC_ASSERT(MP_QSTR__lt_lambda_gt_ <= UINT8_MAX);
47     MP_STATIC_ASSERT(MP_QSTR__lt_listcomp_gt_ <= UINT8_MAX);
48     MP_STATIC_ASSERT(MP_QSTR__lt_dictcomp_gt_ <= UINT8_MAX);
49     MP_STATIC_ASSERT(MP_QSTR__lt_setcomp_gt_ <= UINT8_MAX);
50     MP_STATIC_ASSERT(MP_QSTR__lt_genexpr_gt_ <= UINT8_MAX);
51 
52     scope_t *scope = m_new0(scope_t, 1);
53     scope->kind = kind;
54     scope->pn = pn;
55     scope->source_file = source_file;
56     if (kind == SCOPE_FUNCTION || kind == SCOPE_CLASS) {
57         assert(MP_PARSE_NODE_IS_STRUCT(pn));
58         scope->simple_name = MP_PARSE_NODE_LEAF_ARG(((mp_parse_node_struct_t *)pn)->nodes[0]);
59     } else {
60         scope->simple_name = scope_simple_name_table[kind];
61     }
62     scope->raw_code = mp_emit_glue_new_raw_code();
63     scope->emit_options = emit_options;
64     scope->id_info_alloc = MICROPY_ALLOC_SCOPE_ID_INIT;
65     scope->id_info = m_new(id_info_t, scope->id_info_alloc);
66 
67     return scope;
68 }
69 
scope_free(scope_t * scope)70 void scope_free(scope_t *scope) {
71     m_del(id_info_t, scope->id_info, scope->id_info_alloc);
72     m_del(scope_t, scope, 1);
73 }
74 
scope_find_or_add_id(scope_t * scope,qstr qst,id_info_kind_t kind)75 id_info_t *scope_find_or_add_id(scope_t *scope, qstr qst, id_info_kind_t kind) {
76     id_info_t *id_info = scope_find(scope, qst);
77     if (id_info != NULL) {
78         return id_info;
79     }
80 
81     // make sure we have enough memory
82     if (scope->id_info_len >= scope->id_info_alloc) {
83         scope->id_info = m_renew(id_info_t, scope->id_info, scope->id_info_alloc, scope->id_info_alloc + MICROPY_ALLOC_SCOPE_ID_INC);
84         scope->id_info_alloc += MICROPY_ALLOC_SCOPE_ID_INC;
85     }
86 
87     // add new id to end of array of all ids; this seems to match CPython
88     // important thing is that function arguments are first, but that is
89     // handled by the compiler because it adds arguments before compiling the body
90     id_info = &scope->id_info[scope->id_info_len++];
91 
92     id_info->kind = kind;
93     id_info->flags = 0;
94     id_info->local_num = 0;
95     id_info->qst = qst;
96     return id_info;
97 }
98 
scope_find(scope_t * scope,qstr qst)99 id_info_t *scope_find(scope_t *scope, qstr qst) {
100     for (mp_uint_t i = 0; i < scope->id_info_len; i++) {
101         if (scope->id_info[i].qst == qst) {
102             return &scope->id_info[i];
103         }
104     }
105     return NULL;
106 }
107 
scope_find_global(scope_t * scope,qstr qst)108 id_info_t *scope_find_global(scope_t *scope, qstr qst) {
109     while (scope->parent != NULL) {
110         scope = scope->parent;
111     }
112     return scope_find(scope, qst);
113 }
114 
scope_close_over_in_parents(scope_t * scope,qstr qst)115 STATIC void scope_close_over_in_parents(scope_t *scope, qstr qst) {
116     assert(scope->parent != NULL); // we should have at least 1 parent
117     for (scope_t *s = scope->parent;; s = s->parent) {
118         assert(s->parent != NULL); // we should not get to the outer scope
119         id_info_t *id = scope_find_or_add_id(s, qst, ID_INFO_KIND_UNDECIDED);
120         if (id->kind == ID_INFO_KIND_UNDECIDED) {
121             // variable not previously declared in this scope, so declare it as free and keep searching parents
122             id->kind = ID_INFO_KIND_FREE;
123         } else {
124             // variable is declared in this scope, so finish
125             if (id->kind == ID_INFO_KIND_LOCAL) {
126                 // variable local to this scope, close it over
127                 id->kind = ID_INFO_KIND_CELL;
128             } else {
129                 // ID_INFO_KIND_FREE: variable already closed over in a parent scope
130                 // ID_INFO_KIND_CELL: variable already closed over in this scope
131                 assert(id->kind == ID_INFO_KIND_FREE || id->kind == ID_INFO_KIND_CELL);
132             }
133             return;
134         }
135     }
136 }
137 
scope_check_to_close_over(scope_t * scope,id_info_t * id)138 void scope_check_to_close_over(scope_t *scope, id_info_t *id) {
139     if (scope->parent != NULL) {
140         for (scope_t *s = scope->parent; s->parent != NULL; s = s->parent) {
141             id_info_t *id2 = scope_find(s, id->qst);
142             if (id2 != NULL) {
143                 if (id2->kind == ID_INFO_KIND_LOCAL || id2->kind == ID_INFO_KIND_CELL || id2->kind == ID_INFO_KIND_FREE) {
144                     id->kind = ID_INFO_KIND_FREE;
145                     scope_close_over_in_parents(scope, id->qst);
146                 }
147                 break;
148             }
149         }
150     }
151 }
152 
153 #endif // MICROPY_ENABLE_COMPILER
154