Skip to content

Commit 2bf7c09

Browse files
committed
py: Properly implement deletion of locals and derefs, and detect errors.
Needed to reinstate 2 delete opcodes, to specifically check that a local is not deleted twice.
1 parent 11d8cd5 commit 2bf7c09

12 files changed

Lines changed: 130 additions & 33 deletions

File tree

py/bc0.h

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
// Micro Python byte-codes.
22
// The comment at the end of the line (if it exists) tells the arguments to the byte-code.
33

4-
// TODO Add MP_BC_LOAD_FAST_CHECKED and MP_BC_STORE_FAST_CHECKED for acting on those
5-
// locals which have del called on them anywhere in the function.
6-
// UnboundLocalError: local variable '%s' referenced before assignment
7-
84
#define MP_BC_LOAD_CONST_FALSE (0x10)
95
#define MP_BC_LOAD_CONST_NONE (0x11)
106
#define MP_BC_LOAD_CONST_TRUE (0x12)
@@ -21,12 +17,13 @@
2117
#define MP_BC_LOAD_FAST_1 (0x21)
2218
#define MP_BC_LOAD_FAST_2 (0x22)
2319
#define MP_BC_LOAD_FAST_N (0x23) // uint
24-
#define MP_BC_LOAD_DEREF (0x24) // uint
25-
#define MP_BC_LOAD_NAME (0x25) // qstr
26-
#define MP_BC_LOAD_GLOBAL (0x26) // qstr
27-
#define MP_BC_LOAD_ATTR (0x27) // qstr
28-
#define MP_BC_LOAD_METHOD (0x28) // qstr
29-
#define MP_BC_LOAD_BUILD_CLASS (0x29)
20+
#define MP_BC_LOAD_FAST_CHECKED (0x24) // uint
21+
#define MP_BC_LOAD_DEREF (0x25) // uint
22+
#define MP_BC_LOAD_NAME (0x26) // qstr
23+
#define MP_BC_LOAD_GLOBAL (0x27) // qstr
24+
#define MP_BC_LOAD_ATTR (0x28) // qstr
25+
#define MP_BC_LOAD_METHOD (0x29) // qstr
26+
#define MP_BC_LOAD_BUILD_CLASS (0x2a)
3027

3128
#define MP_BC_STORE_FAST_0 (0x30)
3229
#define MP_BC_STORE_FAST_1 (0x31)
@@ -38,8 +35,10 @@
3835
#define MP_BC_STORE_ATTR (0x37) // qstr
3936
#define MP_BC_STORE_SUBSCR (0x38)
4037

41-
#define MP_BC_DELETE_NAME (0x39) // qstr
42-
#define MP_BC_DELETE_GLOBAL (0x3a) // qstr
38+
#define MP_BC_DELETE_FAST (0x39) // uint
39+
#define MP_BC_DELETE_DEREF (0x3a) // uint
40+
#define MP_BC_DELETE_NAME (0x3b) // qstr
41+
#define MP_BC_DELETE_GLOBAL (0x3c) // qstr
4342

4443
#define MP_BC_DUP_TOP (0x40)
4544
#define MP_BC_DUP_TOP_TWO (0x41)

