diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp index e914150d..8eb3ee3e 100644 --- a/StandAlone/StandAlone.cpp +++ b/StandAlone/StandAlone.cpp @@ -701,8 +701,8 @@ void CompileAndLinkShaders() shader->setStrings(shaderStrings, 1); if (Options & EOptionOutputPreprocessed) { std::string str; - if (shader->preprocess(&Resources, defaultVersion, ENoProfile, - false, false, messages, &str)) { + if (shader->preprocess(&Resources, defaultVersion, ENoProfile, false, false, + messages, &str, glslang::TShader::ForbidInclude())) { PutsIfNonEmpty(str.c_str()); } else { CompileFailed = true; diff --git a/Test/baseResults/preprocessor.include.disabled.vert.err b/Test/baseResults/preprocessor.include.disabled.vert.err new file mode 100644 index 00000000..1145a314 --- /dev/null +++ b/Test/baseResults/preprocessor.include.disabled.vert.err @@ -0,0 +1,13 @@ +ERROR: 0:8000: '#include' : required extension not requested: GL_GOOGLE_include_directive +ERROR: 0:8000: '#include' : must be followed by a file designation +ERROR: 0:8001: '#include' : required extension not requested: GL_GOOGLE_include_directive +ERROR: 0:8001: '#include' : must be followed by a file designation +ERROR: 0:8002: '#include' : required extension not requested: GL_GOOGLE_include_directive +ERROR: 0:8002: '#include' : unexpected include directive +ERROR: 0:8003: '#include' : required extension not requested: GL_GOOGLE_include_directive +ERROR: 0:8003: '#include' : extra content after file designation +ERROR: 0:8004: '#include' : required extension not requested: GL_GOOGLE_include_directive +ERROR: 0:8004: '#include' : unexpected include directive +ERROR: 10 compilation errors. No code generated. + + diff --git a/Test/baseResults/preprocessor.include.disabled.vert.out b/Test/baseResults/preprocessor.include.disabled.vert.out new file mode 100644 index 00000000..e69de29b diff --git a/Test/baseResults/preprocessor.include.enabled.vert.err b/Test/baseResults/preprocessor.include.enabled.vert.err new file mode 100644 index 00000000..2cdf3f28 --- /dev/null +++ b/Test/baseResults/preprocessor.include.enabled.vert.err @@ -0,0 +1,8 @@ +ERROR: 0:8000: '#include' : must be followed by a file designation +ERROR: 0:8001: '#include' : must be followed by a file designation +ERROR: 0:8002: '#include' : unexpected include directive +ERROR: 0:8003: '#include' : extra content after file designation +ERROR: 0:8004: '#include' : unexpected include directive +ERROR: 5 compilation errors. No code generated. + + diff --git a/Test/baseResults/preprocessor.include.enabled.vert.out b/Test/baseResults/preprocessor.include.enabled.vert.out new file mode 100644 index 00000000..e69de29b diff --git a/Test/preprocessor.include.disabled.vert b/Test/preprocessor.include.disabled.vert new file mode 100644 index 00000000..130d928e --- /dev/null +++ b/Test/preprocessor.include.disabled.vert @@ -0,0 +1,7 @@ +#line 8000 +#include +#include 123 +#include "foo" +#include "foo" garbage +#include "no-eol" + diff --git a/Test/preprocessor.include.enabled.vert b/Test/preprocessor.include.enabled.vert new file mode 100644 index 00000000..ecdf466e --- /dev/null +++ b/Test/preprocessor.include.enabled.vert @@ -0,0 +1,7 @@ +#extension GL_GOOGLE_include_directive : enable +#line 8000 +#include +#include 123 +#include "foo" +#include "foo" garbage +#include "no-eol" diff --git a/Test/test-preprocessor-list b/Test/test-preprocessor-list index e75ac1b4..cd2381a5 100644 --- a/Test/test-preprocessor-list +++ b/Test/test-preprocessor-list @@ -4,6 +4,8 @@ preprocessor.edge_cases.vert preprocessor.errors.vert preprocessor.extensions.vert preprocessor.function_macro.vert +preprocessor.include.enabled.vert +preprocessor.include.disabled.vert preprocessor.line.vert preprocessor.line.frag preprocessor.pragma.vert diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h index 6f5645ef..e008c0f2 100644 --- a/glslang/MachineIndependent/ParseHelper.h +++ b/glslang/MachineIndependent/ParseHelper.h @@ -214,9 +214,11 @@ public: int getNumErrors() const { return numErrors; } const TSourceLoc& getCurrentLoc() const { return currentScanner->getSourceLoc(); } void setCurrentLine(int line) { currentScanner->setLine(line); } + void setCurrentColumn(int col) { currentScanner->setColumn(col); } void setCurrentSourceName(const char* name) { currentScanner->setFile(name); } void setCurrentString(int string) { currentScanner->setString(string); } void setScanner(TInputScanner* scanner) { currentScanner = scanner; } + TInputScanner* getScanner() const { return currentScanner; } bool lineDirectiveShouldSetNextLine() const; @@ -235,6 +237,7 @@ public: void checkDeprecated(const TSourceLoc&, int queryProfiles, int depVersion, const char* featureDesc); void requireNotRemoved(const TSourceLoc&, int queryProfiles, int removedVersion, const char* featureDesc); void requireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], const char* featureDesc); + void ppRequireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], const char* featureDesc); TExtensionBehavior getExtensionBehavior(const char*); bool extensionTurnedOn(const char* const extension); bool extensionsTurnedOn(int numExtensions, const char* const extensions[]); @@ -250,13 +253,14 @@ public: protected: void nonInitConstCheck(const TSourceLoc&, TString& identifier, TType& type); - void inheritGlobalDefaults(TQualifier& dst) const; + void inheritGlobalDefaults(TQualifier& dst) const; TVariable* makeInternalVariable(const char* name, const TType&) const; TVariable* declareNonArray(const TSourceLoc&, TString& identifier, TType&, bool& newDeclaration); void declareArray(const TSourceLoc&, TString& identifier, const TType&, TSymbol*&, bool& newDeclaration); TIntermNode* executeInitializer(const TSourceLoc&, TIntermTyped* initializer, TVariable* variable); TIntermTyped* convertInitializerList(const TSourceLoc&, const TType&, TIntermTyped* initializer); TOperator mapTypeToConstructorOp(const TType&) const; + bool checkExtensionsRequested(const TSourceLoc&, int numExtensions, const char* const extensions[], const char* featureDesc); void updateExtensionBehavior(const char* const extension, TExtensionBehavior); void finalErrorCheck(); void outputMessage(const TSourceLoc&, const char* szReason, const char* szToken, diff --git a/glslang/MachineIndependent/Scan.h b/glslang/MachineIndependent/Scan.h index ff7bfa2f..eade05fd 100644 --- a/glslang/MachineIndependent/Scan.h +++ b/glslang/MachineIndependent/Scan.h @@ -154,6 +154,9 @@ public: loc[getLastValidSourceIndex()].name = nullptr; } + // for #include content indentation + void setColumn(int col) { loc[getLastValidSourceIndex()].column = col; } + const TSourceLoc& getSourceLoc() const { return loc[std::max(0, std::min(currentSource, numSources - finale - 1))]; } // Returns the index (starting from 0) of the most recent valid source string we are reading from. int getLastValidSourceIndex() const { return std::min(currentSource, numSources - 1); } diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index e98df614..617f9e21 100644 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -130,7 +130,7 @@ bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profil TIntermediate intermediate(language, version, profile); TParseContext parseContext(symbolTable, intermediate, true, version, profile, language, infoSink); - TPpContext ppContext(parseContext); + TPpContext ppContext(parseContext, TShader::ForbidInclude()); TScanContext scanContext(parseContext); parseContext.setScanContext(&scanContext); parseContext.setPpContext(&ppContext); @@ -463,7 +463,8 @@ bool ProcessDeferred( EShMessages messages, // warnings/errors/AST; things to print out TIntermediate& intermediate, // returned tree, etc. ProcessingContext& processingContext, - bool requireNonempty + bool requireNonempty, + const TShader::Includer& includer ) { if (! InitThread()) @@ -565,7 +566,7 @@ bool ProcessDeferred( TParseContext parseContext(symbolTable, intermediate, false, version, profile, compiler->getLanguage(), compiler->infoSink, forwardCompatible, messages); glslang::TScanContext scanContext(parseContext); - TPpContext ppContext(parseContext); + TPpContext ppContext(parseContext, includer); parseContext.setScanContext(&scanContext); parseContext.setPpContext(&ppContext); parseContext.setLimits(*resources); @@ -838,6 +839,7 @@ bool PreprocessDeferred( bool forceDefaultVersionAndProfile, bool forwardCompatible, // give errors for use of deprecated features EShMessages messages, // warnings/errors/AST; things to print out + const TShader::Includer& includer, TIntermediate& intermediate, // returned tree, etc. std::string* outputString) { @@ -845,7 +847,8 @@ bool PreprocessDeferred( return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames, preamble, optLevel, resources, defaultVersion, defaultProfile, forceDefaultVersionAndProfile, - forwardCompatible, messages, intermediate, parser, false); + forwardCompatible, messages, intermediate, parser, + false, includer); } @@ -874,13 +877,15 @@ bool CompileDeferred( bool forceDefaultVersionAndProfile, bool forwardCompatible, // give errors for use of deprecated features EShMessages messages, // warnings/errors/AST; things to print out - TIntermediate& intermediate) // returned tree, etc. + TIntermediate& intermediate,// returned tree, etc. + const TShader::Includer& includer) { DoFullParse parser; return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames, preamble, optLevel, resources, defaultVersion, defaultProfile, forceDefaultVersionAndProfile, - forwardCompatible, messages, intermediate, parser, true); + forwardCompatible, messages, intermediate, parser, + true, includer); } } // end anonymous namespace for local functions @@ -1024,7 +1029,7 @@ int ShCompile( TIntermediate intermediate(compiler->getLanguage()); bool success = CompileDeferred(compiler, shaderStrings, numStrings, inputLengths, nullptr, "", optLevel, resources, defaultVersion, ENoProfile, false, - forwardCompatible, messages, intermediate); + forwardCompatible, messages, intermediate, TShader::ForbidInclude()); // // Call the machine dependent compiler @@ -1327,7 +1332,7 @@ void TShader::setStringsWithLengthsAndNames( // Returns true for success. // bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, - bool forwardCompatible, EShMessages messages) + bool forwardCompatible, EShMessages messages, const Includer& includer) { if (! InitThread()) return false; @@ -1340,7 +1345,7 @@ bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion return CompileDeferred(compiler, strings, numStrings, lengths, stringNames, preamble, EShOptNone, builtInResources, defaultVersion, defaultProfile, forceDefaultVersionAndProfile, - forwardCompatible, messages, *intermediate); + forwardCompatible, messages, *intermediate, includer); } bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion, bool forwardCompatible, EShMessages messages) @@ -1351,9 +1356,11 @@ bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion // Fill in a string with the result of preprocessing ShaderStrings // Returns true if all extensions, pragmas and version strings were valid. bool TShader::preprocess(const TBuiltInResource* builtInResources, - int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, - bool forwardCompatible, - EShMessages message, std::string* output_string) + int defaultVersion, EProfile defaultProfile, + bool forceDefaultVersionAndProfile, + bool forwardCompatible, EShMessages message, + std::string* output_string, + const TShader::Includer& includer) { if (! InitThread()) return false; @@ -1366,7 +1373,7 @@ bool TShader::preprocess(const TBuiltInResource* builtInResources, return PreprocessDeferred(compiler, strings, numStrings, lengths, stringNames, preamble, EShOptNone, builtInResources, defaultVersion, defaultProfile, forceDefaultVersionAndProfile, - forwardCompatible, message, *intermediate, output_string); + forwardCompatible, message, includer, *intermediate, output_string); } const char* TShader::getInfoLog() diff --git a/glslang/MachineIndependent/Versions.cpp b/glslang/MachineIndependent/Versions.cpp index 28ad10cb..c751076f 100644 --- a/glslang/MachineIndependent/Versions.cpp +++ b/glslang/MachineIndependent/Versions.cpp @@ -174,7 +174,9 @@ void TParseContext::initializeExtensionBehavior() extensionBehavior[E_GL_ARB_viewport_array] = EBhDisable; // extensionBehavior[E_GL_ARB_cull_distance] = EBhDisable; // present for 4.5, but need extension control over block members + // #line and #include extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhDisable; + extensionBehavior[E_GL_GOOGLE_include_directive] = EBhDisable; // AEP extensionBehavior[E_GL_ANDROID_extension_pack_es31a] = EBhDisablePartial; @@ -219,7 +221,9 @@ const char* TParseContext::getPreamble() "#define GL_OES_EGL_image_external 1\n" "#define GL_EXT_shader_texture_lod 1\n" + // #line and #include "#define GL_GOOGLE_cpp_style_line_directive 1\n" + "#define GL_GOOGLE_include_directive 1\n" // AEP "#define GL_ANDROID_extension_pack_es31a 1\n" @@ -270,6 +274,7 @@ const char* TParseContext::getPreamble() "#define GL_ARB_viewport_array 1\n" "#define GL_GOOGLE_cpp_style_line_directive 1\n" + "#define GL_GOOGLE_include_directive 1\n" // "#define GL_ARB_cull_distance 1\n" // present for 4.5, but need extension control over block members ; } @@ -406,17 +411,15 @@ void TParseContext::requireNotRemoved(const TSourceLoc& loc, int profileMask, in } } -// -// Use when there are no profile/version to check, it's just an error if one of the -// extensions is not present. -// -void TParseContext::requireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc) +// Returns true if at least one of the extensions in the extensions parameter is requested. Otherwise, returns false. +// Warns appropriately if the requested behavior of an extension is "warn". +bool TParseContext::checkExtensionsRequested(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc) { // First, see if any of the extensions are enabled for (int i = 0; i < numExtensions; ++i) { TExtensionBehavior behavior = getExtensionBehavior(extensions[i]); if (behavior == EBhEnable || behavior == EBhRequire) - return; + return true; } // See if any extensions want to give a warning on use; give warnings for all such extensions @@ -433,7 +436,17 @@ void TParseContext::requireExtensions(const TSourceLoc& loc, int numExtensions, } } if (warned) - return; + return true; + return false; +} + +// +// Use when there are no profile/version to check, it's just an error if one of the +// extensions is not present. +// +void TParseContext::requireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc) +{ + if (checkExtensionsRequested(loc, numExtensions, extensions, featureDesc)) return; // If we get this far, give errors explaining what extensions are needed if (numExtensions == 1) @@ -445,6 +458,24 @@ void TParseContext::requireExtensions(const TSourceLoc& loc, int numExtensions, } } +// +// Use by preprocessor when there are no profile/version to check, it's just an error if one of the +// extensions is not present. +// +void TParseContext::ppRequireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc) +{ + if (checkExtensionsRequested(loc, numExtensions, extensions, featureDesc)) return; + + // If we get this far, give errors explaining what extensions are needed + if (numExtensions == 1) + ppError(loc, "required extension not requested:", featureDesc, extensions[0]); + else { + ppError(loc, "required extension not requested:", featureDesc, "Possible extensions include:"); + for (int i = 0; i < numExtensions; ++i) + infoSink.info.message(EPrefixNone, extensions[i]); + } +} + TExtensionBehavior TParseContext::getExtensionBehavior(const char* extension) { auto iter = extensionBehavior.find(TString(extension)); @@ -525,6 +556,8 @@ void TParseContext::updateExtensionBehavior(int line, const char* extension, con updateExtensionBehavior(line, "GL_EXT_shader_io_blocks", behaviorString); else if (strcmp(extension, "GL_OES_tessellation_shader") == 0) updateExtensionBehavior(line, "GL_OES_shader_io_blocks", behaviorString); + else if (strcmp(extension, "GL_GOOGLE_include_directive") == 0) + updateExtensionBehavior(line, "GL_GOOGLE_cpp_style_line_directive", behaviorString); } void TParseContext::updateExtensionBehavior(const char* extension, TExtensionBehavior behavior) diff --git a/glslang/MachineIndependent/Versions.h b/glslang/MachineIndependent/Versions.h index ae8fd414..bb3cbc4d 100644 --- a/glslang/MachineIndependent/Versions.h +++ b/glslang/MachineIndependent/Versions.h @@ -112,7 +112,9 @@ const char* const E_GL_ARB_shader_texture_image_samples = "GL_ARB_shader_texture const char* const E_GL_ARB_viewport_array = "GL_ARB_viewport_array"; //const char* const E_GL_ARB_cull_distance = "GL_ARB_cull_distance"; // present for 4.5, but need extension control over block members +// #line and #include const char* const E_GL_GOOGLE_cpp_style_line_directive = "GL_GOOGLE_cpp_style_line_directive"; +const char* const E_GL_GOOGLE_include_directive = "GL_GOOGLE_include_directive"; // AEP const char* const E_GL_ANDROID_extension_pack_es31a = "GL_ANDROID_extension_pack_es31a"; diff --git a/glslang/MachineIndependent/preprocessor/Pp.cpp b/glslang/MachineIndependent/preprocessor/Pp.cpp index 279706ec..d62b7369 100644 --- a/glslang/MachineIndependent/preprocessor/Pp.cpp +++ b/glslang/MachineIndependent/preprocessor/Pp.cpp @@ -83,6 +83,7 @@ NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include #include #include @@ -596,6 +597,51 @@ int TPpContext::CPPifdef(int defined, TPpToken* ppToken) return token; } +// Handle #include +int TPpContext::CPPinclude(TPpToken* ppToken) +{ + const TSourceLoc directiveLoc = ppToken->loc; + int token = scanToken(ppToken); + if (token != PpAtomConstString) { + // TODO: handle angle brackets. + parseContext.ppError(directiveLoc, "must be followed by a file designation", "#include", ""); + } else { + // Make a copy of the name because it will be overwritten by the next token scan. + const std::string filename = ppToken->name; + token = scanToken(ppToken); + if (token != '\n' && token != EndOfInput) { + parseContext.ppError(ppToken->loc, "extra content after file designation", "#include", ""); + } else { + std::string sourceName; + std::string replacement; + std::tie(sourceName, replacement) = includer.include(filename.c_str()); + if (!sourceName.empty()) { + if (!replacement.empty()) { + const bool forNextLine = parseContext.lineDirectiveShouldSetNextLine(); + std::ostringstream content; + content << "#line " << forNextLine << " " << "\"" << sourceName << "\"\n"; + content << replacement << (replacement.back() == '\n' ? "" : "\n"); + content << "#line " << directiveLoc.line + forNextLine << " "; + if (directiveLoc.name != nullptr) { + content << "\"" << directiveLoc.name << "\""; + } else { + content << directiveLoc.string; + } + content << "\n"; + pushInput(new TokenizableString(directiveLoc, content.str(), this)); + } + // At EOF, there's no "current" location anymore. + if (token != EndOfInput) parseContext.setCurrentColumn(0); + // Don't accidentally return EndOfInput, which will end all preprocessing. + return '\n'; + } else { + parseContext.ppError(directiveLoc, replacement.c_str(), "#include", ""); + } + } + } + return token; +} + // Handle #line int TPpContext::CPPline(TPpToken* ppToken) { @@ -629,7 +675,7 @@ int TPpContext::CPPline(TPpToken* ppToken) if (token != '\n') { if (token == PpAtomConstString) { - parseContext.requireExtensions(directiveLoc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based #line"); + parseContext.ppRequireExtensions(directiveLoc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based #line"); // We need to save a copy of the string instead of pointing // to the name field of the token since the name field // will likely be overwritten by the next token scan. @@ -845,6 +891,10 @@ int TPpContext::readCPPline(TPpToken* ppToken) case PpAtomIfndef: token = CPPifdef(0, ppToken); break; + case PpAtomInclude: + parseContext.ppRequireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_include_directive, "#include"); + token = CPPinclude(ppToken); + break; case PpAtomLine: token = CPPline(ppToken); break; @@ -967,7 +1017,7 @@ int TPpContext::MacroExpand(int atom, TPpToken* ppToken, bool expandUndef, bool case PpAtomFileMacro: { if (const char* current_file = parseContext.getCurrentLoc().name) { - parseContext.requireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based __FILE__"); + parseContext.ppRequireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based __FILE__"); sprintf(ppToken->name, "\"%s\"", current_file); } else { ppToken->ival = parseContext.getCurrentLoc().string; diff --git a/glslang/MachineIndependent/preprocessor/PpAtom.cpp b/glslang/MachineIndependent/preprocessor/PpAtom.cpp index 04e0d630..b3cb2b07 100644 --- a/glslang/MachineIndependent/preprocessor/PpAtom.cpp +++ b/glslang/MachineIndependent/preprocessor/PpAtom.cpp @@ -120,6 +120,9 @@ const struct { { PpAtomLineMacro, "__LINE__" }, { PpAtomFileMacro, "__FILE__" }, { PpAtomVersionMacro, "__VERSION__" }, + + { PpAtomInclude, "include" }, + }; } // end anonymous namespace diff --git a/glslang/MachineIndependent/preprocessor/PpContext.cpp b/glslang/MachineIndependent/preprocessor/PpContext.cpp index 828764b0..b8d2c737 100644 --- a/glslang/MachineIndependent/preprocessor/PpContext.cpp +++ b/glslang/MachineIndependent/preprocessor/PpContext.cpp @@ -83,8 +83,8 @@ NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace glslang { -TPpContext::TPpContext(TParseContext& pc) : - preamble(0), strings(0), parseContext(pc), inComment(false) +TPpContext::TPpContext(TParseContext& pc, const TShader::Includer& inclr) : + preamble(0), strings(0), parseContext(pc), includer(inclr), inComment(false) { InitAtomTable(); InitScanner(); diff --git a/glslang/MachineIndependent/preprocessor/PpContext.h b/glslang/MachineIndependent/preprocessor/PpContext.h index 74a9f5dd..5d06e701 100644 --- a/glslang/MachineIndependent/preprocessor/PpContext.h +++ b/glslang/MachineIndependent/preprocessor/PpContext.h @@ -118,7 +118,7 @@ class TInputScanner; // Don't expect too much in terms of OO design. class TPpContext { public: - TPpContext(TParseContext&); + TPpContext(TParseContext&, const TShader::Includer&); virtual ~TPpContext(); void setPreamble(const char* preamble, size_t length); @@ -134,6 +134,10 @@ public: virtual int getch() = 0; virtual void ungetch() = 0; + // Will be called when we start reading tokens from this instance + virtual void notifyActivated() {} + // Will be called when we do not read tokens from this instance anymore + virtual void notifyDeleted() {} protected: bool done; TPpContext* pp; @@ -144,9 +148,11 @@ public: void pushInput(tInput* in) { inputStack.push_back(in); + in->notifyActivated(); } void popInput() { + inputStack.back()->notifyDeleted(); delete inputStack.back(); inputStack.pop_back(); } @@ -281,6 +287,8 @@ protected: // from Pp.cpp // TSourceLoc ifloc; /* outermost #if */ + // Used to obtain #include content. + const TShader::Includer& includer; int InitCPP(); int CPPdefine(TPpToken * ppToken); @@ -291,6 +299,7 @@ protected: int evalToToken(int token, bool shortCircuit, int& res, bool& err, TPpToken * ppToken); int CPPif (TPpToken * ppToken); int CPPifdef(int defined, TPpToken * ppToken); + int CPPinclude(TPpToken * ppToken); int CPPline(TPpToken * ppToken); int CPPerror(TPpToken * ppToken); int CPPpragma(TPpToken * ppToken); @@ -419,6 +428,51 @@ protected: TInputScanner* input; }; + // Holds a string that can be tokenized via the tInput interface. + class TokenizableString : public tInput { + public: + // Copies str, which must be non-empty. + TokenizableString(const TSourceLoc& startLoc, const std::string& str, TPpContext* pp) + : tInput(pp), + str_(str), + strings(str_.data()), + length(str_.size()), + scanner(1, &strings, &length), + prevScanner(nullptr), + stringInput(pp, scanner) { + scanner.setLine(startLoc.line); + scanner.setString(startLoc.string); + scanner.setFile(startLoc.name); + } + + // tInput methods: + int scan(TPpToken* t) override { return stringInput.scan(t); } + int getch() override { return stringInput.getch(); } + void ungetch() override { stringInput.ungetch(); } + + void notifyActivated() override + { + prevScanner = pp->parseContext.getScanner(); + pp->parseContext.setScanner(&scanner); + } + void notifyDeleted() override { pp->parseContext.setScanner(prevScanner); } + + private: + // Stores the titular string. + const std::string str_; + // Will point to str_[0] and be passed to scanner constructor. + const char* const strings; + // Length of str_, passed to scanner constructor. + size_t length; + // Scans over str_. + TInputScanner scanner; + // The previous effective scanner before the scanner in this instance + // has been activated. + TInputScanner* prevScanner; + // Delegate object implementing the tInput interface. + tStringInput stringInput; + }; + int InitScanner(); int ScanFromString(char* s); void missingEndifCheck(); diff --git a/glslang/MachineIndependent/preprocessor/PpTokens.h b/glslang/MachineIndependent/preprocessor/PpTokens.h index 0d116f9b..391b04ad 100644 --- a/glslang/MachineIndependent/preprocessor/PpTokens.h +++ b/glslang/MachineIndependent/preprocessor/PpTokens.h @@ -158,6 +158,9 @@ enum EFixedAtoms { PpAtomFileMacro, PpAtomVersionMacro, + // #include + PpAtomInclude, + PpAtomLast, }; diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h index edca638c..3fbfe354 100644 --- a/glslang/Public/ShaderLang.h +++ b/glslang/Public/ShaderLang.h @@ -249,6 +249,7 @@ SH_IMPORT_EXPORT int ShGetUniformLocation(const ShHandle uniformMap, const char* #include #include +#include class TCompiler; class TInfoSink; @@ -288,14 +289,35 @@ public: void setStringsWithLengthsAndNames( const char* const* s, const int* l, const char* const* names, int n); void setPreamble(const char* s) { preamble = s; } - bool parse(const TBuiltInResource*, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, bool forwardCompatible, EShMessages); + + // Interface to #include handlers. + class Includer { + public: + // On success, returns the full path and content of the file with the given + // filename that replaces "#include filename". On failure, returns an empty + // string and an error message. + virtual std::pair include(const char* filename) const = 0; + }; + + // Returns an error message for any #include directive. + class ForbidInclude : public Includer { + public: + std::pair include(const char* filename) const override + { + return std::make_pair("", "unexpected include directive"); + } + }; + + bool parse(const TBuiltInResource*, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, + bool forwardCompatible, EShMessages, const Includer& = ForbidInclude()); + // Equivalent to parse() without a default profile and without forcing defaults. // Provided for backwards compatibility. bool parse(const TBuiltInResource*, int defaultVersion, bool forwardCompatible, EShMessages); bool preprocess(const TBuiltInResource* builtInResources, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, - bool forwardCompatible, - EShMessages message, std::string* outputString); + bool forwardCompatible, EShMessages message, std::string* outputString, + const TShader::Includer& includer); const char* getInfoLog(); const char* getInfoDebugLog();