Skip to content

Commit 6ce4277

Browse files
committed
py: Make all LOAD_FAST ops check for unbound local.
This is necessary to catch all cases where locals are referenced before assignment. We still keep the _0, _1, _2 versions of LOAD_FAST to help reduced the byte code size in RAM. Addresses issue adafruit#457.
1 parent c2803db commit 6ce4277

6 files changed

Lines changed: 35 additions & 34 deletions

File tree

py/bc0.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
#define MP_BC_LOAD_FAST_1 (0x21)
1818
#define MP_BC_LOAD_FAST_2 (0x22)
1919
#define MP_BC_LOAD_FAST_N (0x23) // uint
20-
#define MP_BC_LOAD_FAST_CHECKED (0x24) // uint
2120
#define MP_BC_LOAD_DEREF (0x25) // uint
2221
#define MP_BC_LOAD_NAME (0x26) // qstr
2322
#define MP_BC_LOAD_GLOBAL (0x27) // qstr

py/emitbc.c

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -413,17 +413,11 @@ STATIC void emit_bc_load_null(emit_t *emit) {
413413
STATIC void emit_bc_load_fast(emit_t *emit, qstr qstr, uint id_flags, int local_num) {
414414
assert(local_num >= 0);
415415
emit_bc_pre(emit, 1);
416-
if (id_flags & ID_FLAG_IS_DELETED) {
417-
// This local may be deleted, so need to do a checked load.
418-
emit_write_byte_code_byte_uint(emit, MP_BC_LOAD_FAST_CHECKED, local_num);
419-
} else {
420-
// This local is never deleted, so can do a fast, uncheched load.
421-
switch (local_num) {
422-
case 0: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_0); break;
423-
case 1: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_1); break;
424-
case 2: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_2); break;
425-
default: emit_write_byte_code_byte_uint(emit, MP_BC_LOAD_FAST_N, local_num); break;
426-
}
416+
switch (local_num) {
417+
case 0: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_0); break;
418+
case 1: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_1); break;
419+
case 2: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_2); break;
420+
default: emit_write_byte_code_byte_uint(emit, MP_BC_LOAD_FAST_N, local_num); break;
427421
}
428422
}
429423

py/emitpass1.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ STATIC void emit_pass1_store_id(emit_t *emit, qstr qstr) {
9696

9797
STATIC void emit_pass1_delete_id(emit_t *emit, qstr qstr) {
9898
id_info_t *id = get_id_for_modification(emit->scope, qstr);
99-
id->flags |= ID_FLAG_IS_DELETED;
99+
// this flag is unused
100+
//id->flags |= ID_FLAG_IS_DELETED;
101+
(void)id; // suppress compiler warning
100102
}
101103

102104
const emit_method_table_t emit_pass1_method_table = {

py/showbc.c

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,6 @@ void mp_byte_code_print(const byte *ip, int len) {
132132
printf("LOAD_FAST_N " UINT_FMT, unum);
133133
break;
134134

135-
case MP_BC_LOAD_FAST_CHECKED:
136-
DECODE_UINT;
137-
printf("LOAD_FAST_CHECKED " UINT_FMT, unum);
138-
break;
139-
140135
case MP_BC_LOAD_DEREF:
141136
DECODE_UINT;
142137
printf("LOAD_DEREF " UINT_FMT, unum);

py/vm.c

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -252,25 +252,21 @@ mp_vm_return_kind_t mp_execute_byte_code_2(const byte *code_info, const byte **i
252252
break;
253253

254254
case MP_BC_LOAD_FAST_0:
255-
PUSH(fastn[0]);
256-
break;
255+
obj1 = fastn[0];
256+
goto load_check;
257257

258258
case MP_BC_LOAD_FAST_1:
259-
PUSH(fastn[-1]);
260-
break;
259+
obj1 = fastn[-1];
260+
goto load_check;
261261

262262
case MP_BC_LOAD_FAST_2:
263-
PUSH(fastn[-2]);
264-
break;
263+
obj1 = fastn[-2];
264+
goto load_check;
265265

266266
case MP_BC_LOAD_FAST_N:
267-
DECODE_UINT;
268-
PUSH(fastn[-unum]);
269-
break;
270-
271-
case MP_BC_LOAD_FAST_CHECKED:
272267
DECODE_UINT;
273268
obj1 = fastn[-unum];
269+
load_check:
274270
if (obj1 == MP_OBJ_NULL) {
275271
local_name_error:
276272
nlr_raise(mp_obj_new_exception_msg(&mp_type_NameError, "local variable referenced before assignment"));
@@ -281,11 +277,7 @@ mp_vm_return_kind_t mp_execute_byte_code_2(const byte *code_info, const byte **i
281277
case MP_BC_LOAD_DEREF:
282278
DECODE_UINT;
283279
obj1 = mp_obj_cell_get(fastn[-unum]);
284-
if (obj1 == MP_OBJ_NULL) {
285-
goto local_name_error;
286-
}
287-
PUSH(obj1);
288-
break;
280+
goto load_check;
289281

290282
case MP_BC_LOAD_NAME:
291283
DECODE_QSTR;

tests/basics/unboundlocal.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# locals referenced before assignment
2+
3+
def f1():
4+
print(x)
5+
x = 1
6+
7+
def f2():
8+
for i in range(0):
9+
print(i)
10+
print(i)
11+
12+
def check(f):
13+
try:
14+
f()
15+
except NameError:
16+
print("NameError")
17+
18+
check(f1)
19+
check(f2)

0 commit comments

Comments
 (0)