Skip to content

Commit

Permalink
Fix bug in nested compile call error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
goodpaul6 committed Jun 10, 2024
1 parent 5d9e474 commit 52a813e
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 56 deletions.
53 changes: 24 additions & 29 deletions examples/server/src/loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,63 +86,58 @@ static TINY_FOREIGN_FUNCTION(CallWaitUnsafe) {
}

static TINY_MACRO_FUNCTION(CallWaitMacro) {
if(nargs != 1) {
return (Tiny_MacroResult){
.type = TINY_MACRO_ERROR,
.errorMessage = "'call_wait' macro expects exactly 1 argument."
};
if (nargs != 1) {
return (Tiny_MacroResult){.type = TINY_MACRO_ERROR,
.error.msg = "'call_wait' macro expects exactly 1 argument."};
}

if(!asName) {
return (Tiny_MacroResult){
.type = TINY_MACRO_ERROR,
.errorMessage = "'call_wait' macro requires an 'as' name."
};
if (!asName) {
return (Tiny_MacroResult){.type = TINY_MACRO_ERROR,
.error.msg = "'call_wait' macro requires an 'as' name."};
}

const Tiny_Symbol* func = Tiny_FindFuncSymbol(state, args[0]);

if(!func) {
if (!func) {
return (Tiny_MacroResult){
.type = TINY_MACRO_ERROR,
.errorMessage = "Function supplied to 'call_wait' macro does not exist."
};
.error.msg = "Function supplied to 'call_wait' macro does not exist."};
}

char sigbuf[1024] = { 0 };
char sigbuf[1024] = {0};
int sigused = 0;

sigused += snprintf(sigbuf, sizeof(sigbuf), "func %s(", asName);
char argsbuf[512] = { 0 };

char argsbuf[512] = {0};
int argsused = 0;

for (int i = 0; i < Tiny_SymbolArrayCount(func->func.args); ++i) {
const Tiny_Symbol* arg = func->func.args[i];

if(i > 0) {
if (i > 0) {
sigused += snprintf(sigbuf + sigused, sizeof(sigbuf) - sigused, ", ");
argsused += snprintf(argsbuf + argsused, sizeof(argsbuf) - argsused, ", ");
}

sigused += snprintf(sigbuf + sigused, sizeof(sigbuf) - sigused,
"a%i: %s", i, arg->var.tag->name);
sigused +=
snprintf(sigbuf + sigused, sizeof(sigbuf) - sigused, "a%i: %s", i, arg->var.tag->name);

argsused += snprintf(argsbuf + argsused, sizeof(argsbuf) - argsused,
"a%i", i);
argsused += snprintf(argsbuf + argsused, sizeof(argsbuf) - argsused, "a%i", i);
}

if(func->func.returnTag->type != TINY_SYM_TAG_VOID) {
sigused += snprintf(sigbuf + sigused, sizeof(sigbuf) - sigused, "): %s { return call_wait_unsafe(\"%s\", %s) }",
func->func.returnTag->name, args[0], argsbuf);
if (func->func.returnTag->type != TINY_SYM_TAG_VOID) {
sigused += snprintf(sigbuf + sigused, sizeof(sigbuf) - sigused,
"): %s { return call_wait_unsafe(\"%s\", %s) }",
func->func.returnTag->name, args[0], argsbuf);
} else {
sigused += snprintf(sigbuf + sigused, sizeof(sigbuf) - sigused, ") { call_wait_unsafe(\"%s\", %s) }",
args[0], argsbuf);
sigused += snprintf(sigbuf + sigused, sizeof(sigbuf) - sigused,
") { call_wait_unsafe(\"%s\", %s) }", args[0], argsbuf);
}

Tiny_CompileString(state, "(call_wait macro code)", sigbuf);

return (Tiny_MacroResult){ .type = TINY_MACRO_SUCCESS };
return (Tiny_MacroResult){.type = TINY_MACRO_SUCCESS};
}

static TINY_FOREIGN_FUNCTION(Sends) {
Expand Down Expand Up @@ -200,8 +195,8 @@ static TINY_FOREIGN_FUNCTION(Sendf) {

Tiny_GetExecutingFileLine(thread, &fileName, &line);

fprintf(stderr, "%s(%i): Invalid format specifier '%%%c' in sendf.\n",
fileName, line, *s);
fprintf(stderr, "%s(%i): Invalid format specifier '%%%c' in sendf.\n", fileName,
line, *s);
} break;
}

Expand Down
24 changes: 19 additions & 5 deletions examples/terp/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ static Tiny_MacroResult ImportModuleFunction(Tiny_State* state, char* const* arg
if (nargs != 1) {
return (Tiny_MacroResult){
.type = TINY_MACRO_ERROR,
.errorMessage = "Expected exactly 1 argument to 'import'",
.error.msg = "Expected exactly 1 argument to 'import'",
};
}

Expand All @@ -20,9 +20,18 @@ static Tiny_MacroResult ImportModuleFunction(Tiny_State* state, char* const* arg
return (Tiny_MacroResult){.type = TINY_MACRO_SUCCESS};
}

Tiny_RegisterType(state, buf);
Tiny_CompileResult compileResult = Tiny_CompileFile(state, args[0]);

if (compileResult.type != TINY_COMPILE_SUCCESS) {
Tiny_MacroResult macroResult = {.type = TINY_MACRO_ERROR};

snprintf(macroResult.error.msg, sizeof(macroResult.error.msg),
"Failed to compile imported file: %s", compileResult.error.msg);

return macroResult;
}

Tiny_CompileFile(state, args[0]);
Tiny_RegisterType(state, buf);

return (Tiny_MacroResult){.type = TINY_MACRO_SUCCESS};
}
Expand Down Expand Up @@ -51,7 +60,12 @@ int main(int argc, char** argv) {

Tiny_BindMacro(state, "import", ImportModuleFunction);

Tiny_CompileFile(state, argv[1]);
Tiny_CompileResult compileResult = Tiny_CompileFile(state, argv[1]);

if (compileResult.type != TINY_COMPILE_SUCCESS) {
fprintf(stderr, "%s\n", compileResult.error.msg);
return 1;
}

if (dis) {
char buf[1024];
Expand Down Expand Up @@ -81,4 +95,4 @@ int main(int argc, char** argv) {
Tiny_DeleteState(state);

return 0;
}
}
13 changes: 12 additions & 1 deletion examples/terp/src/point.tiny
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,15 @@ struct Point {
y: int
}

use json("Point")
p := new Point{10, 20}

struct PointPoints {
x: Point
y: Point
}

pp := new PointPoints{p, p}

use json("PointPoints")

printf("%q\n", PointPoints_to_json(pp))
36 changes: 36 additions & 0 deletions test/src/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,41 @@ static void test_DisasmOne() {
Tiny_DeleteState(state);
}

static TINY_MACRO_FUNCTION(CompileStringMacro) {
if (nargs != 1) {
return (Tiny_MacroResult){
.type = TINY_MACRO_ERROR,
.error.msg = "compile_string requires one arg",
};
}

Tiny_CompileResult compileResult =
Tiny_CompileString(state, "(compile_string macro code)", args[0]);

if (compileResult.type != TINY_COMPILE_SUCCESS) {
Tiny_MacroResult mr = {.type = TINY_MACRO_ERROR};

snprintf(mr.error.msg, sizeof(mr.error.msg), "%s", compileResult.error.msg);
return mr;
}

return (Tiny_MacroResult){.type = TINY_MACRO_SUCCESS};
}

static void test_NestCompileFailPropagates() {
Tiny_State *state = CreateState();

Tiny_BindMacro(state, "compile_string", CompileStringMacro);

const char *code = "use compile_string(\"x: int = null\")";

Tiny_CompileResult result = Tiny_CompileString(state, "(macro fail)", code);

lequal_return(result.type, TINY_COMPILE_ERROR);

Tiny_DeleteState(state);
}

static void test_GetStringConst() {
Tiny_State *state = CreateState();

Expand Down Expand Up @@ -1050,6 +1085,7 @@ int main(int argc, char *argv[]) {
lrun("Tiny Can't Assign Nullable to Non-Nullable", test_CantAssignNullableToNonNullable);
lrun("Tiny Test DisasmOne", test_DisasmOne);
lrun("Tiny Test GetStringConstant", test_GetStringConst);
lrun("Tiny Nested Compile Error Propagates", test_NestCompileFailPropagates);

lrun("Check no leak in tests", test_CheckMallocs);

Expand Down
5 changes: 3 additions & 2 deletions tiny/include/detail.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,9 @@ typedef struct Tiny_State {
Tiny_Arena parserArena;

// If there's an error while compiling a string or a file, we reset
// back to this jump buffer
jmp_buf compileErrorJmpBuf;
// back to this jump buffer.
jmp_buf compileErrorJmpBufs[TINY_MAX_NESTED_COMPILE_CALLS];
int compileCallNestCount;

// In case there was a compile error, it gets put in
// here.
Expand Down
19 changes: 18 additions & 1 deletion tiny/include/tiny.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@
#define TINY_MAX_COMPILE_ERR_MSG_SZ 1024
#endif

#ifndef TINY_MAX_MACRO_RESULT_ERR_MSG_SZ
#define TINY_MAX_MACRO_RESULT_ERR_MSG_SZ 1024
#endif

// I use setjmp/longjmp internally to handle compile errors.
// This means I have to store an array of jmp_buf to handle
// errors in nested compile calls without clobbering previous
// ones. This is because you can't safely copy jmp_buf.
//
// Anyways, here's the nesting limit.
#ifndef TINY_MAX_NESTED_COMPILE_CALLS
#define TINY_MAX_NESTED_COMPILE_CALLS 16
#endif

// This function should be able to handle all of `malloc`,
// `realloc`, and `free`:
//
Expand Down Expand Up @@ -396,7 +410,10 @@ typedef enum Tiny_MacroResultType {

typedef struct Tiny_MacroResult {
Tiny_MacroResultType type;
const char *errorMessage;

struct {
char msg[TINY_MAX_MACRO_RESULT_ERR_MSG_SZ];
} error;
} Tiny_MacroResult;

// This function is called for each instance of the `use` statement in Tiny code.
Expand Down
25 changes: 17 additions & 8 deletions tiny/src/std.c
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,16 @@ static Tiny_MacroResult BindJsonSerializerForType(Tiny_State *state, const Tiny_

used += snprintf(buf + used, sizeof(buf) - used, ",\n\t\"}\")\n}");

Tiny_CompileString(state, "(json mod)", buf);
Tiny_CompileResult compileResult = Tiny_CompileString(state, "(json mod)", buf);

if (compileResult.type != TINY_COMPILE_SUCCESS) {
Tiny_MacroResult macroResult = {.type = TINY_MACRO_ERROR};

snprintf(macroResult.error.msg, sizeof(macroResult.error.msg),
"Failed to compile JSON code: %s", compileResult.error.msg);

return macroResult;
}

return (Tiny_MacroResult){.type = TINY_MACRO_SUCCESS};
}
Expand All @@ -744,7 +753,7 @@ static TINY_MACRO_FUNCTION(JsonMacroFunction) {
if (nargs != 1) {
return (Tiny_MacroResult){
.type = TINY_MACRO_ERROR,
.errorMessage = "Must specify exactly 1 argument to 'use json'",
.error.msg = "Must specify exactly 1 argument to 'use json'",
};
}

Expand All @@ -753,7 +762,7 @@ static TINY_MACRO_FUNCTION(JsonMacroFunction) {
if (sym->type != TINY_SYM_TAG_STRUCT) {
return (Tiny_MacroResult){
.type = TINY_MACRO_ERROR,
.errorMessage = "Must specify struct type as argument to 'use json_mod'",
.error.msg = "Must specify struct type as argument to 'use json_mod'",
};
}

Expand All @@ -764,21 +773,21 @@ static TINY_MACRO_FUNCTION(ArrayMacroFunction) {
if (nargs != 1) {
return (Tiny_MacroResult){
.type = TINY_MACRO_ERROR,
.errorMessage = "Must specify exactly 1 argument to 'use array'",
.error.msg = "Must specify exactly 1 argument to 'use array'",
};
}

if (!asName) {
return (Tiny_MacroResult){
.type = TINY_MACRO_ERROR,
.errorMessage = "Must specify an 'as' name when doing 'use array'",
.error.msg = "Must specify an 'as' name when doing 'use array'",
};
}

if (!Tiny_FindTypeSymbol(state, args[0])) {
return (Tiny_MacroResult){
.type = TINY_MACRO_ERROR,
.errorMessage = "The array element type you specified does not exist",
.error.msg = "The array element type you specified does not exist",
};
}

Expand Down Expand Up @@ -902,14 +911,14 @@ static TINY_MACRO_FUNCTION(DelegateMacroFunction) {
if (nargs != 1) {
return (Tiny_MacroResult){
.type = TINY_MACRO_ERROR,
.errorMessage = "Must specify exactly 1 argument to 'use delegate'",
.error.msg = "Must specify exactly 1 argument to 'use delegate'",
};
}

if (!asName) {
return (Tiny_MacroResult){
.type = TINY_MACRO_ERROR,
.errorMessage = "Must specify an 'as' name when doing 'use delegate'",
.error.msg = "Must specify an 'as' name when doing 'use delegate'",
};
}

Expand Down
Loading

0 comments on commit 52a813e

Please sign in to comment.