From b4ba9e2a405cff363420dac1a8e01bcf63f07a61 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 3 Feb 2009 00:31:34 -0500 Subject: [PATCH] Allow multiple errors from parsing. --- assemble.c | 12 ++- finderrors.c | 4 +- mojoshader.c | 204 +++++++++++++++++++++++++++++------------ mojoshader.h | 30 +++++- mojoshader_assembler.c | 56 ++++++++--- mojoshader_opengl.c | 4 +- testoutput.c | 12 ++- testparse.c | 12 ++- 8 files changed, 247 insertions(+), 87 deletions(-) diff --git a/assemble.c b/assemble.c index 22f90ecc..e0f646d6 100644 --- a/assemble.c +++ b/assemble.c @@ -24,8 +24,16 @@ static int assemble(const char *buf, const char *outfile) int retval = 0; pd = MOJOSHADER_assemble(buf, NULL, 0, NULL, 0, NULL, NULL, NULL); - if (pd->error != NULL) - printf("ERROR: (line %d) %s\n", pd->error_position, pd->error); + if (pd->error_count > 0) + { + int i; + for (i = 0; i < pd->error_count; i++) + { + printf("ERROR: (line %d) %s\n", + pd->errors[i].error_position, + pd->errors[i].error); + } // for + } // if else { if (pd->output != NULL) diff --git a/finderrors.c b/finderrors.c index 30fd17ea..459a7275 100644 --- a/finderrors.c +++ b/finderrors.c @@ -85,9 +85,9 @@ static int do_file(const char *profile, const char *dname, const char *fn, int * buf[rc] = '\0'; // make sure the source is null-terminated. a = MOJOSHADER_assemble((char *) buf, 0, 0, 0, 0, 0, 0, 0); - if (a->error) + if (a->error_count > 0) { - report("FAIL: %s (line %d) %s\n", fname, a->error_position, a->error); + report("FAIL: %s (line %d) %s\n", fname, a->errors[0].error_position, a->errors[0].error); return 1; } // if diff --git a/mojoshader.c b/mojoshader.c index 44d4a53d..b23e2acb 100644 --- a/mojoshader.c +++ b/mojoshader.c @@ -108,6 +108,12 @@ typedef struct ConstantsList struct ConstantsList *next; } ConstantsList; +typedef struct ErrorList +{ + MOJOSHADER_error error; + struct ErrorList *next; +} ErrorList; + typedef struct VariableList { MOJOSHADER_uniformType type; @@ -158,6 +164,8 @@ typedef struct // Context...this is state that changes as we parse through a shader... struct Context { + int isfail; + int out_of_memory; MOJOSHADER_malloc malloc; MOJOSHADER_free free; void *malloc_data; @@ -183,7 +191,6 @@ struct Context const char *shader_type_str; const char *endline; int endline_len; - const char *failstr; char scratch[SCRATCH_BUFFERS][SCRATCH_BUFFER_SIZE]; int scratchidx; // current scratch buffer. int profileid; @@ -212,6 +219,8 @@ struct Context int last_address_reg_component; RegisterList used_registers; RegisterList defined_registers; + int error_count; + ErrorList *errors; int constant_count; ConstantsList *constants; int uniform_count; @@ -236,15 +245,15 @@ struct Context // Convenience functions for allocators... +static MOJOSHADER_error out_of_mem_error = { "Out of memory", NULL, -1 }; MOJOSHADER_parseData out_of_mem_data = { - "Out of memory", -1, 0, 0, 0, 0, MOJOSHADER_TYPE_UNKNOWN, 0, 0, 0, 0 + 1, &out_of_mem_error, 0, 0, 0, 0, MOJOSHADER_TYPE_UNKNOWN, 0, 0, 0, 0 }; const char *out_of_mem_str = "Out of memory"; static inline int out_of_memory(Context *ctx) { - if (ctx->failstr == NULL) - ctx->failstr = out_of_mem_str; // fail() would call malloc(). + ctx->isfail = ctx->out_of_memory = 1; return FAIL; } // out_of_memory @@ -323,24 +332,13 @@ static inline int shader_is_vertex(const Context *ctx) static inline int isfail(const Context *ctx) { - return (ctx->failstr != NULL); + return ctx->isfail; } // isfail static inline char *get_scratch_buffer(Context *ctx) { - if ((ctx->scratchidx >= SCRATCH_BUFFERS) && !isfail(ctx)) - { - // can't call fail() here, since it calls back into here. - const char *errstr = "BUG: overflowed scratch buffers"; - char *failstr = (char *) Malloc(ctx, strlen(errstr) + 1); - if (failstr != NULL) - { - strcpy(failstr, errstr); - ctx->failstr = failstr; - } // if - } // if - + assert(ctx->scratchidx < SCRATCH_BUFFERS); ctx->scratchidx = (ctx->scratchidx + 1) % SCRATCH_BUFFERS; return ctx->scratch[ctx->scratchidx]; } // get_scratch_buffer @@ -349,29 +347,71 @@ static inline char *get_scratch_buffer(Context *ctx) static int failf(Context *ctx, const char *fmt, ...) ISPRINTF(2,3); static int failf(Context *ctx, const char *fmt, ...) { - if (ctx->failstr == NULL) // don't change existing error. + ctx->isfail = 1; + if (ctx->out_of_memory) + return FAIL; + + int error_position = 0; + switch (ctx->parse_phase) { - char *scratch = get_scratch_buffer(ctx); - va_list ap; - va_start(ap, fmt); - const int len = vsnprintf(scratch, SCRATCH_BUFFER_SIZE, fmt, ap); - va_end(ap); + case MOJOSHADER_PARSEPHASE_NOTSTARTED: + error_position = -2; + break; + case MOJOSHADER_PARSEPHASE_WORKING: + error_position = (ctx->tokens - ctx->orig_tokens) * sizeof (uint32); + break; + case MOJOSHADER_PARSEPHASE_DONE: + error_position = -1; + break; + default: + assert(0 && "Unexpected value"); + return FAIL; + } // switch + + ErrorList *error = (ErrorList *) Malloc(ctx, sizeof (ErrorList)); + if (error == NULL) + return FAIL; + + char *scratch = get_scratch_buffer(ctx); + va_list ap; + va_start(ap, fmt); + const int len = vsnprintf(scratch, SCRATCH_BUFFER_SIZE, fmt, ap); + va_end(ap); - char *failstr = (char *) Malloc(ctx, len + 1); - if (failstr != NULL) + char *failstr = (char *) Malloc(ctx, len + 1); + if (failstr == NULL) + Free(ctx, error); + else + { + // see comments about scratch buffer overflow in output_line(). + if (len < SCRATCH_BUFFER_SIZE) + strcpy(failstr, scratch); // copy it over. + else { - // see comments about scratch buffer overflow in output_line(). - if (len < SCRATCH_BUFFER_SIZE) - strcpy(failstr, scratch); // copy it over. - else - { - va_start(ap, fmt); - vsnprintf(failstr, len + 1, fmt, ap); // rebuild it. - va_end(ap); - } // else - ctx->failstr = failstr; - } // if - } // if + va_start(ap, fmt); + vsnprintf(failstr, len + 1, fmt, ap); // rebuild it. + va_end(ap); + } // else + + error->error.error = failstr; + error->error.filename = NULL; // no filename at this level. + error->error.error_position = error_position; + + ErrorList *prev = NULL; + error->next = ctx->errors; + while (error->next != NULL) + { + prev = error->next; + error->next = error->next->next; + } // while + + if (prev != NULL) + prev->next = error; + else + ctx->errors = error; + + ctx->error_count++; + } // else return FAIL; } // failf @@ -6795,6 +6835,19 @@ static void free_variable_list(MOJOSHADER_free f, void *d, VariableList *item) } // free_variable_list +static void free_error_list(MOJOSHADER_free f, void *d, ErrorList *item) +{ + while (item != NULL) + { + ErrorList *next = item->next; + f((void *) item->error.error, d); + f((void *) item->error.filename, d); + f(item, d); + item = next; + } // while +} // free_error_list + + static void destroy_context(Context *ctx) { if (ctx != NULL) @@ -6816,8 +6869,7 @@ static void destroy_context(Context *ctx) free_reglist(f, d, ctx->attributes.next); free_reglist(f, d, ctx->samplers.next); free_variable_list(f, d, ctx->variables); - if ((ctx->failstr != NULL) && (ctx->failstr != out_of_mem_str)) - f((void *) ctx->failstr, d); + free_error_list(f, d, ctx->errors); f(ctx, d); } // if } // destroy_context @@ -7043,6 +7095,31 @@ static MOJOSHADER_sampler *build_samplers(Context *ctx) } // build_samplers +static MOJOSHADER_error *build_errors(Context *ctx) +{ + int total = 0; + MOJOSHADER_error *retval; + retval = (MOJOSHADER_error *) Malloc(ctx, sizeof (MOJOSHADER_error)); + if (retval == NULL) + return NULL; + + ErrorList *item = ctx->errors; + while (item != NULL) + { + ErrorList *next = item->next; + // reuse the string allocations + memcpy(&retval[total], &item->error, sizeof (MOJOSHADER_error)); + Free(ctx, item); + item = next; + total++; + } // while + ctx->errors = NULL; + + assert(total == ctx->error_count); + return retval; +} // build_errors + + static MOJOSHADER_attribute *build_attributes(Context *ctx, int *_count) { int count = 0; @@ -7123,9 +7200,13 @@ static MOJOSHADER_parseData *build_parsedata(Context *ctx) MOJOSHADER_attribute *attributes = NULL; MOJOSHADER_sampler *samplers = NULL; MOJOSHADER_swizzle *swizzles = NULL; + MOJOSHADER_error *errors = NULL; MOJOSHADER_parseData *retval = NULL; int attribute_count = 0; + if (ctx->out_of_memory) + return &out_of_mem_data; + retval = (MOJOSHADER_parseData*) Malloc(ctx, sizeof(MOJOSHADER_parseData)); if (retval == NULL) return &out_of_mem_data; @@ -7147,6 +7228,9 @@ static MOJOSHADER_parseData *build_parsedata(Context *ctx) if (!isfail(ctx)) samplers = build_samplers(ctx); + if (!isfail(ctx)) + errors = build_errors(ctx); + if (!isfail(ctx)) { if (ctx->swizzles_count > 0) @@ -7188,25 +7272,20 @@ static MOJOSHADER_parseData *build_parsedata(Context *ctx) Free(ctx, samplers); } // if - switch (ctx->parse_phase) + if (ctx->out_of_memory) { - case MOJOSHADER_PARSEPHASE_NOTSTARTED: - retval->error_position = -2; - break; - case MOJOSHADER_PARSEPHASE_WORKING: - retval->error_position = (ctx->tokens - ctx->orig_tokens) * sizeof (uint32); - break; - case MOJOSHADER_PARSEPHASE_DONE: - retval->error_position = -1; - break; - } // switch - - retval->error = ctx->failstr; // we recycle. :) - ctx->failstr = NULL; // don't let this get free()'d too soon. + for (i = 0; i < ctx->sampler_count; i++) + { + Free(ctx, (void *) errors[i].filename); + Free(ctx, (void *) errors[i].error); + } // for + Free(ctx, ctx->errors); + Free(ctx, retval); + return &out_of_mem_data; + } // if } // if else { - retval->error_position = -3; retval->profile = ctx->profile->name; retval->output = output; retval->output_len = ctx->output_len; @@ -7226,6 +7305,8 @@ static MOJOSHADER_parseData *build_parsedata(Context *ctx) retval->swizzles = swizzles; } // else + retval->error_count = ctx->error_count; + retval->errors = errors; retval->malloc = (ctx->malloc == internal_malloc) ? NULL : ctx->malloc; retval->free = (ctx->free == internal_free) ? NULL : ctx->free; retval->malloc_data = ctx->malloc_data; @@ -7473,6 +7554,18 @@ void MOJOSHADER_freeParseData(const MOJOSHADER_parseData *_data) if (data->swizzles != NULL) f((void *) data->swizzles, d); + if (data->errors != NULL) + { + for (i = 0; i < data->error_count; i++) + { + if (data->errors[i].error != NULL) + f((void *) data->errors[i].error, d); + if (data->errors[i].filename != NULL) + f((void *) data->errors[i].filename, d); + } // for + f((void *) data->errors, d); + } // if + if (data->uniforms != NULL) { for (i = 0; i < data->uniform_count; i++) @@ -7515,9 +7608,6 @@ void MOJOSHADER_freeParseData(const MOJOSHADER_parseData *_data) f((void *) data->symbols, d); } // if - if ((data->error != NULL) && (data->error != out_of_mem_str)) - f((void *) data->error, d); - f(data, d); } // MOJOSHADER_freeParseData diff --git a/mojoshader.h b/mojoshader.h index bce76ed1..422dbaa1 100644 --- a/mojoshader.h +++ b/mojoshader.h @@ -319,11 +319,7 @@ typedef struct MOJOSHADER_symbol } MOJOSHADER_symbol; -/* - * Structure used to return data from parsing of a shader... - */ -/* !!! FIXME: most of these ints should be unsigned. */ -typedef struct MOJOSHADER_parseData +typedef struct MOJOSHADER_error { /* * Human-readable error, if there is one. Will be NULL if there was no @@ -332,6 +328,11 @@ typedef struct MOJOSHADER_parseData */ const char *error; + /* + * Filename where error happened. + */ + const char *filename; + /* * Position of error, if there is one. Will be -3 if there was no * error, -2 if there was an error before processing started, and @@ -341,6 +342,25 @@ typedef struct MOJOSHADER_parseData * a line number in the source code you supplied (starting at one). */ int error_position; +} MOJOSHADER_error; + +/* + * Structure used to return data from parsing of a shader... + */ +/* !!! FIXME: most of these ints should be unsigned. */ +typedef struct MOJOSHADER_parseData +{ + /* + * The number of elements pointed to by (errors). + */ + int error_count; + + /* + * (error_count) elements of data that specify errors that were generated + * by parsing this shader. + * This can be NULL if there were no errors or if (error_count) is zero. + */ + MOJOSHADER_error *errors; /* * The name of the profile used to parse the shader. Will be NULL on error. diff --git a/mojoshader_assembler.c b/mojoshader_assembler.c index 0bd99d40..2b193469 100644 --- a/mojoshader_assembler.c +++ b/mojoshader_assembler.c @@ -27,10 +27,15 @@ typedef struct TokenizerContext } TokenizerContext; -typedef struct Context Context; +typedef struct SourcePos +{ + const char *filename; + uint32 line; +} SourcePos; + // Context...this is state that changes as we assemble a shader... -struct Context +typedef struct Context { MOJOSHADER_malloc malloc; MOJOSHADER_free free; @@ -46,13 +51,13 @@ struct Context int tokenbufpos; DestArgInfo dest_arg; uint32 *output; - uint32 *token_to_line; + SourcePos *token_to_source; uint8 *ctab; uint32 ctab_len; uint32 ctab_allocation; size_t output_len; size_t output_allocation; -}; +} Context; // Convenience functions for allocators... @@ -153,6 +158,13 @@ static int ui32fromstr(const char *str, uint32 *ui32) } // ui32fromstr +static inline void add_token_sourcepos(Context *ctx, const size_t idx) +{ + ctx->token_to_source[idx].line = ctx->tctx.linenum; + ctx->token_to_source[idx].filename = NULL; +} // add_token_sourcepos + + static void output_token_noswap(Context *ctx, const uint32 token) { if (isfail(ctx)) @@ -172,19 +184,19 @@ static void output_token_noswap(Context *ctx, const uint32 token) Free(ctx, ctx->output); ctx->output = (uint32 *) ptr; - ptr = Malloc(ctx, newsize * sizeof (uint32)); + ptr = Malloc(ctx, newsize * sizeof (SourcePos)); if (ptr == NULL) return; if (ctx->output_len > 0) - memcpy(ptr, ctx->token_to_line, ctx->output_len * sizeof (uint32)); - Free(ctx, ctx->token_to_line); - ctx->token_to_line = (uint32 *) ptr; + memcpy(ptr, ctx->token_to_source, ctx->output_len * sizeof (SourcePos)); + Free(ctx, ctx->token_to_source); + ctx->token_to_source = (SourcePos *) ptr; ctx->output_allocation = newsize; } // if ctx->output[ctx->output_len] = token; - ctx->token_to_line[ctx->output_len] = ctx->tctx.linenum; + add_token_sourcepos(ctx, ctx->output_len); ctx->output_len++; } // output_token_noswap @@ -1645,8 +1657,8 @@ static void destroy_context(Context *ctx) f((void *) ctx->failstr, d); if (ctx->output != NULL) f(ctx->output, d); - if (ctx->token_to_line != NULL) - f(ctx->token_to_line, d); + if (ctx->token_to_source != NULL) + f(ctx->token_to_source, d); if (ctx->ctab != NULL) f(ctx->ctab, d); f(ctx, d); @@ -1668,19 +1680,30 @@ static const MOJOSHADER_parseData *build_failed_assembly(Context *ctx) retval->malloc = (ctx->malloc == internal_malloc) ? NULL : ctx->malloc; retval->free = (ctx->free == internal_free) ? NULL : ctx->free; retval->malloc_data = ctx->malloc_data; - retval->error = ctx->failstr; // we recycle. :) + + // !!! FIXME: handle multiple errors. + retval->errors = (MOJOSHADER_error *) + Malloc(ctx, sizeof (MOJOSHADER_error)); + if (retval->errors == NULL) + { + Free(ctx, retval); + return &out_of_mem_data; + } // if + retval->errors->error = ctx->failstr; + retval->errors->filename = NULL; + ctx->failstr = NULL; // don't let this get free()'d too soon. switch (ctx->parse_phase) { case MOJOSHADER_PARSEPHASE_NOTSTARTED: - retval->error_position = -2; + retval->errors->error_position = -2; break; case MOJOSHADER_PARSEPHASE_WORKING: - retval->error_position = ctx->tctx.linenum; + retval->errors->error_position = ctx->tctx.linenum; break; case MOJOSHADER_PARSEPHASE_DONE: - retval->error_position = -1; + retval->errors->error_position = -1; break; } // switch @@ -1910,6 +1933,8 @@ const MOJOSHADER_parseData *MOJOSHADER_assemble(const char *source, ctx->output_len * sizeof (uint32), NULL, 0, m, f, d); +// !!! FIXME: multiple errors. +#if 0 // on error, map the bytecode back to a line number. if (retval->error_position >= 0) { @@ -1920,6 +1945,7 @@ const MOJOSHADER_parseData *MOJOSHADER_assemble(const char *source, else retval->error_position = -1; // oh well. } // if +#endif } // if destroy_context(ctx); diff --git a/mojoshader_opengl.c b/mojoshader_opengl.c index b3a386d4..9ed5fe13 100644 --- a/mojoshader_opengl.c +++ b/mojoshader_opengl.c @@ -1067,9 +1067,9 @@ MOJOSHADER_glShader *MOJOSHADER_glCompileShader(const unsigned char *tokenbuf, ctx->malloc_fn, ctx->free_fn, ctx->malloc_data); - if (pd->error != NULL) + if (pd->errors != NULL) { - set_error(pd->error); + set_error(pd->errors[0].error); goto compile_shader_fail; } // if diff --git a/testoutput.c b/testoutput.c index 33097493..6eeaa8b4 100644 --- a/testoutput.c +++ b/testoutput.c @@ -17,8 +17,16 @@ static int do_parse(const unsigned char *buf, const int len, const char *prof) int retval = 0; pd = MOJOSHADER_parse(prof, buf, len, NULL, 0, NULL, NULL, NULL); - if (pd->error != NULL) - printf("ERROR: (position %d) %s\n", pd->error_position, pd->error); + if (pd->error_count > 0) + { + int i; + for (i = 0; i < pd->error_count; i++) + { + printf("ERROR: (line %d) %s\n", + pd->errors[i].error_position, + pd->errors[i].error); + } // for + } // if else { retval = 1; diff --git a/testparse.c b/testparse.c index b2c89986..52a27ad5 100644 --- a/testparse.c +++ b/testparse.c @@ -61,8 +61,16 @@ static int do_parse(const unsigned char *buf, const int len, const char *prof) pd = MOJOSHADER_parse(prof, buf, len, NULL, 0, Malloc, Free, NULL); printf("PROFILE: %s\n", prof); - if (pd->error != NULL) - printf("ERROR: (position %d) %s\n", pd->error_position, pd->error); + if (pd->error_count > 0) + { + int i; + for (i = 0; i < pd->error_count; i++) + { + printf("ERROR: (position %d) %s\n", + pd->errors[i].error_position, + pd->errors[i].error); + } // for + } // if else { retval = 1;