From 7e991e7be19a5e2924bccb423c89b615e47a7e9d Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Wed, 18 Jun 2014 23:02:00 +0000 Subject: [PATCH] Add more allowances for relaxed error checking mode: Warn instead of error on use of a disabled extension, allow 'f' suffix on floating point literals, and allow #version after tokens. git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@27113 e7fa87d3-cd2b-0410-9028-fcbf551c1848 --- glslang/MachineIndependent/Scan.cpp | 144 +++++++++++++-------- glslang/MachineIndependent/Scan.h | 2 +- glslang/MachineIndependent/ShaderLang.cpp | 18 ++- glslang/MachineIndependent/Versions.cpp | 4 + .../MachineIndependent/preprocessor/PpScanner.cpp | 3 +- 5 files changed, 114 insertions(+), 57 deletions(-) diff --git a/glslang/MachineIndependent/Scan.cpp b/glslang/MachineIndependent/Scan.cpp index 3d675df..5f92ba7 100644 --- a/glslang/MachineIndependent/Scan.cpp +++ b/glslang/MachineIndependent/Scan.cpp @@ -155,79 +155,117 @@ void TInputScanner::consumeWhitespaceComment(bool& foundNonSpaceTab) // or no #version was found; otherwise, returns false. There is no error case, it always // succeeds, but will leave version == 0 if no #version was found. // +// Sets versionNotFirstToken based on whether tokens (beyond white space and comments) +// appeared before the #version. +// // N.B. does not attempt to leave input in any particular known state. The assumption // is that scanning will start anew, following the rules for the chosen version/profile, // and with a corresponding parsing context. // -bool TInputScanner::scanVersion(int& version, EProfile& profile) +bool TInputScanner::scanVersion(int& version, EProfile& profile, bool& notFirstToken) { // This function doesn't have to get all the semantics correct, // just find the #version if there is a correct one present. // The preprocessor will have the responsibility of getting all the semantics right. + bool versionNotFirst = false; // means not first WRT comments and white space, nothing more + notFirstToken = false; // means not first WRT to real tokens version = 0; // means not found profile = ENoProfile; bool foundNonSpaceTab = false; - consumeWhitespaceComment(foundNonSpaceTab); - - // # - if (get() != '#') - return true; - - // whitespace + bool lookingInMiddle = false; int c; do { - c = get(); - } while (c == ' ' || c == '\t'); - - if ( c != 'v' || - get() != 'e' || - get() != 'r' || - get() != 's' || - get() != 'i' || - get() != 'o' || - get() != 'n') - return true; + if (lookingInMiddle) { + notFirstToken = true; + // make forward progress by finishing off the current line plus extra new lines + if (peek() == '\n' || peek() == '\r') { + while (peek() == '\n' || peek() == '\r') + get(); + } else + do { + c = get(); + } while (c > 0 && c != '\n' && c != '\r'); + while (peek() == '\n' || peek() == '\r') + get(); + if (peek() < 0) + return true; + } + lookingInMiddle = true; + + // Nominal start, skipping the desktop allowed comments and white space, but tracking if + // something else was found for ES: + consumeWhitespaceComment(foundNonSpaceTab); + if (foundNonSpaceTab) + versionNotFirst = true; + + // "#" + if (get() != '#') { + versionNotFirst = true; + continue; + } - // whitespace - do { - c = get(); - } while (c == ' ' || c == '\t'); + // whitespace + do { + c = get(); + } while (c == ' ' || c == '\t'); + + // "version" + if ( c != 'v' || + get() != 'e' || + get() != 'r' || + get() != 's' || + get() != 'i' || + get() != 'o' || + get() != 'n') { + versionNotFirst = true; + continue; + } - // version number - while (c >= '0' && c <= '9') { - version = 10 * version + (c - '0'); - c = get(); - } - if (version == 0) - return true; - - // whitespace - while (c == ' ' || c == '\t') - c = get(); + // whitespace + do { + c = get(); + } while (c == ' ' || c == '\t'); - // profile - const int maxProfileLength = 13; // not including any 0 - char profileString[maxProfileLength]; - int profileLength; - for (profileLength = 0; profileLength < maxProfileLength; ++profileLength) { - if (c < 0 || c == ' ' || c == '\t' || c == '\n' || c == '\r') - break; - profileString[profileLength] = c; - c = get(); - } - if (c > 0 && c != ' ' && c != '\t' && c != '\n' && c != '\r') - return true; + // version number + while (c >= '0' && c <= '9') { + version = 10 * version + (c - '0'); + c = get(); + } + if (version == 0) { + versionNotFirst = true; + continue; + } + + // whitespace + while (c == ' ' || c == '\t') + c = get(); + + // profile + const int maxProfileLength = 13; // not including any 0 + char profileString[maxProfileLength]; + int profileLength; + for (profileLength = 0; profileLength < maxProfileLength; ++profileLength) { + if (c < 0 || c == ' ' || c == '\t' || c == '\n' || c == '\r') + break; + profileString[profileLength] = c; + c = get(); + } + if (c > 0 && c != ' ' && c != '\t' && c != '\n' && c != '\r') { + versionNotFirst = true; + continue; + } - if (profileLength == 2 && strncmp(profileString, "es", profileLength) == 0) - profile = EEsProfile; - else if (profileLength == 4 && strncmp(profileString, "core", profileLength) == 0) - profile = ECoreProfile; - else if (profileLength == 13 && strncmp(profileString, "compatibility", profileLength) == 0) - profile = ECompatibilityProfile; + if (profileLength == 2 && strncmp(profileString, "es", profileLength) == 0) + profile = EEsProfile; + else if (profileLength == 4 && strncmp(profileString, "core", profileLength) == 0) + profile = ECoreProfile; + else if (profileLength == 13 && strncmp(profileString, "compatibility", profileLength) == 0) + profile = ECompatibilityProfile; - return foundNonSpaceTab; + return versionNotFirst; + } while (true); } // Fill this in when doing glslang-level scanning, to hand back to the parser. diff --git a/glslang/MachineIndependent/Scan.h b/glslang/MachineIndependent/Scan.h index 6a8d0ff..67e6193 100644 --- a/glslang/MachineIndependent/Scan.h +++ b/glslang/MachineIndependent/Scan.h @@ -112,7 +112,7 @@ public: void consumeWhiteSpace(bool& foundNonSpaceTab); bool consumeComment(); void consumeWhitespaceComment(bool& foundNonSpaceTab); - bool scanVersion(int& version, EProfile& profile); + bool scanVersion(int& version, EProfile& profile, bool& notFirstToken); protected: diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index 73b6276..b7c89f8 100644 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -461,11 +461,19 @@ bool CompileDeferred( int version; EProfile profile; glslang::TInputScanner userInput(numStrings, &strings[1], &lengths[1]); // no preamble - bool versionNotFirst = userInput.scanVersion(version, profile); + bool versionNotFirstToken; + bool versionNotFirst = userInput.scanVersion(version, profile, versionNotFirstToken); bool versionNotFound = version == 0; bool goodVersion = DeduceVersionProfile(compiler->infoSink, compiler->getLanguage(), versionNotFirst, defaultVersion, version, profile); bool versionWillBeError = (versionNotFound || (profile == EEsProfile && version >= 300 && versionNotFirst)); - + bool warnVersionNotFirst = false; + if (! versionWillBeError && versionNotFirstToken) { + if (messages & EShMsgRelaxedErrors) + warnVersionNotFirst = true; + else + versionWillBeError = true; + } + intermediate.setVersion(version); intermediate.setProfile(profile); SetupBuiltinSymbolTable(version, profile); @@ -496,6 +504,12 @@ bool CompileDeferred( parseContext.setLimits(*resources); if (! goodVersion) parseContext.addError(); + if (warnVersionNotFirst) { + TSourceLoc loc; + loc.line = 1; + loc.string = 0; + parseContext.warn(loc, "Illegal to have non-comment, non-whitespace tokens before #version", "#version", ""); + } parseContext.initializeExtensionBehavior(); diff --git a/glslang/MachineIndependent/Versions.cpp b/glslang/MachineIndependent/Versions.cpp index eea1302..4ba3319 100644 --- a/glslang/MachineIndependent/Versions.cpp +++ b/glslang/MachineIndependent/Versions.cpp @@ -367,6 +367,10 @@ void TParseContext::requireExtensions(TSourceLoc loc, int numExtensions, const c bool warned = false; for (int i = 0; i < numExtensions; ++i) { TExtensionBehavior behavior = getExtensionBehavior(extensions[i]); + if (behavior == EBhDisable && (messages & EShMsgRelaxedErrors)) { + infoSink.info.message(EPrefixWarning, "The following extension must be enabled to use this feature:", loc); + behavior = EBhWarn; + } if (behavior == EBhWarn) { infoSink.info.message(EPrefixWarning, ("extension " + TString(extensions[i]) + " is being used for " + featureDesc).c_str(), loc); warned = true; diff --git a/glslang/MachineIndependent/preprocessor/PpScanner.cpp b/glslang/MachineIndependent/preprocessor/PpScanner.cpp index 879031f..4fa7ad5 100644 --- a/glslang/MachineIndependent/preprocessor/PpScanner.cpp +++ b/glslang/MachineIndependent/preprocessor/PpScanner.cpp @@ -205,7 +205,8 @@ int TPpContext::lFloatConst(int len, int ch, TPpToken* ppToken) } } else if (ch == 'f' || ch == 'F') { parseContext.profileRequires(ppToken->loc, EEsProfile, 300, 0, "floating-point suffix"); - parseContext.profileRequires(ppToken->loc, ~EEsProfile, 120, 0, "floating-point suffix"); + if ((parseContext.messages & EShMsgRelaxedErrors) == 0) + parseContext.profileRequires(ppToken->loc, ~EEsProfile, 120, 0, "floating-point suffix"); if (! HasDecimalOrExponent) parseContext.error(ppToken->loc, "float literal needs a decimal point or exponent", "", ""); if (len < TPpToken::maxTokenLength) -- 2.7.4