From 6f91a71306b487ce4da7b291b66d2ea2cc0872ef Mon Sep 17 00:00:00 2001 From: r4 Date: Wed, 29 Dec 2021 22:15:56 +0100 Subject: [PATCH] fix memory leak + add function to convert to string --- main.c | 55 +++++++++++++++++++++++++++++++++++++++++++++---------- parse.c | 14 ++++++++++++++ 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/main.c b/main.c index a8f1687..05098da 100644 --- a/main.c +++ b/main.c @@ -127,6 +127,40 @@ static Value fn_ptr(Value *args) { return (Value){ .type = TypePtr, .Ptr = { .type = TypeVoid, .val = NULL }}; } +static Value fn_string(Value *args) { + char *res = xmalloc(64); + size_t len; + + switch (args[0].type) { + case TypeVoid: strcpy(res, "(void)"); len = 6; break; + case TypeFloat: len = snprintf(res, 64, "%f", args[0].Float); break; + case TypeInt: len = snprintf(res, 64, "%zd", args[0].Int); break; + case TypeBool: + if (args[0].Bool) { + strcpy(res, "true"); + len = 4; + } else { + strcpy(res, "false"); + len = 5; + } + break; + case TypeChar: res[0] = args[0].Char; len = 1; break; + default: ASSERT_UNREACHED(); + } + + return (Value){ + .type = TypeArr, + .Arr = { + .is_string = true, + .dynamically_allocated = true, + .type = TypeChar, + .vals = res, + .len = len, + .cap = 64, + }, + }; +} + static Value fn_pow(Value *args) { if (!(args[0].type == TypeFloat && args[1].type == TypeFloat)) { set_err("pow() requires arguments of type float"); @@ -232,16 +266,17 @@ int main(int argc, const char **argv) { print_toks(&tokens); /* parse tokens into IR code */ BuiltinFunc funcs[] = { - { .name = "put", .kind = FuncVarArgs, .returns = false, .side_effects = true, .VarArgs = { .min_args = 0, .NoRet.func = fn_put, }}, - { .name = "putln", .kind = FuncVarArgs, .returns = false, .side_effects = true, .VarArgs = { .min_args = 0, .NoRet.func = fn_putln, }}, - { .name = "int", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_int, }}, - { .name = "float", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_float, }}, - { .name = "bool", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_bool, }}, - { .name = "char", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_char, }}, - { .name = "ptr", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 0, .WithRet.func = fn_ptr, }}, - { .name = "pow", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 2, .WithRet.func = fn_pow, }}, - { .name = "sleep", .kind = FuncFixedArgs, .returns = false, .side_effects = true, .FixedArgs = { .n_args = 1, .NoRet.func = fn_sleep, }}, - { .name = "getln", .kind = FuncFixedArgs, .returns = true, .side_effects = true, .FixedArgs = { .n_args = 0, .WithRet.func = fn_getln, }}, + { .name = "put", .kind = FuncVarArgs, .returns = false, .side_effects = true, .VarArgs = { .min_args = 0, .NoRet.func = fn_put, }}, + { .name = "putln", .kind = FuncVarArgs, .returns = false, .side_effects = true, .VarArgs = { .min_args = 0, .NoRet.func = fn_putln, }}, + { .name = "int", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_int, }}, + { .name = "float", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_float, }}, + { .name = "bool", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_bool, }}, + { .name = "char", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_char, }}, + { .name = "ptr", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 0, .WithRet.func = fn_ptr, }}, + { .name = "string", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 1, .WithRet.func = fn_string, }}, + { .name = "pow", .kind = FuncFixedArgs, .returns = true, .side_effects = false, .FixedArgs = { .n_args = 2, .WithRet.func = fn_pow, }}, + { .name = "sleep", .kind = FuncFixedArgs, .returns = false, .side_effects = true, .FixedArgs = { .n_args = 1, .NoRet.func = fn_sleep, }}, + { .name = "getln", .kind = FuncFixedArgs, .returns = true, .side_effects = true, .FixedArgs = { .n_args = 0, .WithRet.func = fn_getln, }}, }; IRList ir = parse(&tokens, funcs, sizeof(funcs) / sizeof(funcs[0])); if (err) { diff --git a/parse.c b/parse.c index ea1b918..870f493 100644 --- a/parse.c +++ b/parse.c @@ -31,6 +31,7 @@ static IRParam tok_to_irparam(Scope *sc, Tok *t); static Scope make_scope(Scope *parent, bool with_idents); static void term_scope(Scope *sc); static bool expr_flush_ir_and_maybe_return(IRList *out_ir, TokList *toks, IRTok instr, TokListItem *expr_start, Scope *expr_scope, TokListItem *t, ExprRet *out_ret); +static void make_value_statically_allocated(Value *v); static ExprRet expr(IRList *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t); static void expr_into_addr(IRList *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t, size_t addr); static IRParam expr_into_irparam(IRList *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, TokListItem *t); @@ -160,6 +161,13 @@ static bool expr_flush_ir_and_maybe_return(IRList *out_ir, TokList *toks, IRTok } } +static void make_value_statically_allocated(Value *v) { + switch (v->type) { + case TypeArr: v->Arr.dynamically_allocated = false; break; + default: break; + } +} + /* The job of this function is to reduce the expression to the most simple form * writing the least IR instructions possible (without overanalyzing). * This means that the only IR instructions it will be writing are those for @@ -355,6 +363,12 @@ static ExprRet expr(IRList *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, func.VarArgs.WithRet.func(args_len - func.VarArgs.min_args, arg_vals) : func.FixedArgs.WithRet.func(arg_vals), }; + /* since we have a literal return value, we want it to be fully treated like one by the memory manager */ + make_value_statically_allocated(&func_ident->tok.Val); + /* immediately free any heap-allocated literals that are no longer needed */ + for (size_t i = 0; i < args_len; i++) + free_value(&arg_vals[i], true); + /* free buffers */ if (arg_vals) free(arg_vals); if (args)