Front-end: Fix known crashes by early exit on error (issue #29, issue #34, issue #35).

Added -C option to request cascading errors.  By default, will exit early,
to avoid all error-recovery-based crashes.

This works by simulating end-of-file in input on first error, so no
need for exception handling, or stack unwinding, or any complex error
checking/handling to get out of the stack.
This commit is contained in:
John Kessenich 2016-07-09 14:50:57 -06:00
parent 75b0316f6a
commit a86836ede2
11 changed files with 57 additions and 37 deletions

View File

@ -58,25 +58,26 @@ extern "C" {
// Command-line options // Command-line options
enum TOptions { enum TOptions {
EOptionNone = 0x0000, EOptionNone = 0,
EOptionIntermediate = 0x0001, EOptionIntermediate = (1 << 0),
EOptionSuppressInfolog = 0x0002, EOptionSuppressInfolog = (1 << 1),
EOptionMemoryLeakMode = 0x0004, EOptionMemoryLeakMode = (1 << 2),
EOptionRelaxedErrors = 0x0008, EOptionRelaxedErrors = (1 << 3),
EOptionGiveWarnings = 0x0010, EOptionGiveWarnings = (1 << 4),
EOptionLinkProgram = 0x0020, EOptionLinkProgram = (1 << 5),
EOptionMultiThreaded = 0x0040, EOptionMultiThreaded = (1 << 6),
EOptionDumpConfig = 0x0080, EOptionDumpConfig = (1 << 7),
EOptionDumpReflection = 0x0100, EOptionDumpReflection = (1 << 8),
EOptionSuppressWarnings = 0x0200, EOptionSuppressWarnings = (1 << 9),
EOptionDumpVersions = 0x0400, EOptionDumpVersions = (1 << 10),
EOptionSpv = 0x0800, EOptionSpv = (1 << 11),
EOptionHumanReadableSpv = 0x1000, EOptionHumanReadableSpv = (1 << 12),
EOptionVulkanRules = 0x2000, EOptionVulkanRules = (1 << 13),
EOptionDefaultDesktop = 0x4000, EOptionDefaultDesktop = (1 << 14),
EOptionOutputPreprocessed = 0x8000, EOptionOutputPreprocessed = (1 << 15),
EOptionOutputHexadecimal = 0x10000, EOptionOutputHexadecimal = (1 << 16),
EOptionReadHlsl = 0x20000, EOptionReadHlsl = (1 << 17),
EOptionCascadingErrors = (1 << 18),
}; };
// //
@ -247,6 +248,9 @@ void ProcessArguments(int argc, char* argv[])
case 'c': case 'c':
Options |= EOptionDumpConfig; Options |= EOptionDumpConfig;
break; break;
case 'C':
Options |= EOptionCascadingErrors;
break;
case 'd': case 'd':
Options |= EOptionDefaultDesktop; Options |= EOptionDefaultDesktop;
break; break;
@ -347,6 +351,8 @@ void SetMessageOptions(EShMessages& messages)
messages = (EShMessages)(messages | EShMsgOnlyPreprocessor); messages = (EShMessages)(messages | EShMsgOnlyPreprocessor);
if (Options & EOptionReadHlsl) if (Options & EOptionReadHlsl)
messages = (EShMessages)(messages | EShMsgReadHlsl); messages = (EShMessages)(messages | EShMsgReadHlsl);
if (Options & EOptionCascadingErrors)
messages = (EShMessages)(messages | EShMsgCascadingErrors);
} }
// //
@ -772,6 +778,7 @@ void usage()
" errors will appear on stderr.\n" " errors will appear on stderr.\n"
" -c configuration dump;\n" " -c configuration dump;\n"
" creates the default configuration file (redirect to a .conf file)\n" " creates the default configuration file (redirect to a .conf file)\n"
" -C cascading errors; risks crashes from accumulation of error recoveries\n"
" -d default to desktop (#version 110) when there is no shader #version\n" " -d default to desktop (#version 110) when there is no shader #version\n"
" (default is ES version 100)\n" " (default is ES version 100)\n"
" -D input is HLSL\n" " -D input is HLSL\n"

View File

