MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add try, catch, finally

+406 -2
+1 -1
meson.build
··· 41 41 build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() 42 42 43 43 version_conf = configuration_data() 44 - version_conf.set('ANT_VERSION', '0.0.5.43') 44 + version_conf.set('ANT_VERSION', '0.0.5.44') 45 45 version_conf.set('ANT_GIT_HASH', git_hash) 46 46 version_conf.set('ANT_BUILD_DATE', build_date) 47 47
+254 -1
src/ant.c
··· 180 180 static jsval_t js_while(struct js *js); 181 181 static jsval_t js_do_while(struct js *js); 182 182 static jsval_t js_block_or_stmt(struct js *js); 183 + static jsval_t js_try(struct js *js); 183 184 static jsval_t do_op(struct js *, uint8_t op, jsval_t l, jsval_t r); 184 185 static jsval_t do_instanceof(struct js *js, jsval_t l, jsval_t r); 185 186 static jsval_t do_in(struct js *js, jsval_t l, jsval_t r); ··· 3491 3492 return res; 3492 3493 } 3493 3494 3495 + static jsval_t js_try(struct js *js) { 3496 + uint8_t flags = js->flags, exe = !(flags & F_NOEXEC); 3497 + jsval_t res = js_mkundef(); 3498 + jsval_t try_result = js_mkundef(); 3499 + jsval_t catch_result = js_mkundef(); 3500 + jsval_t finally_result = js_mkundef(); 3501 + 3502 + bool had_exception = false; 3503 + char saved_errmsg[256] = {0}; 3504 + jsval_t exception_value = js_mkundef(); 3505 + 3506 + js->consumed = 1; 3507 + 3508 + if (next(js) != TOK_LBRACE) { 3509 + return js_mkerr(js, "{ expected after try"); 3510 + } 3511 + 3512 + jsoff_t try_start = js->pos; 3513 + js->flags |= F_NOEXEC; 3514 + js->consumed = 1; 3515 + 3516 + while (next(js) != TOK_EOF && next(js) != TOK_RBRACE) { 3517 + jsval_t v = js_stmt(js); 3518 + if (is_err(v)) break; 3519 + } 3520 + if (next(js) == TOK_RBRACE) js->consumed = 1; 3521 + jsoff_t try_end = js->pos; 3522 + 3523 + bool has_catch = false; 3524 + bool has_finally = false; 3525 + jsoff_t catch_start = 0, catch_end = 0; 3526 + jsoff_t finally_start = 0, finally_end = 0; 3527 + jsoff_t catch_param_off = 0, catch_param_len = 0; 3528 + 3529 + if (lookahead(js) == TOK_CATCH) { 3530 + has_catch = true; 3531 + js->consumed = 1; 3532 + next(js); 3533 + js->consumed = 1; 3534 + 3535 + if (next(js) == TOK_LPAREN) { 3536 + js->consumed = 1; 3537 + if (next(js) == TOK_IDENTIFIER) { 3538 + catch_param_off = js->toff; 3539 + catch_param_len = js->tlen; 3540 + js->consumed = 1; 3541 + } 3542 + if (next(js) != TOK_RPAREN) { 3543 + return js_mkerr(js, ") expected in catch"); 3544 + } 3545 + js->consumed = 1; 3546 + } 3547 + 3548 + if (next(js) != TOK_LBRACE) { 3549 + return js_mkerr(js, "{ expected after catch"); 3550 + } 3551 + 3552 + catch_start = js->pos; 3553 + js->consumed = 1; 3554 + 3555 + while (next(js) != TOK_EOF && next(js) != TOK_RBRACE) { 3556 + jsval_t v = js_stmt(js); 3557 + if (is_err(v)) break; 3558 + } 3559 + if (next(js) == TOK_RBRACE) js->consumed = 1; 3560 + catch_end = js->pos; 3561 + } 3562 + 3563 + if (lookahead(js) == TOK_FINALLY) { 3564 + has_finally = true; 3565 + js->consumed = 1; 3566 + next(js); 3567 + js->consumed = 1; 3568 + 3569 + if (next(js) != TOK_LBRACE) { 3570 + return js_mkerr(js, "{ expected after finally"); 3571 + } 3572 + 3573 + finally_start = js->pos; 3574 + js->consumed = 1; 3575 + 3576 + while (next(js) != TOK_EOF && next(js) != TOK_RBRACE) { 3577 + jsval_t v = js_stmt(js); 3578 + if (is_err(v)) break; 3579 + } 3580 + if (next(js) == TOK_RBRACE) js->consumed = 1; 3581 + finally_end = js->pos; 3582 + } 3583 + 3584 + if (!has_catch && !has_finally) { 3585 + return js_mkerr(js, "try requires catch or finally"); 3586 + } 3587 + 3588 + jsoff_t end_pos = has_finally ? finally_end : (has_catch ? catch_end : try_end); 3589 + 3590 + if (exe) { 3591 + bool try_returned = false; 3592 + jsval_t try_return_value = js_mkundef(); 3593 + 3594 + js->flags = flags & (uint8_t)~F_NOEXEC; 3595 + js->pos = try_start; 3596 + js->consumed = 1; 3597 + 3598 + while (next(js) != TOK_EOF && next(js) != TOK_RBRACE && !(js->flags & (F_RETURN | F_THROW | F_BREAK))) { 3599 + try_result = js_stmt(js); 3600 + if (is_err(try_result)) { 3601 + had_exception = true; 3602 + break; 3603 + } 3604 + } 3605 + 3606 + if (js->flags & F_RETURN) { 3607 + try_returned = true; 3608 + try_return_value = try_result; 3609 + js->flags &= (uint8_t)~(F_RETURN | F_NOEXEC); 3610 + } 3611 + 3612 + if (js->flags & F_THROW) { 3613 + had_exception = true; 3614 + js->flags &= (uint8_t)~F_THROW; 3615 + strncpy(saved_errmsg, js->errmsg, sizeof(saved_errmsg) - 1); 3616 + saved_errmsg[sizeof(saved_errmsg) - 1] = '\0'; 3617 + 3618 + jsval_t err_obj = mkobj(js, 0); 3619 + jsval_t msg_key = js_mkstr(js, "message", 7); 3620 + jsval_t name_key = js_mkstr(js, "name", 4); 3621 + 3622 + char *colon = strchr(saved_errmsg, ':'); 3623 + if (colon && strncmp(saved_errmsg, "Uncaught ", 9) == 0) { 3624 + char *type_start = saved_errmsg + 9; 3625 + size_t type_len = colon - type_start; 3626 + char *msg_start = colon + 2; 3627 + char *newline = strchr(msg_start, '\n'); 3628 + size_t msg_len = newline ? (size_t)(newline - msg_start) : strlen(msg_start); 3629 + 3630 + jsval_t name_val = js_mkstr(js, type_start, type_len); 3631 + jsval_t msg_val = js_mkstr(js, msg_start, msg_len); 3632 + setprop(js, err_obj, name_key, name_val); 3633 + setprop(js, err_obj, msg_key, msg_val); 3634 + } else { 3635 + jsval_t msg_val = js_mkstr(js, saved_errmsg, strlen(saved_errmsg)); 3636 + jsval_t name_val = js_mkstr(js, "Error", 5); 3637 + setprop(js, err_obj, name_key, name_val); 3638 + setprop(js, err_obj, msg_key, msg_val); 3639 + } 3640 + 3641 + exception_value = err_obj; 3642 + js->errmsg[0] = '\0'; 3643 + } 3644 + 3645 + if (next(js) == TOK_RBRACE) js->consumed = 1; 3646 + 3647 + bool exception_handled = false; 3648 + bool catch_returned = false; 3649 + jsval_t catch_return_value = js_mkundef(); 3650 + 3651 + if (had_exception && has_catch) { 3652 + exception_handled = true; 3653 + mkscope(js); 3654 + 3655 + if (catch_param_len > 0) { 3656 + jsval_t key = js_mkstr(js, &js->code[catch_param_off], catch_param_len); 3657 + mkprop(js, js->scope, key, exception_value, false); 3658 + } 3659 + 3660 + js->flags = flags & (uint8_t)~F_NOEXEC; 3661 + js->pos = catch_start; 3662 + js->consumed = 1; 3663 + 3664 + while (next(js) != TOK_EOF && next(js) != TOK_RBRACE && !(js->flags & (F_RETURN | F_THROW | F_BREAK))) { 3665 + catch_result = js_stmt(js); 3666 + if (is_err(catch_result)) break; 3667 + } 3668 + 3669 + if (js->flags & F_RETURN) { 3670 + catch_returned = true; 3671 + catch_return_value = catch_result; 3672 + js->flags &= (uint8_t)~(F_RETURN | F_NOEXEC); 3673 + } 3674 + 3675 + if (next(js) == TOK_RBRACE) js->consumed = 1; 3676 + delscope(js); 3677 + 3678 + if (js->flags & F_THROW) { 3679 + exception_handled = false; 3680 + strncpy(saved_errmsg, js->errmsg, sizeof(saved_errmsg) - 1); 3681 + saved_errmsg[sizeof(saved_errmsg) - 1] = '\0'; 3682 + } else { 3683 + res = catch_result; 3684 + } 3685 + } 3686 + 3687 + if (has_finally) { 3688 + uint8_t pre_finally_flags = js->flags; 3689 + bool had_pre_finally_exception = (js->flags & F_THROW) != 0; 3690 + char pre_finally_errmsg[256] = {0}; 3691 + if (had_pre_finally_exception) { 3692 + strncpy(pre_finally_errmsg, js->errmsg, sizeof(pre_finally_errmsg) - 1); 3693 + js->flags &= (uint8_t)~F_THROW; 3694 + js->errmsg[0] = '\0'; 3695 + } 3696 + 3697 + js->flags = flags & (uint8_t)~F_NOEXEC; 3698 + js->pos = finally_start; 3699 + js->consumed = 1; 3700 + 3701 + while (next(js) != TOK_EOF && next(js) != TOK_RBRACE && !(js->flags & (F_RETURN | F_THROW | F_BREAK))) { 3702 + finally_result = js_stmt(js); 3703 + if (is_err(finally_result)) break; 3704 + } 3705 + 3706 + if (next(js) == TOK_RBRACE) js->consumed = 1; 3707 + 3708 + if (!(js->flags & (F_RETURN | F_THROW))) { 3709 + if (had_pre_finally_exception) { 3710 + js->flags = pre_finally_flags; 3711 + strncpy(js->errmsg, pre_finally_errmsg, sizeof(js->errmsg) - 1); 3712 + } else if (had_exception && !exception_handled) { 3713 + js->flags |= F_THROW; 3714 + strncpy(js->errmsg, saved_errmsg, sizeof(js->errmsg) - 1); 3715 + } else if (catch_returned) { 3716 + js->flags |= F_RETURN; 3717 + res = catch_return_value; 3718 + } else if (try_returned) { 3719 + js->flags |= F_RETURN; 3720 + res = try_return_value; 3721 + } 3722 + } 3723 + } else if (had_exception && !exception_handled) { 3724 + js->flags |= F_THROW; 3725 + strncpy(js->errmsg, saved_errmsg, sizeof(js->errmsg) - 1); 3726 + res = mkval(T_ERR, 0); 3727 + } else if (catch_returned) { 3728 + js->flags |= F_RETURN; 3729 + res = catch_return_value; 3730 + } else if (try_returned) { 3731 + js->flags |= F_RETURN; 3732 + res = try_return_value; 3733 + } 3734 + 3735 + if (!had_exception && !try_returned && !(js->flags & (F_RETURN | F_THROW))) { 3736 + res = try_result; 3737 + } 3738 + } 3739 + 3740 + js->pos = end_pos; 3741 + js->tok = TOK_SEMICOLON; 3742 + js->consumed = 0; 3743 + 3744 + return res; 3745 + } 3746 + 3494 3747 static jsval_t js_break(struct js *js) { 3495 3748 if (js->flags & F_NOEXEC) { 3496 3749 } else { ··· 3719 3972 case TOK_CASE: case TOK_CATCH: 3720 3973 case TOK_DEFAULT: case TOK_FINALLY: 3721 3974 case TOK_SWITCH: 3722 - case TOK_TRY: 3723 3975 case TOK_WITH: case TOK_YIELD: 3724 3976 res = js_mkerr(js, "'%.*s' not implemented", (int) js->tlen, js->code + js->toff); 3725 3977 break; ··· 3758 4010 case TOK_LBRACE: res = js_block(js, !(js->flags & F_NOEXEC)); break; 3759 4011 case TOK_FOR: res = js_for(js); break; 3760 4012 case TOK_RETURN: res = js_return(js); break; 4013 + case TOK_TRY: res = js_try(js); break; 3761 4014 default: res = resolveprop(js, js_expr(js)); break; 3762 4015 } 3763 4016
+151
tests/test_try_catch.cjs
··· 1 + // Test 1: Basic try-catch 2 + console.log("Test 1: Basic try-catch"); 3 + let result1 = "not caught"; 4 + try { 5 + throw new Error("test error"); 6 + result1 = "should not reach"; 7 + } catch (e) { 8 + result1 = "caught: " + e.message; 9 + } 10 + console.log(result1); 11 + 12 + // Test 2: Try without exception 13 + console.log("\nTest 2: Try without exception"); 14 + let result2 = "initial"; 15 + try { 16 + result2 = "no error"; 17 + } catch (e) { 18 + result2 = "caught"; 19 + } 20 + console.log(result2); 21 + 22 + // Test 3: Try-catch-finally 23 + console.log("\nTest 3: Try-catch-finally"); 24 + let result3 = []; 25 + try { 26 + result3.push("try"); 27 + throw new Error("error"); 28 + } catch (e) { 29 + result3.push("catch"); 30 + } finally { 31 + result3.push("finally"); 32 + } 33 + console.log(result3.join(", ")); 34 + 35 + // Test 4: Finally without catch 36 + console.log("\nTest 4: Try-finally (no exception)"); 37 + let result4 = []; 38 + try { 39 + result4.push("try"); 40 + } finally { 41 + result4.push("finally"); 42 + } 43 + console.log(result4.join(", ")); 44 + 45 + // Test 5: Catch without parentheses (optional binding) 46 + console.log("\nTest 5: Catch without binding"); 47 + let result5 = "not caught"; 48 + try { 49 + throw "error string"; 50 + } catch { 51 + result5 = "caught without binding"; 52 + } 53 + console.log(result5); 54 + 55 + // Test 6: Nested try-catch 56 + console.log("\nTest 6: Nested try-catch"); 57 + let result6 = []; 58 + try { 59 + result6.push("outer try"); 60 + try { 61 + result6.push("inner try"); 62 + throw new Error("inner error"); 63 + } catch (e) { 64 + result6.push("inner catch: " + e.message); 65 + } 66 + result6.push("after inner"); 67 + } catch (e) { 68 + result6.push("outer catch"); 69 + } 70 + console.log(result6.join(", ")); 71 + 72 + // Test 7: Rethrow in catch 73 + console.log("\nTest 7: Rethrow in catch"); 74 + let result7 = []; 75 + try { 76 + try { 77 + throw new Error("original"); 78 + } catch (e) { 79 + result7.push("first catch: " + e.message); 80 + throw new Error("rethrown"); 81 + } 82 + } catch (e) { 83 + result7.push("second catch: " + e.message); 84 + } 85 + console.log(result7.join(", ")); 86 + 87 + // Test 8: Finally runs even with return 88 + console.log("\nTest 8: Finally with return"); 89 + function testFinallyReturn() { 90 + let x = []; 91 + try { 92 + x.push("try"); 93 + return x; 94 + } finally { 95 + x.push("finally"); 96 + } 97 + } 98 + console.log(testFinallyReturn().join(", ")); 99 + 100 + // Test 9: Access error properties 101 + console.log("\nTest 9: Error properties"); 102 + try { 103 + throw new Error("test message"); 104 + } catch (e) { 105 + console.log("name: " + e.name); 106 + console.log("message: " + e.message); 107 + } 108 + 109 + // Test 10: Custom error class 110 + console.log("\nTest 10: Custom error class"); 111 + class CustomError extends Error { 112 + constructor(msg) { 113 + super(msg); 114 + this.name = "CustomError"; 115 + } 116 + } 117 + 118 + try { 119 + throw new CustomError("custom message"); 120 + } catch (e) { 121 + console.log("Caught " + e.name + ": " + e.message); 122 + } 123 + 124 + // Test 11: Finally modifies value 125 + console.log("\nTest 11: Finally executes after catch"); 126 + let counter = 0; 127 + try { 128 + counter = 1; 129 + throw new Error("test"); 130 + } catch (e) { 131 + counter = 2; 132 + } finally { 133 + counter = counter + 10; 134 + } 135 + console.log("counter: " + counter); 136 + 137 + // Test 12: Exception in finally propagates 138 + console.log("\nTest 12: Exception in try, no catch"); 139 + let result12 = []; 140 + try { 141 + try { 142 + throw new Error("original error"); 143 + } finally { 144 + result12.push("finally ran"); 145 + } 146 + } catch (e) { 147 + result12.push("outer caught: " + e.message); 148 + } 149 + console.log(result12.join(", ")); 150 + 151 + console.log("\nAll tests completed!");