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 #457.
This commit is contained in:
Damien George 2014-04-12 18:20:40 +01:00
parent c2803db010
commit 6ce4277551
6 changed files with 35 additions and 34 deletions

View File

@ -17,7 +17,6 @@
#define MP_BC_LOAD_FAST_1 (0x21)
#define MP_BC_LOAD_FAST_2 (0x22)
#define MP_BC_LOAD_FAST_N (0x23) // uint
#define MP_BC_LOAD_FAST_CHECKED (0x24) // uint
#define MP_BC_LOAD_DEREF (0x25) // uint
#define MP_BC_LOAD_NAME (0x26) // qstr
#define MP_BC_LOAD_GLOBAL (0x27) // qstr

View File

@ -413,17 +413,11 @@ STATIC void emit_bc_load_null(emit_t *emit) {
STATIC void emit_bc_load_fast(emit_t *emit, qstr qstr, uint id_flags, int local_num) {
assert(local_num >= 0);
emit_bc_pre(emit, 1);
if (id_flags & ID_FLAG_IS_DELETED) {
// This local may be deleted, so need to do a checked load.
emit_write_byte_code_byte_uint(emit, MP_BC_LOAD_FAST_CHECKED, local_num);
} else {
// This local is never deleted, so can do a fast, uncheched load.
switch (local_num) {
case 0: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_0); break;
case 1: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_1); break;
case 2: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_2); break;
default: emit_write_byte_code_byte_uint(emit, MP_BC_LOAD_FAST_N, local_num); break;
}
switch (local_num) {
case 0: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_0); break;
case 1: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_1); break;
case 2: emit_write_byte_code_byte(emit, MP_BC_LOAD_FAST_2); break;
default: emit_write_byte_code_byte_uint(emit, MP_BC_LOAD_FAST_N, local_num); break;
}
}

View File

@ -96,7 +96,9 @@ STATIC void emit_pass1_store_id(emit_t *emit, qstr qstr) {
STATIC void emit_pass1_delete_id(emit_t *emit, qstr qstr) {
id_info_t *id = get_id_for_modification(emit->scope, qstr);
id->flags |= ID_FLAG_IS_DELETED;
// this flag is unused
//id->flags |= ID_FLAG_IS_DELETED;
(void)id; // suppress compiler warning
}
const emit_method_table_t emit_pass1_method_table = {

View File

@ -132,11 +132,6 @@ void mp_byte_code_print(const byte *ip, int len) {
printf("LOAD_FAST_N " UINT_FMT, unum);
break;
case MP_BC_LOAD_FAST_CHECKED:
DECODE_UINT;
printf("LOAD_FAST_CHECKED " UINT_FMT, unum);
break;
case MP_BC_LOAD_DEREF:
DECODE_UINT;
printf("LOAD_DEREF " UINT_FMT, unum);

24
py/vm.c
View File

@ -252,25 +252,21 @@ dispatch_loop:
break;
case MP_BC_LOAD_FAST_0:
PUSH(fastn[0]);
break;
obj1 = fastn[0];
goto load_check;
case MP_BC_LOAD_FAST_1:
PUSH(fastn[-1]);
break;
obj1 = fastn[-1];
goto load_check;
case MP_BC_LOAD_FAST_2:
PUSH(fastn[-2]);
break;
obj1 = fastn[-2];
goto load_check;
case MP_BC_LOAD_FAST_N:
DECODE_UINT;
PUSH(fastn[-unum]);
break;
case MP_BC_LOAD_FAST_CHECKED:
DECODE_UINT;
obj1 = fastn[-unum];
load_check:
if (obj1 == MP_OBJ_NULL) {
local_name_error:
nlr_raise(mp_obj_new_exception_msg(&mp_type_NameError, "local variable referenced before assignment"));
@ -281,11 +277,7 @@ dispatch_loop:
case MP_BC_LOAD_DEREF:
DECODE_UINT;
obj1 = mp_obj_cell_get(fastn[-unum]);
if (obj1 == MP_OBJ_NULL) {
goto local_name_error;
}
PUSH(obj1);
break;
goto load_check;
case MP_BC_LOAD_NAME:
DECODE_QSTR;

View File

@ -0,0 +1,19 @@
# locals referenced before assignment
def f1():
print(x)
x = 1
def f2():
for i in range(0):
print(i)
print(i)
def check(f):
try:
f()
except NameError:
print("NameError")
check(f1)
check(f2)