bmx-ng / bcc

A next-generation bcc parser for BlitzMax
zlib License
33 stars 13 forks source link

Try/Catch in release builds (code there but not throwing) #636

Closed GWRon closed 8 months ago

GWRon commented 8 months ago

Using Adam Novagen's code from there: https://www.syntaxbomb.com/blitzmax-blitzmax-ng/legacy-have-i-completely-misunderstood-exceptions/new/#new

SuperStrict

Local array:Int[] = [5,10,15,20] ' 4 elements

Try
    For Local i:Int = 0 To 7 ' Try to get 8 elements
        Print "[" + i + "] = " + array[i]
    Next
Catch err:Object
    Print err.ToString()
EndTry

The following code is created with NG...

in debug builds:

        #line 5 "/home/ronny/Arbeit/Tools/BlitzMaxNG/tmp/untitled1.bmx"
        {
            BBOBJECT ex;
            bbExTry {
                case 0: {
                    bbOnDebugPushExState();
                    struct BBDebugScope __scope = {
                        BBDEBUGSCOPE_LOCALBLOCK,
                        0,
                        {
                            {
                                BBDEBUGDECL_END
                            }
                        }
                    };
                    bbOnDebugEnterScope((BBDebugScope *)&__scope);
                    struct BBDebugStm __stmt_0 = {0x558ac721d846ce0d, 6, 0};
                    bbOnDebugEnterStm(&__stmt_0);
                    #line 6 "/home/ronny/Arbeit/Tools/BlitzMaxNG/tmp/untitled1.bmx"
                    {
                        BBINT bbt_i=0;
                        for(;(bbt_i<=7);bbt_i=(bbt_i+1)){
                            struct BBDebugScope_1 __scope = {
                                BBDEBUGSCOPE_LOCALBLOCK,
                                0,
                                {
                                    {
                                        BBDEBUGDECL_LOCAL,
                                        "i",
                                        "i",
                                        .var_address=&bbt_i
                                    },
                                    {
                                        BBDEBUGDECL_END
                                    }
                                }
                            };
                            bbOnDebugEnterScope((BBDebugScope *)&__scope);
                            struct BBDebugStm __stmt_0 = {0x558ac721d846ce0d, 7, 0};
                            bbOnDebugEnterStm(&__stmt_0);
                            #line 7 "/home/ronny/Arbeit/Tools/BlitzMaxNG/tmp/untitled1.bmx"
                            brl_standardio_Print(bbStringConcat(bbStringConcat(bbStringConcat(((BBString*)&_s11),bbStringFromInt(bbt_i)),((BBString*)&_s12)),bbStringFromInt(((BBINT*)BBARRAYDATAINDEX((bbt_array),(bbt_array)->dims,((BBUINT)bbt_i)))[((BBUINT)bbt_i)])));
                            bbOnDebugLeaveScope();
                        }
                    }
                    bbOnDebugLeaveScope();
                    bbExLeave();
                    bbOnDebugPopExState();
                }
                break;
                case 1: {
                    bbOnDebugPopExState();
                    ex = bbExCatch();
                    if (bbObjectDowncast((BBOBJECT)ex,(BBClass*)&bbObjectClass) != &bbNullObject) {
                        BBOBJECT bbt_err=(BBOBJECT)ex;
                        struct BBDebugScope_1 __scope = {
                            BBDEBUGSCOPE_LOCALBLOCK,
                            0,
                            {
                                {
                                    BBDEBUGDECL_LOCAL,
                                    "err",
                                    ":Object",
                                    .var_address=&bbt_err
                                },
                                {
                                    BBDEBUGDECL_END
                                }
                            }
                        };
                        bbOnDebugEnterScope((BBDebugScope *)&__scope);
                        struct BBDebugStm __stmt_0 = {0x558ac721d846ce0d, 10, 0};
                        bbOnDebugEnterStm(&__stmt_0);
                        #line 10 "/home/ronny/Arbeit/Tools/BlitzMaxNG/tmp/untitled1.bmx"
                        brl_standardio_Print(((BBOBJECT)bbNullObjectTest((BBObject*)bbt_err))->clas->ToString((BBOBJECT)bbt_err));
                        bbOnDebugLeaveScope();
                    } else {
                        goto _rethrow;
                    }
                    goto _endtry;
                }
                break;
                _rethrow:;
                bbExThrow(ex);
            }
        }
        _endtry:;

