Skip to content

Commit a939f37

Browse files
committed
fix: preserve nested exception semantics
1 parent 84c27d4 commit a939f37

8 files changed

Lines changed: 155 additions & 54 deletions

File tree

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# R1.4.1 任务报告
2+
3+
## 结论
4+
5+
已在现有 parser/ASM/VM 异常框架内修复 Issue #368 的两个剩余可局部闭环问题:handler 内再次抛出的异常可继续传播到外层 try;父类 handler 的 alias 按实际异常类型构造对象,不再错误绑定为父类实例。未新增 VM 指令,未修改 `PikaVMError` 布局,未扩大 typed except 语法范围。
6+
7+
## 实现
8+
9+
- 将线程现有 `try_state` 从二值用法改为同字段的嵌套深度计数;`TRY` 递增、`NTR` 递减,所有 try 内判断统一按非零处理。handler 内抛出时仍保留外层深度,因此继续跳转到外层 `NTR/EXP`
10+
- alias 字节码继续复用 `EXP -> JEZ -> OUT``EXP` 命中后根据错误栈中的 `exception_type` 复用既有异常哈希表,在 builtins 方法表定位实际异常构造器,构造对象并交给现有 `OUT`;parser 不再按 handler 声明类型执行 `RUN`
11+
- 为既有跨文件函数 `obj_getProp()` 补充头文件声明,消除隐式返回类型导致的指针截断风险。
12+
- 本轮恢复的是 alias 的实际异常类型语义;原异常对象 identity、消息和参数未存入现有 `PikaVMError`,若要求完整保留需扩展错误状态模型,超出本轮非架构边界。
13+
14+
## 测试
15+
16+
- 失败基线:`except ArithmeticError as err``isinstance(err, ZeroDivisionError)` 为 0;内层 ValueError handler 再抛 TypeError 未被外层捕获。
17+
- 修复后 typed-except runtime matrix 和 legacy untyped `EXP`:2/2 通过,每个场景析构后 `pikaMemNow()==0`
18+
- parser 合法/非法 typed except 邻接组合:5/5 通过。
19+
- Docker 隔离网络、LVGL 和既定不稳定 REPL 项的最终 Linux 回归:455/455 通过。
20+
- Valgrind 定向:50,586 alloc/free,退出时 0 bytes,0 errors。
21+
- minimal 配置 Release core 完整编译通过,临时配置已恢复 default。
22+
23+
## 资源
24+
25+
以提交 `84c27d435` 为同路径、同容器、Release、`PIKA_CONFIG_ENABLE=ON` 基线:
26+
27+
- default:text 1,041,801 -> 1,042,129 B,增加 328 B;data 126,480 B、bss 24,342 B 不变。
28+
- minimal:text 990,809 -> 990,825 B,增加 16 B;data 47,436 B、bss 23,078 B 不变。
29+
- 额外寄存器收敛前 default 曾增加 456 B;复用 `EXP` 返回寄存器后回收 128 B。
30+
- 剩余 Flash 增量已登记 R1.4.2,按第二阶段独立回收,不撤销本轮正确性合同。
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# R1.4.2 阶段报告
2+
3+
## 当前状态
4+
5+
资源债务尚未回收,任务保持进行中。已确认的最小正确性实现相对 `84c27d435` 为 default +328 B text、minimal +16 B text,data/bss 均不变。
6+
7+
## 已拒绝实验
8+
9+
- 直接调用哈希定位到的零参数原生异常 constructor:typed-except 测试通过且矩阵分配减少 172 次,但 default 净增扩大到 472 B、minimal 仍增加 16 B;异常 alias 是冷路径,不接受以额外 Flash 换取该分配收益,已撤回。
10+
- 对函数/类定义处理器 `__VM_instruction_handler_DEF` 单独使用 GCC `Os`:default 净增 416 B、minimal 净增 104 B,均劣于基线方案,已撤回。
11+
12+
## 后续边界
13+
14+
下一轮只接受经相同 Release A/B 证明 default 与 minimal 总体 text 均不增长的补偿;不得改变 R1.4.1 的嵌套传播、alias 实际类型和 legacy `EXP` 合同,不得降低 VM/runtime 热路径性能或增加 RAM。