@ -22,15 +22,15 @@ rm -f comp.spv frag.spv geom.spv tesc.spv tese.spv vert.spv
# reflection tests # reflection tests
# #
echo Running reflection... echo Running reflection...
$EXE -l -q reflection.vert > $TARGETDIR/reflection.vert.out $EXE -l -q -C reflection.vert > $TARGETDIR/reflection.vert.out
diff -b $BASEDIR/reflection.vert.out $TARGETDIR/reflection.vert.out || HASERROR=1 diff -b $BASEDIR/reflection.vert.out $TARGETDIR/reflection.vert.out || HASERROR=1
# #
# multi-threaded test # multi-threaded test
# #
echo Comparing single thread to multithread for all tests in current directory... echo Comparing single thread to multithread for all tests in current directory...
$EXE -i *.vert *.geom *.frag *.tes* *.comp > singleThread.out $EXE -i -C *.vert *.geom *.frag *.tes* *.comp > singleThread.out
$EXE -i *.vert *.geom *.frag *.tes* *.comp -t > multiThread.out $EXE -i -C *.vert *.geom *.frag *.tes* *.comp -t > multiThread.out
diff singleThread.out multiThread.out || HASERROR=1 diff singleThread.out multiThread.out || HASERROR=1
if [ $HASERROR -eq 0 ] if [ $HASERROR -eq 0 ]

View File