py/compile.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,7 @@ void close_over_variables_etc(compiler_t *comp, scope_t *this_scope, int n_pos_d
797797
EMIT_ARG(load_closure, id->qstr, id->local_num);
798798
#else
799799
// in Micro Python we load closures using LOAD_FAST
800-
EMIT_ARG(load_fast, id->qstr, id->local_num);
800+
EMIT_ARG(load_fast, id->qstr, id->flags, id->local_num);
801801
#endif
802802
nfree += 1;
803803
}
@@ -2208,8 +2208,8 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar
22082208
// get first argument to function
22092209
bool found = false;
22102210
for (int i = 0; i < comp->scope_cur->id_info_len; i++) {
2211-
if (comp->scope_cur->id_info[i].flags && ID_FLAG_IS_PARAM) {
2212-
EMIT_ARG(load_fast, MP_QSTR_, comp->scope_cur->id_info[i].local_num);
2211+
if (comp->scope_cur->id_info[i].flags & ID_FLAG_IS_PARAM) {
2212+
EMIT_ARG(load_fast, MP_QSTR_, comp->scope_cur->id_info[i].flags, comp->scope_cur->id_info[i].local_num);
22132213
found = true;
22142214
break;
22152215
}
@@ -2990,7 +2990,7 @@ void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
29902990
#if MICROPY_EMIT_CPYTHON
29912991
EMIT_ARG(load_closure, MP_QSTR___class__, 0); // XXX check this is the correct local num
29922992
#else
2993-
EMIT_ARG(load_fast, MP_QSTR___class__, id->local_num);
2993+
EMIT_ARG(load_fast, MP_QSTR___class__, id->flags, id->local_num);
29942994
#endif
29952995
}
29962996
EMIT(return_value);
@@ -3154,7 +3154,7 @@ void compile_scope_compute_things(compiler_t *comp, scope_t *scope) {
31543154
if (num_free > 0) {
31553155
for (int i = 0; i < scope->id_info_len; i++) {
31563156
id_info_t *id = &scope->id_info[i];
3157-
if (id->kind != ID_INFO_KIND_FREE || (id->flags && ID_FLAG_IS_PARAM)) {
3157+
if (id->kind != ID_INFO_KIND_FREE || (id->flags & ID_FLAG_IS_PARAM)) {
31583158
id->local_num += num_free;
31593159
}
31603160
}

py/emit.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ typedef struct _emit_method_table_t {
4343
void (*load_const_id)(emit_t *emit, qstr qstr);
4444
void (*load_const_str)(emit_t *emit, qstr qstr, bool bytes);
4545
void (*load_const_verbatim_str)(emit_t *emit, const char *str); // only needed for emitcpy
46-
void (*load_fast)(emit_t *emit, qstr qstr, int local_num);
46+
void (*load_fast)(emit_t *emit, qstr qstr, uint id_flags, int local_num);
4747
void (*load_deref)(emit_t *emit, qstr qstr, int local_num);
48-
void (*load_closure)(emit_t *emit, qstr qstr, int local_num);
48+
void (*load_closure)(emit_t *emit, qstr qstr, int local_num); // only needed for emitcpy
4949
void (*load_name)(emit_t *emit, qstr qstr);
5050
void (*load_global)(emit_t *emit, qstr qstr);
5151
void (*load_attr)(emit_t *emit, qstr qstr);

py/emitbc.c

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -408,14 +408,20 @@ STATIC void emit_bc_load_null(emit_t *emit) {
408408
emit_write_byte_code_byte(emit, MP_BC_LOAD_NULL);
409409
};
410410

411-
STATIC void emit_bc_load_fast(emit_t *emit, qstr qstr, int local_num) {
411+
STATIC void emit_bc_load_fast(emit_t *emit, qstr qstr, uint id_flags, int local_num) {
412412
assert(local_num >= 0);
413413
emit_bc_pre(emit, 1);
414-
switch (local_num) {
415-
case 0: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_0); break;
416-
case 1: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_1); break;
417-
case 2: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_2); break;
418-
default: emit_write_byte_code_byte_uint(emit, MP_BC_LOAD_FAST_N, local_num); break;
414+
if (id_flags & ID_FLAG_IS_DELETED) {
415+
// This local may be deleted, so need to do a checked load.
416+
emit_write_byte_code_byte_uint(emit, MP_BC_LOAD_FAST_CHECKED, local_num);
417+
} else {
418+
// This local is never deleted, so can do a fast, uncheched load.
419+
switch (local_num) {
420+
case 0: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_0); break;
421+
case 1: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_1); break;
422+
case 2: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_2); break;
423+
default: emit_write_byte_code_byte_uint(emit, MP_BC_LOAD_FAST_N, local_num); break;
424+
}
419425
}
420426
}
421427

@@ -491,13 +497,11 @@ STATIC void emit_bc_store_subscr(emit_t *emit) {
491497
}
492498

493499
STATIC void emit_bc_delete_fast(emit_t *emit, qstr qstr, int local_num) {
494-
emit_bc_load_null(emit);
495-
emit_bc_store_fast(emit, qstr, local_num);
500+
emit_write_byte_code_byte_uint(emit, MP_BC_DELETE_FAST, local_num);
496501
}
497502

498503
STATIC void emit_bc_delete_deref(emit_t *emit, qstr qstr, int local_num) {
499-
emit_bc_load_null(emit);
500-
emit_bc_store_deref(emit, qstr, local_num);
504+
emit_write_byte_code_byte_uint(emit, MP_BC_DELETE_DEREF, local_num);
501505
}
502506

503507
STATIC void emit_bc_delete_name(emit_t *emit, qstr qstr) {

py/emitcommon.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ void emit_common_load_id(emit_t *emit, const emit_method_table_t *emit_method_ta
2525
} else if (id->kind == ID_INFO_KIND_GLOBAL_EXPLICIT) {
2626
EMIT(load_global, qstr);
2727
} else if (id->kind == ID_INFO_KIND_LOCAL) {
28-
EMIT(load_fast, qstr, id->local_num);
28+
EMIT(load_fast, qstr, id->flags, id->local_num);
2929
} else if (id->kind == ID_INFO_KIND_CELL || id->kind == ID_INFO_KIND_FREE) {
3030
EMIT(load_deref, qstr, id->local_num);
3131
} else {

py/emitnative.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,7 @@ STATIC void emit_native_load_const_verbatim_str(emit_t *emit, const char *str) {
704704
assert(0);
705705
}
706706

707-
STATIC void emit_native_load_fast(emit_t *emit, qstr qstr, int local_num) {
707+
STATIC void emit_native_load_fast(emit_t *emit, qstr qstr, uint id_flags, int local_num) {
708708
vtype_kind_t vtype = emit->local_vtype[local_num];
709709
if (vtype == VTYPE_UNBOUND) {
710710
printf("ViperTypeError: local %s used before type known\n", qstr_str(qstr));

py/emitpass1.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ STATIC void emit_pass1_store_id(emit_t *emit, qstr qstr) {
9191
}
9292

9393
STATIC void emit_pass1_delete_id(emit_t *emit, qstr qstr) {
94-
get_id_for_modification(emit->scope, qstr);
94+
id_info_t *id = get_id_for_modification(emit->scope, qstr);
95+
id->flags |= ID_FLAG_IS_DELETED;
9596
}
9697

9798
const emit_method_table_t emit_pass1_method_table = {

py/scope.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ enum {
88

99
enum {
1010
ID_FLAG_IS_PARAM = 0x01,
11+
ID_FLAG_IS_DELETED = 0x02,
1112
};
1213

1314
typedef struct _id_info_t {

py/showbc.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,11 @@ void mp_byte_code_print(const byte *ip, int len) {
123123
printf("LOAD_FAST_N " UINT_FMT, unum);
124124
break;
125125

126+
case MP_BC_LOAD_FAST_CHECKED:
127+
DECODE_UINT;
128+
printf("LOAD_FAST_CHECKED " UINT_FMT, unum);
129+
break;
130+
126131
case MP_BC_LOAD_DEREF:
127132
DECODE_UINT;
128133
printf("LOAD_DEREF " UINT_FMT, unum);
@@ -193,6 +198,16 @@ void mp_byte_code_print(const byte *ip, int len) {
193198
printf("STORE_SUBSCR");
194199
break;
195200

201+
case MP_BC_DELETE_FAST:
202+
DECODE_UINT;
203+
printf("DELETE_FAST " UINT_FMT, unum);
204+
break;
205+
206+
case MP_BC_DELETE_DEREF:
207+
DECODE_UINT;
208+
printf("DELETE_DEREF " UINT_FMT, unum);
209+
break;
210+
196211
case MP_BC_DELETE_NAME:
197212
DECODE_QSTR;
198213
printf("DELETE_NAME %s", qstr_str(qstr));

py/vm.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,23 @@ mp_vm_return_kind_t mp_execute_byte_code_2(const byte *code_info, const byte **i
241241
PUSH(fastn[-unum]);
242242
break;
243243

244+
case MP_BC_LOAD_FAST_CHECKED:
245+
DECODE_UINT;
246+
obj1 = fastn[-unum];
247+
if (obj1 == MP_OBJ_NULL) {
248+
local_name_error:
249+
nlr_raise(mp_obj_new_exception_msg(&mp_type_NameError, "local variable referenced before assignment"));
250+
}
251+
PUSH(obj1);
252+
break;
253+
244254
case MP_BC_LOAD_DEREF:
245255
DECODE_UINT;
246-
PUSH(mp_obj_cell_get(fastn[-unum]));
256+
obj1 = mp_obj_cell_get(fastn[-unum]);
257+
if (obj1 == MP_OBJ_NULL) {
258+
goto local_name_error;
259+
}
260+
PUSH(obj1);
247261
break;
248262

249263
case MP_BC_LOAD_NAME:
@@ -314,6 +328,22 @@ mp_vm_return_kind_t mp_execute_byte_code_2(const byte *code_info, const byte **i
314328
sp -= 3;
315329
break;
316330

331+
case MP_BC_DELETE_FAST:
332+
DECODE_UINT;
333+
if (fastn[-unum] == MP_OBJ_NULL) {
334+
goto local_name_error;
335+
}
336+
fastn[-unum] = MP_OBJ_NULL;
337+
break;
338+
339+
case MP_BC_DELETE_DEREF:
340+
DECODE_UINT;
341+
if (mp_obj_cell_get(fastn[-unum]) == MP_OBJ_NULL) {
342+
goto local_name_error;
343+
}
344+
mp_obj_cell_set(fastn[-unum], MP_OBJ_NULL);
345+
break;
346+
317347
case MP_BC_DELETE_NAME:
318348
DECODE_QSTR;
319349
mp_delete_name(qst);

0 commit comments

Comments
 (0)