docs/MDTODO/pikapython-release-engineering.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,16 @@
2222

2323
将 release_diff.py 改为不 checkout 工作区的只读比较,并增加 release-check 统一校验运行时版本、version_config、CMSIS Pack、包索引 commit 和 tag 一致性;依赖 R1.2,完成任务后将详细报告写入[任务报告](./details/pikapython-release-engineering/R1.3_Task_Report.md)
2424

25-
### R1.4
25+
### R1.4 [in_progress]
2626

2727
复验并收口 Issue #367#368#369:已修复项补证据后关闭,仍复现的现有语法子集误拒绝作为 RC blocker 最小修复并回归;依赖 R1.1,完成任务后将详细报告写入[任务报告](./details/pikapython-release-engineering/R1.4_Task_Report.md)
2828

29+
#### R1.4.1 [completed]
30+
31+
补齐并修复 Issue #368 剩余的 handler 内再次抛出到外层及父类捕获子类时 alias 保留原始异常实例语义;只在现有 typed except 子集和 VM 错误状态框架内最小处理,不改变异常/字节码大架构,完成 default/minimal 回归与资源核算,完成任务后将详细报告写入[任务报告](./details/pikapython-release-engineering/R1.4.1_Task_Report.md)
32+
#### R1.4.2 [in_progress]
33+
34+
第二阶段回收 R1.4.1 异常正确性修复的 Flash 增量:以同配置 Release A/B 为准,在不改变 typed except、嵌套传播和 alias 实际类型合同、不降低 VM/runtime 性能且不增加 RAM 的前提下,回收 default 328 B、minimal 16 B text;允许由其他低风险位置补偿,分别记录原增量、补偿收益和最终净变化,完成任务后将详细报告写入[任务报告](./details/pikapython-release-engineering/R1.4.2_Task_Report.md)
2935
### R1.5
3036

3137
修复 Issue #372 的 optimize_speed runtime 缓存受 GTest 注册和分配布局影响问题,补不依赖偶然布局的回归并保持资源总体不膨胀;依赖 R1.1,完成任务后将详细报告写入[任务报告](./details/pikapython-release-engineering/R1.5_Task_Report.md)