@ -377,6 +377,9 @@ void C_DECL TParseContext::error(const TSourceLoc& loc, const char* szReason, co
va_start(args, szExtraInfoFormat); va_start(args, szExtraInfoFormat);
outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args); outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args);
va_end(args); va_end(args);
if ((messages & EShMsgCascadingErrors) == 0)
currentScanner->setEndOfInput();
} }
void C_DECL TParseContext::warn(const TSourceLoc& loc, const char* szReason, const char* szToken, void C_DECL TParseContext::warn(const TSourceLoc& loc, const char* szReason, const char* szToken,
@ -397,6 +400,9 @@ void C_DECL TParseContext::ppError(const TSourceLoc& loc, const char* szReason,
va_start(args, szExtraInfoFormat); va_start(args, szExtraInfoFormat);
outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args); outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args);
va_end(args); va_end(args);
if ((messages & EShMsgCascadingErrors) == 0)
currentScanner->setEndOfInput();
} }
void C_DECL TParseContext::ppWarn(const TSourceLoc& loc, const char* szReason, const char* szToken, void C_DECL TParseContext::ppWarn(const TSourceLoc& loc, const char* szReason, const char* szToken,

View File

@ -40,7 +40,7 @@
namespace glslang { namespace glslang {
// Use a global end-of-input character, so no tranlation is needed across // Use a global end-of-input character, so no translation is needed across
// layers of encapsulation. Characters are all 8 bit, and positive, so there is // layers of encapsulation. Characters are all 8 bit, and positive, so there is
// no aliasing of character 255 onto -1, for example. // no aliasing of character 255 onto -1, for example.
const int EndOfInput = -1; const int EndOfInput = -1;
@ -82,7 +82,8 @@ public:
int get() int get()
{ {
int ret = peek(); int ret = peek();
if (ret == EndOfInput) return ret; if (ret == EndOfInput)
return ret;
++loc[currentSource].column; ++loc[currentSource].column;
++logicalSourceLoc.column; ++logicalSourceLoc.column;
if (ret == '\n') { if (ret == '\n') {
@ -123,7 +124,8 @@ public:
void unget() void unget()
{ {
// Do not roll back once we've reached the end of the file. // Do not roll back once we've reached the end of the file.
if (endOfFileReached) return; if (endOfFileReached)
return;
if (currentChar > 0) { if (currentChar > 0) {
--currentChar; --currentChar;
@ -196,6 +198,12 @@ public:
loc[getLastValidSourceIndex()].column = col; loc[getLastValidSourceIndex()].column = col;
} }
void setEndOfInput()
{
endOfFileReached = true;
currentSource = numSources;
}
const TSourceLoc& getSourceLoc() const const TSourceLoc& getSourceLoc() const
{ {
if (singleLogical) { if (singleLogical) {
@ -255,7 +263,7 @@ protected:
bool singleLogical; // treats the strings as a single logical string. bool singleLogical; // treats the strings as a single logical string.
// locations will be reported from the first string. // locations will be reported from the first string.
// set to true once peak() returns EndOfFile, so that we won't roll back // Set to true once peek() returns EndOfFile, so that we won't roll back
// once we've reached EndOfFile. // once we've reached EndOfFile.
bool endOfFileReached; bool endOfFileReached;
}; };

View File

@ -141,6 +141,7 @@ enum EShMessages {
EShMsgVulkanRules = (1 << 4), // issue messages for Vulkan-requirements of GLSL for SPIR-V EShMsgVulkanRules = (1 << 4), // issue messages for Vulkan-requirements of GLSL for SPIR-V
EShMsgOnlyPreprocessor = (1 << 5), // only print out errors produced by the preprocessor EShMsgOnlyPreprocessor = (1 << 5), // only print out errors produced by the preprocessor
EShMsgReadHlsl = (1 << 6), // use HLSL parsing rules and semantics EShMsgReadHlsl = (1 << 6), // use HLSL parsing rules and semantics
EShMsgCascadingErrors = (1 << 7), // get cascading errors; risks error-recovery issues, instead of an early exit
}; };
// //

View File

@ -97,8 +97,8 @@ TEST_P(ConfigTest, FromFile)
INSTANTIATE_TEST_CASE_P( INSTANTIATE_TEST_CASE_P(
Glsl, ConfigTest, Glsl, ConfigTest,
::testing::ValuesIn(std::vector<TestCaseSpec>({ ::testing::ValuesIn(std::vector<TestCaseSpec>({
{"specExamples.vert", "baseResults/test.conf", "specExamples.vert.out", EShMsgAST}, {"specExamples.vert", "baseResults/test.conf", "specExamples.vert.out", (EShMessages)(EShMsgAST | EShMsgCascadingErrors)},
{"100Limits.vert", "100.conf", "100LimitsConf.vert.out", EShMsgDefault}, {"100Limits.vert", "100.conf", "100LimitsConf.vert.out", EShMsgCascadingErrors},
})), })),
); );
// clang-format on // clang-format on

View File

@ -51,7 +51,7 @@ namespace glslangtest {
// gets fixed. // gets fixed.
class GlslangInitializer { class GlslangInitializer {
public: public:
GlslangInitializer() : lastMessages(EShMsgDefault) GlslangInitializer() : lastMessages(EShMsgCascadingErrors)
{ {
glslang::InitializeProcess(); glslang::InitializeProcess();
} }

View File

@ -48,8 +48,7 @@ TEST_P(LinkTest, FromFile)
{ {
const auto& fileNames = GetParam(); const auto& fileNames = GetParam();
const size_t fileCount = fileNames.size(); const size_t fileCount = fileNames.size();
const EShMessages controls = DeriveOptions( const EShMessages controls = DeriveOptions(Source::GLSL, Semantics::OpenGL, Target::AST);
Source::GLSL, Semantics::OpenGL, Target::AST);
GlslangResult result; GlslangResult result;
// Compile each input shader file. // Compile each input shader file.

View File

@ -68,7 +68,7 @@ EShLanguage GetShaderStage(const std::string& stage)
EShMessages DeriveOptions(Source source, Semantics semantics, Target target) EShMessages DeriveOptions(Source source, Semantics semantics, Target target)
{ {
EShMessages result = EShMsgDefault; EShMessages result = EShMsgCascadingErrors;
switch (source) { switch (source) {
case Source::GLSL: case Source::GLSL:

View File

@ -264,8 +264,7 @@ public:
tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
const EShMessages controls = DeriveOptions(source, semantics, target); const EShMessages controls = DeriveOptions(source, semantics, target);
GlslangResult result = GlslangResult result = compileAndLink(testName, input, entryPointName, controls);
compileAndLink(testName, input, entryPointName, controls);
// Generate the hybrid output in the way of glslangValidator. // Generate the hybrid output in the way of glslangValidator.
std::ostringstream stream; std::ostringstream stream;
@ -290,7 +289,7 @@ public:
glslang::TShader::ForbidInclude includer; glslang::TShader::ForbidInclude includer;
const bool success = shader.preprocess( const bool success = shader.preprocess(
&glslang::DefaultTBuiltInResource, defaultVersion, defaultProfile, &glslang::DefaultTBuiltInResource, defaultVersion, defaultProfile,
forceVersionProfile, isForwardCompatible, EShMsgOnlyPreprocessor, forceVersionProfile, isForwardCompatible, (EShMessages)(EShMsgOnlyPreprocessor | EShMsgCascadingErrors),
&ppShader, includer); &ppShader, includer);
std::string log = shader.getInfoLog(); std::string log = shader.getInfoLog();