and in release builds:

        #line 5 "/home/ronny/Arbeit/Tools/BlitzMaxNG/tmp/untitled1.bmx"
        {
            BBOBJECT ex;
            bbExTry {
                case 0: {
                    #line 6 "/home/ronny/Arbeit/Tools/BlitzMaxNG/tmp/untitled1.bmx"
                    {
                        BBINT bbt_i=0;
                        for(;(bbt_i<=7);bbt_i=(bbt_i+1)){
                            #line 7 "/home/ronny/Arbeit/Tools/BlitzMaxNG/tmp/untitled1.bmx"
                            brl_standardio_Print(bbStringConcat(bbStringConcat(bbStringConcat(((BBString*)&_s11),bbStringFromInt(bbt_i)),((BBString*)&_s12)),bbStringFromInt(((BBINT*)BBARRAYDATA(bbt_array,1))[((BBUINT)bbt_i)])));
                        }
                    }
                    bbExLeave();
                }
                break;
                case 1: {
                    ex = bbExCatch();
                    if (bbObjectDowncast((BBOBJECT)ex,(BBClass*)&bbObjectClass) != &bbNullObject) {
                        BBOBJECT bbt_err=(BBOBJECT)ex;
                        #line 10 "/home/ronny/Arbeit/Tools/BlitzMaxNG/tmp/untitled1.bmx"
                        brl_standardio_Print((bbt_err)->clas->ToString((BBOBJECT)bbt_err));
                    } else {
                        goto _rethrow;
                    }
                    goto _endtry;
                }
                break;
                _rethrow:;
                bbExThrow(ex);
            }
        }
        _endtry:;

So both contain bbExTry and friends - but only the debug build actually throws the error. Release builds simply print

[0] = 5
[1] = 10
[2] = 15
[3] = 20
[4] = 0
[5] = 0
[6] = 0
[7] = 0

here (so do not catch the out-of-bounds-error). If I added a

Local o:object
o.ToString()

into the Try-Catch block, then it segfaults (so the null access is also not catched).

Expected behaviour: I guess it should be able to try/catch it ... or if this should only work in debug builds, then it should not emit the C code for try/catch stuff.

Kerntrick commented 8 months ago

I've always understood that release builds didn't do bounds checking.
No bounds check == no exception to catch. Am i misunderstanding the issue?

GWRon commented 8 months ago

Yes. But also null accesses are not catched.

What should be "catchable" in release builds then / for what could try/catch be used there?

woollybah commented 8 months ago

The OOB array access will not throw because there is no bounds checking in release mode. This has always been the case.

If you don't know at runtime whether you are likely to be trying to access outwith the bounds of an array, you should design your program to stay within those bounds. The same applies to attempting to use null objects.

GWRon commented 8 months ago

I still would like to know what the try catch ...is able to catch. Throw-new-exception-stuff only?

That an array access on its own does no boundary check and blindly tells what it is told to do... I kinda knew that but yeah, dunno what try/catch changes behind the scenes.

Feel free to reply to the syntaxbomb thread too.. so I do not have to relay the "solution" to Adam's question/issue.

HurryStarfish commented 8 months ago

I still would like to know what the try catch ...is able to catch.

Anything object that's thrown. But the brounds check that would throw the exception simply isn't included in release builds. The bbdoc comments on the exception types in blitz.mod/blitz.bmx mention this. TArrayBoundsException, TNullObjectException, TOutOfDataException and TInvalidEnumException are only thrown in debug mode to make it easier to find errors in your code. There is nothing special about those types themselves, if you want you can throw and catch them like any other. It's just not a good idea because that's not what they're for, they're supposed to never happen in a bug-free program. (That goes for the other exceptions in blitz.bmx too, e.g. don't catch a RuntimeError.)

GWRon commented 8 months ago

I linked that issue in the SB thread so I guess this helps Adam already.