port/linux/test/pikaMain-test.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,9 +504,16 @@ TEST(pikaMain, python3_typed_except_runtime_matrix) {
504504
{"result=0\ntry:\n raise ValueError\nexcept ValueError as err:\n"
505505
" result=isinstance(err, ValueError)\n",
506506
1},
507+
{"result=0\ntry:\n 1/0\nexcept ArithmeticError as err:\n"
508+
" result=isinstance(err, ZeroDivisionError)\n",
509+
1},
507510
{"result=0\ntry:\n try:\n 1/0\n except ValueError:\n"
508511
" result=1\nexcept ZeroDivisionError:\n result=2\n",
509512
2},
513+
{"result=0\ntry:\n try:\n raise ValueError\n"
514+
" except ValueError:\n raise TypeError\n"
515+
"except TypeError:\n result=1\n",
516+
1},
510517
{"result=0\ntry:\n 1/0\nexcept Exception:\n result=1\n", 1},
511518
{"result=0\ntry:\n 1/0\nexcept ArithmeticError:\n result=1\n",
512519
1},

src/PikaObj.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4550,7 +4550,7 @@ void obj_setSysOut(PikaObj* self, char* fmt, ...) {
45504550
if (self->vmFrame->error.code == PIKA_RES_OK) {
45514551
self->vmFrame->error.code = PIKA_RES_ERR_RUNTIME_ERROR;
45524552
}
4553-
if (self->vmFrame->vm_thread->try_state == TRY_STATE_INNER) {
4553+
if (self->vmFrame->vm_thread->try_state != TRY_STATE_NONE) {
45544554
return;
45554555
}
45564556
}

src/PikaObj.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ PikaObj* obj_newObjFromConstructor(PikaObj* self,
397397
NewFun newClassFun);
398398
PikaObj* newRootObj(char* name, NewFun newObjFun);
399399
PikaObj* obj_getClassObj(PikaObj* obj);
400+
NativeProperty* obj_getProp(PikaObj* self);
400401
Arg* obj_getMethodArg(PikaObj* obj, char* methodName);
401402
Arg* obj_getMethodArg_noalloc(PikaObj* obj, char* methodName, Arg* arg_reg);
402403
Arg* obj_getMethodArgWithFullPath(PikaObj* obj, char* methodPath);

src/PikaParser.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4229,10 +4229,7 @@ PIKA_PARSER_OPTIMIZE_SIZE char* AST_genAsm_top(AST* oAST, Args* outBuffs) {
42294229
sPikaAsm = strsAppend(&buffs, sPikaAsm, "0 JEZ 1\n");
42304230
char* sAlias = AST_getNodeAttr(oAST, "except_alias");
42314231
if (NULL != sAlias) {
4232-
sPikaAsm = strsAppend(&buffs, sPikaAsm, "0 RUN ");
4233-
sPikaAsm = strsAppend(
4234-
&buffs, sPikaAsm, AST_getNodeAttr(oAST, "except"));
4235-
sPikaAsm = strsAppend(&buffs, sPikaAsm, "\n0 OUT ");
4232+
sPikaAsm = strsAppend(&buffs, sPikaAsm, "0 OUT ");
42364233
sPikaAsm = strsAppend(&buffs, sPikaAsm, sAlias);
42374234
sPikaAsm = strsAppend(&buffs, sPikaAsm, "\n");
42384235
}

src/PikaVM.c

Lines changed: 94 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,10 @@ void pika_vmSignal_setCtrlClear(void) {
436436

437437
/* head declare start */
438438
static uint32_t PikaVMFrame_getInputArgNum(PikaVMFrame* vm);
439+
static Arg* VM_instruction_handler_RUN(PikaObj* self,
440+
PikaVMFrame* vm,
441+
char* data,
442+
Arg* arg_ret_reg);
439443
static VMParameters* __pikaVM_runByteCodeFrameWithState(
440444
PikaObj* self,
441445
VMParameters* locals,
@@ -468,52 +472,54 @@ static void PikaVMFrame_setErrorCode(PikaVMFrame* vm, int8_t error_code) {
468472
#if PIKA_SYNTAX_EXCEPTION_ENABLE
469473
Arg* _type(Arg* arg);
470474

475+
static const Hash exception_hashes[] = {
476+
1142003074, 1346345301, 984971860, 1581723060, 675810883,
477+
2000609318, 603406554, 1802197452, 366528582, 351017945,
478+
1985385347, 650622313, 489249939, 1035868745, 799477063,
479+
823649752, 247530422, 838371290, 1538545476, 1777242020,
480+
2134842892, 1950608113, 742300048, 1836878090, 746628451,
481+
1016945095, 282021160, 196451025, 1431286868, 173529092,
482+
1299431183, 1326281044, 1956727606,
483+
};
484+
485+
static const uint8_t exception_types[] = {
486+
PIKA_EXCEPTION_MODULE_NOT_FOUND,
487+
PIKA_EXCEPTION_UNBOUND_LOCAL,
488+
PIKA_EXCEPTION_ZERO_DIVISION,
489+
PIKA_EXCEPTION_NOT_IMPLEMENTED,
490+
PIKA_EXCEPTION_KEYBOARD_INTERRUPT,
491+
PIKA_EXCEPTION_GENERATOR_EXIT,
492+
PIKA_EXCEPTION_STOP_ITERATION,
493+
PIKA_EXCEPTION_INDENTATION,
494+
PIKA_EXCEPTION_TAB,
495+
PIKA_EXCEPTION_ARITHMETIC,
496+
PIKA_EXCEPTION_OVERFLOW,
497+
PIKA_EXCEPTION_RECURSION,
498+
PIKA_EXCEPTION_RUNTIME,
499+
PIKA_EXCEPTION_LOOKUP,
500+
PIKA_EXCEPTION_INDEX,
501+
PIKA_EXCEPTION_KEY,
502+
PIKA_EXCEPTION_UNICODE,
503+
PIKA_EXCEPTION_UNICODE,
504+
PIKA_EXCEPTION_UNICODE,
505+
PIKA_EXCEPTION_UNICODE,
506+
PIKA_EXCEPTION_VALUE,
507+
PIKA_EXCEPTION_TYPE,
508+
PIKA_EXCEPTION_NAME,
509+
PIKA_EXCEPTION_IMPORT,
510+
PIKA_EXCEPTION_ATTRIBUTE,
511+
PIKA_EXCEPTION_ASSERTION,
512+
PIKA_EXCEPTION_MEMORY,
513+
PIKA_EXCEPTION_OS,
514+
PIKA_EXCEPTION_SYSTEM,
515+
PIKA_EXCEPTION_SYSTEM_EXIT,
516+
PIKA_EXCEPTION_BASE,
517+
PIKA_EXCEPTION_EXCEPTION,
518+
PIKA_EXCEPTION_SYNTAX,
519+
};
520+
471521
PIKA_EXCEPTION_OPTIMIZE_SIZE static PikaExceptionType _exceptionTypeFromName(
472522
char* name) {
473-
static const Hash exception_hashes[] = {
474-
1142003074, 1346345301, 984971860, 1581723060, 675810883,
475-
2000609318, 603406554, 1802197452, 366528582, 351017945,
476-
1985385347, 650622313, 489249939, 1035868745, 799477063,
477-
823649752, 247530422, 838371290, 1538545476, 1777242020,
478-
2134842892, 1950608113, 742300048, 1836878090, 746628451,
479-
1016945095, 282021160, 196451025, 1431286868, 173529092,
480-
1299431183, 1326281044, 1956727606,
481-
};
482-
static const uint8_t exception_types[] = {
483-
PIKA_EXCEPTION_MODULE_NOT_FOUND,
484-
PIKA_EXCEPTION_UNBOUND_LOCAL,
485-
PIKA_EXCEPTION_ZERO_DIVISION,
486-
PIKA_EXCEPTION_NOT_IMPLEMENTED,
487-
PIKA_EXCEPTION_KEYBOARD_INTERRUPT,
488-
PIKA_EXCEPTION_GENERATOR_EXIT,
489-
PIKA_EXCEPTION_STOP_ITERATION,
490-
PIKA_EXCEPTION_INDENTATION,
491-
PIKA_EXCEPTION_TAB,
492-
PIKA_EXCEPTION_ARITHMETIC,
493-
PIKA_EXCEPTION_OVERFLOW,
494-
PIKA_EXCEPTION_RECURSION,
495-
PIKA_EXCEPTION_RUNTIME,
496-
PIKA_EXCEPTION_LOOKUP,
497-
PIKA_EXCEPTION_INDEX,
498-
PIKA_EXCEPTION_KEY,
499-
PIKA_EXCEPTION_UNICODE,
500-
PIKA_EXCEPTION_UNICODE,
501-
PIKA_EXCEPTION_UNICODE,
502-
PIKA_EXCEPTION_UNICODE,
503-
PIKA_EXCEPTION_VALUE,
504-
PIKA_EXCEPTION_TYPE,
505-
PIKA_EXCEPTION_NAME,
506-
PIKA_EXCEPTION_IMPORT,
507-
PIKA_EXCEPTION_ATTRIBUTE,
508-
PIKA_EXCEPTION_ASSERTION,
509-
PIKA_EXCEPTION_MEMORY,
510-
PIKA_EXCEPTION_OS,
511-
PIKA_EXCEPTION_SYSTEM,
512-
PIKA_EXCEPTION_SYSTEM_EXIT,
513-
PIKA_EXCEPTION_BASE,
514-
PIKA_EXCEPTION_EXCEPTION,
515-
PIKA_EXCEPTION_SYNTAX,
516-
};
517523
if (NULL == name) {
518524
return PIKA_EXCEPTION_NONE;
519525
}
@@ -547,7 +553,7 @@ void PikaVMFrame_setSysOut(PikaVMFrame* vm, char* fmt, ...) {
547553
if (vm->error.code == PIKA_RES_OK) {
548554
vm->error.code = PIKA_RES_ERR_RUNTIME_ERROR;
549555
}
550-
if (vm->vm_thread->try_state == TRY_STATE_INNER) {
556+
if (vm->vm_thread->try_state != TRY_STATE_NONE) {
551557
return;
552558
}
553559
va_list args;
@@ -1064,7 +1070,7 @@ static Arg* VM_instruction_handler_TRY(PikaObj* self,
10641070
char* data,
10651071
Arg* arg_ret_reg) {
10661072
pika_assert(NULL != vm->vm_thread);
1067-
vm->vm_thread->try_state = TRY_STATE_INNER;
1073+
vm->vm_thread->try_state++;
10681074
return NULL;
10691075
}
10701076

@@ -1112,6 +1118,28 @@ PIKA_EXCEPTION_OPTIMIZE_SIZE static pika_bool _exceptionTypeIsMatch(
11121118
}
11131119
return pika_false;
11141120
}
1121+
1122+
static char* _exceptionTypeNameInScope(PikaObj* scope,
1123+
PikaExceptionType type) {
1124+
Hash exception_hash = 0;
1125+
for (size_t i = 0; i < sizeof(exception_types); i++) {
1126+
if (exception_types[i] == type) {
1127+
exception_hash = exception_hashes[i];
1128+
break;
1129+
}
1130+
}
1131+
NativeProperty* prop = obj_getProp(scope);
1132+
while (NULL != prop) {
1133+
for (int i = 0; i < (int)prop->methodGroupCount; i++) {
1134+
Arg* method = (Arg*)(prop->methodGroup + i);
1135+
if (method->name_hash == exception_hash) {
1136+
return methodArg_getProp(method)->name;
1137+
}
1138+
}
1139+
prop = (NativeProperty*)prop->super;
1140+
}
1141+
return NULL;
1142+
}
11151143
#endif
11161144

11171145
PIKA_EXCEPTION_OPTIMIZE_SIZE static Arg* VM_instruction_handler_EXP(
@@ -1146,6 +1174,22 @@ PIKA_EXCEPTION_OPTIMIZE_SIZE static Arg* VM_instruction_handler_EXP(
11461174
}
11471175
}
11481176
if (is_match) {
1177+
if (PIKA_INS(OUT) ==
1178+
PikaVMFrame_getInstructWithOffset(
1179+
vm, 2 * (int)instructUnit_getSize())) {
1180+
PikaObj* builtins = obj_getBuiltins();
1181+
char* exception_name = _exceptionTypeNameInScope(
1182+
builtins, error->exception_type);
1183+
obj_deinit(builtins);
1184+
if (NULL == exception_name) {
1185+
exception_name = data;
1186+
}
1187+
Arg* exception_arg = VM_instruction_handler_RUN(
1188+
self, vm, exception_name, arg_ret_reg);
1189+
if (NULL != exception_arg) {
1190+
stack_pushArg(&vm->stack, exception_arg);
1191+
}
1192+
}
11491193
pikaVMThread_clearExceptionStack(vm->vm_thread);
11501194
}
11511195
return arg_setBool(arg_ret_reg, "", is_match);
@@ -1156,7 +1200,9 @@ static Arg* VM_instruction_handler_NTR(PikaObj* self,
11561200
PikaVMFrame* vm,
11571201
char* data,
11581202
Arg* arg_ret_reg) {
1159-
vm->vm_thread->try_state = TRY_STATE_NONE;
1203+
if (vm->vm_thread->try_state != TRY_STATE_NONE) {
1204+
vm->vm_thread->try_state--;
1205+
}
11601206
pikaVMThread_convertExceptionStack(vm->vm_thread);
11611207
return NULL;
11621208
}
@@ -4315,7 +4361,7 @@ static int pikaVM_runInstructUnit(PikaObj* self,
43154361
if (pikaVMFrame_checkErrorStack(vm) != PIKA_RES_OK ||
43164362
VMSignal_getCtrl() == VM_SIGNAL_CTRL_EXIT) {
43174363
/* raise jmp */
4318-
if (vm->vm_thread->try_state == TRY_STATE_INNER) {
4364+
if (vm->vm_thread->try_state != TRY_STATE_NONE) {
43194365
vm->jmp = VM_JMP_RAISE;
43204366
} else {
43214367
/* exit */

0 commit comments

Comments
 (0)