Commit c3bef3e7 by Jamie Madill Committed by Commit Bot

Allow 'defined' in define in non-WebGL.

This is needed to pass dEQP conformance. Several of the harder dEQP tests around this behaviour are excluded from the mustpass list. This is presumably because the behaviours weren't implemented portably. Nevertheless we need to support conformant behaviour for GLES 2.0 Contexts for the most simple uses. This also leaves the error behaviour intact for WebGL specs. Bug: angleproject:1335 Change-Id: Ia80b4f71475efa928488ee6c2ee35c566d4602d4 Reviewed-on: https://chromium-review.googlesource.com/c/1242013Reviewed-by: 's avatarOlli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 607f907d
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
// Version number for shader translation API. // Version number for shader translation API.
// It is incremented every time the API changes. // It is incremented every time the API changes.
#define ANGLE_SH_VERSION 200 #define ANGLE_SH_VERSION 201
enum ShShaderSpec enum ShShaderSpec
{ {
...@@ -626,6 +626,13 @@ GLenum GetGeometryShaderOutputPrimitiveType(const ShHandle handle); ...@@ -626,6 +626,13 @@ GLenum GetGeometryShaderOutputPrimitiveType(const ShHandle handle);
int GetGeometryShaderInvocations(const ShHandle handle); int GetGeometryShaderInvocations(const ShHandle handle);
int GetGeometryShaderMaxVertices(const ShHandle handle); int GetGeometryShaderMaxVertices(const ShHandle handle);
//
// Helper function to identify specs that are based on the WebGL spec.
//
inline bool IsWebGLBasedSpec(ShShaderSpec spec)
{
return (spec == SH_WEBGL_SPEC || spec == SH_WEBGL2_SPEC || spec == SH_WEBGL3_SPEC);
}
} // namespace sh } // namespace sh
#endif // GLSLANG_SHADERLANG_H_ #endif // GLSLANG_SHADERLANG_H_
...@@ -141,72 +141,11 @@ bool isMacroPredefined(const std::string &name, const pp::MacroSet &macroSet) ...@@ -141,72 +141,11 @@ bool isMacroPredefined(const std::string &name, const pp::MacroSet &macroSet)
namespace pp namespace pp
{ {
class DefinedParser : public Lexer
{
public:
DefinedParser(Lexer *lexer, const MacroSet *macroSet, Diagnostics *diagnostics)
: mLexer(lexer), mMacroSet(macroSet), mDiagnostics(diagnostics)
{
}
protected:
void lex(Token *token) override
{
const char kDefined[] = "defined";
mLexer->lex(token);
if (token->type != Token::IDENTIFIER)
return;
if (token->text != kDefined)
return;
bool paren = false;
mLexer->lex(token);
if (token->type == '(')
{
paren = true;
mLexer->lex(token);
}
if (token->type != Token::IDENTIFIER)
{
mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text);
skipUntilEOD(mLexer, token);
return;
}
MacroSet::const_iterator iter = mMacroSet->find(token->text);
std::string expression = iter != mMacroSet->end() ? "1" : "0";
if (paren)
{
mLexer->lex(token);
if (token->type != ')')
{
mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
token->text);
skipUntilEOD(mLexer, token);
return;
}
}
// We have a valid defined operator.
// Convert the current token into a CONST_INT token.
token->type = Token::CONST_INT;
token->text = expression;
}
private:
Lexer *mLexer;
const MacroSet *mMacroSet;
Diagnostics *mDiagnostics;
};
DirectiveParser::DirectiveParser(Tokenizer *tokenizer, DirectiveParser::DirectiveParser(Tokenizer *tokenizer,
MacroSet *macroSet, MacroSet *macroSet,
Diagnostics *diagnostics, Diagnostics *diagnostics,
DirectiveHandler *directiveHandler, DirectiveHandler *directiveHandler,
int maxMacroExpansionDepth) const PreprocessorSettings &settings)
: mPastFirstStatement(false), : mPastFirstStatement(false),
mSeenNonPreprocessorToken(false), mSeenNonPreprocessorToken(false),
mTokenizer(tokenizer), mTokenizer(tokenizer),
...@@ -214,7 +153,7 @@ DirectiveParser::DirectiveParser(Tokenizer *tokenizer, ...@@ -214,7 +153,7 @@ DirectiveParser::DirectiveParser(Tokenizer *tokenizer,
mDiagnostics(diagnostics), mDiagnostics(diagnostics),
mDirectiveHandler(directiveHandler), mDirectiveHandler(directiveHandler),
mShaderVersion(100), mShaderVersion(100),
mMaxMacroExpansionDepth(maxMacroExpansionDepth) mSettings(settings)
{ {
} }
...@@ -843,7 +782,7 @@ void DirectiveParser::parseLine(Token *token) ...@@ -843,7 +782,7 @@ void DirectiveParser::parseLine(Token *token)
bool parsedFileNumber = false; bool parsedFileNumber = false;
int line = 0, file = 0; int line = 0, file = 0;
MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, mMaxMacroExpansionDepth); MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, mSettings, false);
// Lex the first token after "#line" so we can check it for EOD. // Lex the first token after "#line" so we can check it for EOD.
macroExpander.lex(token); macroExpander.lex(token);
...@@ -951,8 +890,7 @@ int DirectiveParser::parseExpressionIf(Token *token) ...@@ -951,8 +890,7 @@ int DirectiveParser::parseExpressionIf(Token *token)
{ {
ASSERT((getDirective(token) == DIRECTIVE_IF) || (getDirective(token) == DIRECTIVE_ELIF)); ASSERT((getDirective(token) == DIRECTIVE_IF) || (getDirective(token) == DIRECTIVE_ELIF));
DefinedParser definedParser(mTokenizer, mMacroSet, mDiagnostics); MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, mSettings, true);
MacroExpander macroExpander(&definedParser, mMacroSet, mDiagnostics, mMaxMacroExpansionDepth);
ExpressionParser expressionParser(&macroExpander, mDiagnostics); ExpressionParser expressionParser(&macroExpander, mDiagnostics);
int expression = 0; int expression = 0;
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "compiler/preprocessor/Lexer.h" #include "compiler/preprocessor/Lexer.h"
#include "compiler/preprocessor/Macro.h" #include "compiler/preprocessor/Macro.h"
#include "compiler/preprocessor/Preprocessor.h"
#include "compiler/preprocessor/SourceLocation.h" #include "compiler/preprocessor/SourceLocation.h"
namespace angle namespace angle
...@@ -28,7 +29,7 @@ class DirectiveParser : public Lexer ...@@ -28,7 +29,7 @@ class DirectiveParser : public Lexer
MacroSet *macroSet, MacroSet *macroSet,
Diagnostics *diagnostics, Diagnostics *diagnostics,
DirectiveHandler *directiveHandler, DirectiveHandler *directiveHandler,
int maxMacroExpansionDepth); const PreprocessorSettings &settings);
~DirectiveParser() override; ~DirectiveParser() override;
void lex(Token *token) override; void lex(Token *token) override;
...@@ -78,7 +79,7 @@ class DirectiveParser : public Lexer ...@@ -78,7 +79,7 @@ class DirectiveParser : public Lexer
Diagnostics *mDiagnostics; Diagnostics *mDiagnostics;
DirectiveHandler *mDirectiveHandler; DirectiveHandler *mDirectiveHandler;
int mShaderVersion; int mShaderVersion;
int mMaxMacroExpansionDepth; const PreprocessorSettings mSettings;
}; };
} // namespace pp } // namespace pp
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "compiler/preprocessor/MacroExpander.h" #include "compiler/preprocessor/MacroExpander.h"
#include <GLSLANG/ShaderLang.h>
#include <algorithm> #include <algorithm>
#include "common/debug.h" #include "common/debug.h"
...@@ -86,12 +87,14 @@ MacroExpander::ScopedMacroReenabler::~ScopedMacroReenabler() ...@@ -86,12 +87,14 @@ MacroExpander::ScopedMacroReenabler::~ScopedMacroReenabler()
MacroExpander::MacroExpander(Lexer *lexer, MacroExpander::MacroExpander(Lexer *lexer,
MacroSet *macroSet, MacroSet *macroSet,
Diagnostics *diagnostics, Diagnostics *diagnostics,
int allowedMacroExpansionDepth) const PreprocessorSettings &settings,
bool parseDefined)
: mLexer(lexer), : mLexer(lexer),
mMacroSet(macroSet), mMacroSet(macroSet),
mDiagnostics(diagnostics), mDiagnostics(diagnostics),
mParseDefined(parseDefined),
mTotalTokensInContexts(0), mTotalTokensInContexts(0),
mAllowedMacroExpansionDepth(allowedMacroExpansionDepth), mSettings(settings),
mDeferReenablingMacros(false) mDeferReenablingMacros(false)
{ {
} }
...@@ -114,6 +117,51 @@ void MacroExpander::lex(Token *token) ...@@ -114,6 +117,51 @@ void MacroExpander::lex(Token *token)
if (token->type != Token::IDENTIFIER) if (token->type != Token::IDENTIFIER)
break; break;
// Defined operator is parsed here since it may be generated by macro expansion.
// Defined operator produced by macro expansion has undefined behavior according to C++
// spec, which the GLSL spec references (see C++14 draft spec section 16.1.4), but this
// behavior is needed for passing dEQP tests, which enforce stricter compatibility between
// implementations.
if (mParseDefined && token->text == kDefined)
{
// Defined inside a macro is forbidden in WebGL.
if (!mContextStack.empty() && sh::IsWebGLBasedSpec(mSettings.shaderSpec))
break;
bool paren = false;
getToken(token);
if (token->type == '(')
{
paren = true;
getToken(token);
}
if (token->type != Token::IDENTIFIER)
{
mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
token->text);
break;
}
auto iter = mMacroSet->find(token->text);
std::string expression = iter != mMacroSet->end() ? "1" : "0";
if (paren)
{
getToken(token);
if (token->type != ')')
{
mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
token->text);
break;
}
}
// We have a valid defined operator.
// Convert the current token into a CONST_INT token.
token->type = Token::CONST_INT;
token->text = expression;
break;
}
if (token->expansionDisabled()) if (token->expansionDisabled())
break; break;
...@@ -386,13 +434,15 @@ bool MacroExpander::collectMacroArgs(const Macro &macro, ...@@ -386,13 +434,15 @@ bool MacroExpander::collectMacroArgs(const Macro &macro,
for (auto &arg : *args) for (auto &arg : *args)
{ {
TokenLexer lexer(&arg); TokenLexer lexer(&arg);
if (mAllowedMacroExpansionDepth < 1) if (mSettings.maxMacroExpansionDepth < 1)
{ {
mDiagnostics->report(Diagnostics::PP_MACRO_INVOCATION_CHAIN_TOO_DEEP, token.location, mDiagnostics->report(Diagnostics::PP_MACRO_INVOCATION_CHAIN_TOO_DEEP, token.location,
token.text); token.text);
return false; return false;
} }
MacroExpander expander(&lexer, mMacroSet, mDiagnostics, mAllowedMacroExpansionDepth - 1); PreprocessorSettings nestedSettings(mSettings.shaderSpec);
nestedSettings.maxMacroExpansionDepth = mSettings.maxMacroExpansionDepth - 1;
MacroExpander expander(&lexer, mMacroSet, mDiagnostics, nestedSettings, mParseDefined);
arg.clear(); arg.clear();
expander.lex(&token); expander.lex(&token);
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "compiler/preprocessor/Lexer.h" #include "compiler/preprocessor/Lexer.h"
#include "compiler/preprocessor/Macro.h" #include "compiler/preprocessor/Macro.h"
#include "compiler/preprocessor/Preprocessor.h"
namespace angle namespace angle
{ {
...@@ -28,7 +29,8 @@ class MacroExpander : public Lexer ...@@ -28,7 +29,8 @@ class MacroExpander : public Lexer
MacroExpander(Lexer *lexer, MacroExpander(Lexer *lexer,
MacroSet *macroSet, MacroSet *macroSet,
Diagnostics *diagnostics, Diagnostics *diagnostics,
int allowedMacroExpansionDepth); const PreprocessorSettings &settings,
bool parseDefined);
~MacroExpander() override; ~MacroExpander() override;
void lex(Token *token) override; void lex(Token *token) override;
...@@ -68,12 +70,13 @@ class MacroExpander : public Lexer ...@@ -68,12 +70,13 @@ class MacroExpander : public Lexer
Lexer *mLexer; Lexer *mLexer;
MacroSet *mMacroSet; MacroSet *mMacroSet;
Diagnostics *mDiagnostics; Diagnostics *mDiagnostics;
bool mParseDefined;
std::unique_ptr<Token> mReserveToken; std::unique_ptr<Token> mReserveToken;
std::vector<MacroContext *> mContextStack; std::vector<MacroContext *> mContextStack;
size_t mTotalTokensInContexts; size_t mTotalTokensInContexts;
int mAllowedMacroExpansionDepth; PreprocessorSettings mSettings;
bool mDeferReenablingMacros; bool mDeferReenablingMacros;
std::vector<std::shared_ptr<Macro>> mMacrosToReenable; std::vector<std::shared_ptr<Macro>> mMacrosToReenable;
......
...@@ -33,12 +33,8 @@ struct PreprocessorImpl ...@@ -33,12 +33,8 @@ struct PreprocessorImpl
const PreprocessorSettings &settings) const PreprocessorSettings &settings)
: diagnostics(diag), : diagnostics(diag),
tokenizer(diag), tokenizer(diag),
directiveParser(&tokenizer, directiveParser(&tokenizer, &macroSet, diag, directiveHandler, settings),
&macroSet, macroExpander(&directiveParser, &macroSet, diag, settings, false)
diag,
directiveHandler,
settings.maxMacroExpansionDepth),
macroExpander(&directiveParser, &macroSet, diag, settings.maxMacroExpansionDepth)
{ {
} }
}; };
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <cstddef> #include <cstddef>
#include "GLSLANG/ShaderLang.h"
#include "common/angleutils.h" #include "common/angleutils.h"
namespace angle namespace angle
...@@ -22,10 +23,17 @@ class DirectiveHandler; ...@@ -22,10 +23,17 @@ class DirectiveHandler;
struct PreprocessorImpl; struct PreprocessorImpl;
struct Token; struct Token;
struct PreprocessorSettings : private angle::NonCopyable struct PreprocessorSettings final
{ {
PreprocessorSettings() : maxMacroExpansionDepth(1000) {} PreprocessorSettings(ShShaderSpec shaderSpec)
: maxMacroExpansionDepth(1000), shaderSpec(shaderSpec)
{
}
PreprocessorSettings(const PreprocessorSettings &other) = default;
int maxMacroExpansionDepth; int maxMacroExpansionDepth;
ShShaderSpec shaderSpec;
}; };
class Preprocessor : angle::NonCopyable class Preprocessor : angle::NonCopyable
......
...@@ -106,6 +106,8 @@ inline bool operator!=(const Token &lhs, const Token &rhs) ...@@ -106,6 +106,8 @@ inline bool operator!=(const Token &lhs, const Token &rhs)
std::ostream &operator<<(std::ostream &out, const Token &token); std::ostream &operator<<(std::ostream &out, const Token &token);
constexpr char kDefined[] = "defined";
} // namespace pp } // namespace pp
} // namespace angle } // namespace angle
......
...@@ -92,11 +92,6 @@ void DumpFuzzerCase(char const *const *shaderStrings, ...@@ -92,11 +92,6 @@ void DumpFuzzerCase(char const *const *shaderStrings,
#endif // defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT) #endif // defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT)
} // anonymous namespace } // anonymous namespace
bool IsWebGLBasedSpec(ShShaderSpec spec)
{
return (spec == SH_WEBGL_SPEC || spec == SH_WEBGL2_SPEC || spec == SH_WEBGL3_SPEC);
}
bool IsGLSL130OrNewer(ShShaderOutput output) bool IsGLSL130OrNewer(ShShaderOutput output)
{ {
return (output == SH_GLSL_130_OUTPUT || output == SH_GLSL_140_OUTPUT || return (output == SH_GLSL_130_OUTPUT || output == SH_GLSL_140_OUTPUT ||
......
...@@ -36,11 +36,6 @@ class TranslatorHLSL; ...@@ -36,11 +36,6 @@ class TranslatorHLSL;
#endif // ANGLE_ENABLE_HLSL #endif // ANGLE_ENABLE_HLSL
// //
// Helper function to identify specs that are based on the WebGL spec.
//
bool IsWebGLBasedSpec(ShShaderSpec spec);
//
// Helper function to check if the shader type is GLSL. // Helper function to check if the shader type is GLSL.
// //
bool IsGLSL130OrNewer(ShShaderOutput output); bool IsGLSL130OrNewer(ShShaderOutput output);
......
...@@ -194,7 +194,7 @@ TParseContext::TParseContext(TSymbolTable &symt, ...@@ -194,7 +194,7 @@ TParseContext::TParseContext(TSymbolTable &symt,
mShaderVersion, mShaderVersion,
mShaderType, mShaderType,
resources.WEBGL_debug_shader_precision == 1), resources.WEBGL_debug_shader_precision == 1),
mPreprocessor(mDiagnostics, &mDirectiveHandler, angle::pp::PreprocessorSettings()), mPreprocessor(mDiagnostics, &mDirectiveHandler, angle::pp::PreprocessorSettings(spec)),
mScanner(nullptr), mScanner(nullptr),
mMinProgramTexelOffset(resources.MinProgramTexelOffset), mMinProgramTexelOffset(resources.MinProgramTexelOffset),
mMaxProgramTexelOffset(resources.MaxProgramTexelOffset), mMaxProgramTexelOffset(resources.MaxProgramTexelOffset),
......
...@@ -30,9 +30,6 @@ ...@@ -30,9 +30,6 @@
998 DEBUG RELEASE : dEQP-GLES2.performance.* = SKIP 998 DEBUG RELEASE : dEQP-GLES2.performance.* = SKIP
998 DEBUG RELEASE : dEQP-GLES2.stress.* = SKIP 998 DEBUG RELEASE : dEQP-GLES2.stress.* = SKIP
// Tests that we fail because they're not in line with the WebGL spec
1335 DEBUG RELEASE : dEQP-GLES2.functional.shaders.preprocessor.conditional_inclusion.basic_2* = FAIL
// Failures related to not supporting separate depth/stencil masks on D3D11. // Failures related to not supporting separate depth/stencil masks on D3D11.
1655 D3D11 : dEQP-GLES2.functional.fragment_ops.depth_stencil.stencil_depth_funcs.stencil_* = FAIL 1655 D3D11 : dEQP-GLES2.functional.fragment_ops.depth_stencil.stencil_depth_funcs.stencil_* = FAIL
1655 D3D11 : dEQP-GLES2.functional.fragment_ops.depth_stencil.stencil_ops.* = FAIL 1655 D3D11 : dEQP-GLES2.functional.fragment_ops.depth_stencil.stencil_ops.* = FAIL
......
...@@ -40,13 +40,6 @@ ...@@ -40,13 +40,6 @@
// We can't support distinct texture sizes in D3D11. // We can't support distinct texture sizes in D3D11.
1097 WIN : dEQP-GLES3.functional.fbo.completeness.size.distinct = FAIL 1097 WIN : dEQP-GLES3.functional.fbo.completeness.size.distinct = FAIL
// Tests that we fail because they're not in line with the WebGL spec
1335 MAC WIN LINUX : dEQP-GLES3.functional.shaders.preprocessor.conditional_inclusion.basic_2* = FAIL
1335 MAC WIN LINUX : dEQP-GLES3.functional.shaders.preprocessor.conditional_inclusion.defined_macro_defined_test* = FAIL
1335 MAC WIN LINUX : dEQP-GLES3.functional.shaders.preprocessor.conditional_inclusion.defined_macro_undef* = FAIL
1335 MAC WIN LINUX : dEQP-GLES3.functional.shaders.preprocessor.conditional_inclusion.define_defined* = FAIL
1335 MAC WIN LINUX : dEQP-GLES3.functional.shaders.preprocessor.conditional_inclusion.define_defined_outside_if* = FAIL
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// Temporary entries: they should be removed once the bugs are fixed. // Temporary entries: they should be removed once the bugs are fixed.
......
...@@ -482,6 +482,7 @@ class GLSLTest : public ANGLETest ...@@ -482,6 +482,7 @@ class GLSLTest : public ANGLETest
glDeleteShader(fs); glDeleteShader(fs);
const std::string &errorMessage = QueryErrorMessage(program); const std::string &errorMessage = QueryErrorMessage(program);
printf("%s\n", errorMessage.c_str());
EXPECT_NE(std::string::npos, errorMessage.find(expectedErrorType)); EXPECT_NE(std::string::npos, errorMessage.find(expectedErrorType));
EXPECT_NE(std::string::npos, errorMessage.find(expectedVariableFullName)); EXPECT_NE(std::string::npos, errorMessage.find(expectedVariableFullName));
...@@ -4938,6 +4939,219 @@ void main() ...@@ -4938,6 +4939,219 @@ void main()
kQuarterSize, kQuarterSize * 2)); kQuarterSize, kQuarterSize * 2));
} }
// Ensure that using defined in a macro works in this simple case. This mirrors a dEQP test.
TEST_P(GLSLTest, DefinedInMacroSucceeds)
{
constexpr char kVS[] = R"(precision mediump float;
attribute highp vec4 position;
varying vec2 out0;
void main()
{
#define AAA defined(BBB)
#if !AAA
out0 = vec2(0.0, 1.0);
#else
out0 = vec2(1.0, 0.0);
#endif
gl_Position = position;
})";
constexpr char kFS[] = R"(precision mediump float;
varying vec2 out0;
void main()
{
gl_FragColor = vec4(out0, 0, 1);
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
drawQuad(program, "position", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Validate the defined operator is evaluated when the macro is called, not when defined.
TEST_P(GLSLTest, DefinedInMacroWithUndef)
{
constexpr char kVS[] = R"(precision mediump float;
attribute highp vec4 position;
varying vec2 out0;
void main()
{
#define BBB 1
#define AAA defined(BBB)
#undef BBB
#if AAA
out0 = vec2(1.0, 0.0);
#else
out0 = vec2(0.0, 1.0);
#endif
gl_Position = position;
})";
constexpr char kFS[] = R"(precision mediump float;
varying vec2 out0;
void main()
{
gl_FragColor = vec4(out0, 0, 1);
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
drawQuad(program, "position", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Validate the defined operator is evaluated when the macro is called, not when defined.
TEST_P(GLSLTest, DefinedAfterMacroUsage)
{
constexpr char kVS[] = R"(precision mediump float;
attribute highp vec4 position;
varying vec2 out0;
void main()
{
#define AAA defined(BBB)
#define BBB 1
#if AAA
out0 = vec2(0.0, 1.0);
#else
out0 = vec2(1.0, 0.0);
#endif
gl_Position = position;
})";
constexpr char kFS[] = R"(precision mediump float;
varying vec2 out0;
void main()
{
gl_FragColor = vec4(out0, 0, 1);
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
drawQuad(program, "position", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Test generating "defined" by concatenation when a macro is called. This is not allowed.
TEST_P(GLSLTest, DefinedInMacroConcatenationNotAllowed)
{
constexpr char kVS[] = R"(precision mediump float;
attribute highp vec4 position;
varying vec2 out0;
void main()
{
#define BBB 1
#define AAA(defi, ned) defi ## ned(BBB)
#if AAA(defi, ned)
out0 = vec2(0.0, 1.0);
#else
out0 = vec2(1.0, 0.0);
#endif
gl_Position = position;
})";
constexpr char kFS[] = R"(precision mediump float;
varying vec2 out0;
void main()
{
gl_FragColor = vec4(out0, 0, 1);
})";
GLuint program = CompileProgram(kVS, kFS);
EXPECT_EQ(0u, program);
glDeleteProgram(program);
}
// Test using defined in a macro parameter name. This is not allowed.
TEST_P(GLSLTest, DefinedAsParameterNameNotAllowed)
{
constexpr char kVS[] = R"(precision mediump float;
attribute highp vec4 position;
varying vec2 out0;
void main()
{
#define BBB 1
#define AAA(defined) defined(BBB)
#if AAA(defined)
out0 = vec2(0.0, 1.0);
#else
out0 = vec2(1.0, 0.0);
#endif
gl_Position = position;
})";
constexpr char kFS[] = R"(precision mediump float;
varying vec2 out0;
void main()
{
gl_FragColor = vec4(out0, 0, 1);
})";
GLuint program = CompileProgram(kVS, kFS);
EXPECT_EQ(0u, program);
glDeleteProgram(program);
}
// Ensure that defined in a macro is no accepted in WebGL.
TEST_P(WebGLGLSLTest, DefinedInMacroFails)
{
constexpr char kVS[] = R"(precision mediump float;
attribute highp vec4 position;
varying float out0;
void main()
{
#define AAA defined(BBB)
#if !AAA
out0 = 1.0;
#else
out0 = 0.0;
#endif
gl_Position = dEQP_Position;
})";
constexpr char kFS[] = R"(precision mediump float;
varying float out0;
void main()
{
gl_FragColor = vec4(out0, 0, 0, 1);
})";
GLuint program = CompileProgram(kVS, kFS);
EXPECT_EQ(0u, program);
glDeleteProgram(program);
}
// Simple test using a define macro in WebGL.
TEST_P(WebGLGLSLTest, DefinedGLESSymbol)
{
constexpr char kVS[] = R"(void main()
{
gl_Position = vec4(1, 0, 0, 1);
})";
constexpr char kFS[] = R"(#if defined(GL_ES)
precision mediump float;
void main()
{
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
}
#else
foo
#endif
)";
ANGLE_GL_PROGRAM(program, kVS, kFS);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these // Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against. // tests should be run against.
ANGLE_INSTANTIATE_TEST(GLSLTest, ANGLE_INSTANTIATE_TEST(GLSLTest,
......
...@@ -40,12 +40,13 @@ void SimplePreprocessorTest::preprocess(const char *input, const pp::Preprocesso ...@@ -40,12 +40,13 @@ void SimplePreprocessorTest::preprocess(const char *input, const pp::Preprocesso
void SimplePreprocessorTest::preprocess(const char *input) void SimplePreprocessorTest::preprocess(const char *input)
{ {
preprocess(input, pp::PreprocessorSettings()); preprocess(input, pp::PreprocessorSettings(SH_GLES2_SPEC));
} }
void SimplePreprocessorTest::preprocess(const char *input, const char *expected) void SimplePreprocessorTest::preprocess(const char *input, const char *expected)
{ {
pp::Preprocessor preprocessor(&mDiagnostics, &mDirectiveHandler, pp::PreprocessorSettings()); pp::Preprocessor preprocessor(&mDiagnostics, &mDirectiveHandler,
pp::PreprocessorSettings(SH_GLES2_SPEC));
std::stringstream output; std::stringstream output;
preprocess(input, &output, &preprocessor); preprocess(input, &output, &preprocessor);
...@@ -55,7 +56,8 @@ void SimplePreprocessorTest::preprocess(const char *input, const char *expected) ...@@ -55,7 +56,8 @@ void SimplePreprocessorTest::preprocess(const char *input, const char *expected)
void SimplePreprocessorTest::lexSingleToken(const char *input, pp::Token *token) void SimplePreprocessorTest::lexSingleToken(const char *input, pp::Token *token)
{ {
pp::Preprocessor preprocessor(&mDiagnostics, &mDirectiveHandler, pp::PreprocessorSettings()); pp::Preprocessor preprocessor(&mDiagnostics, &mDirectiveHandler,
pp::PreprocessorSettings(SH_GLES2_SPEC));
ASSERT_TRUE(preprocessor.init(1, &input, nullptr)); ASSERT_TRUE(preprocessor.init(1, &input, nullptr));
preprocessor.lex(token); preprocessor.lex(token);
} }
...@@ -64,7 +66,8 @@ void SimplePreprocessorTest::lexSingleToken(size_t count, ...@@ -64,7 +66,8 @@ void SimplePreprocessorTest::lexSingleToken(size_t count,
const char *const input[], const char *const input[],
pp::Token *token) pp::Token *token)
{ {
pp::Preprocessor preprocessor(&mDiagnostics, &mDirectiveHandler, pp::PreprocessorSettings()); pp::Preprocessor preprocessor(&mDiagnostics, &mDirectiveHandler,
pp::PreprocessorSettings(SH_GLES2_SPEC));
ASSERT_TRUE(preprocessor.init(count, input, nullptr)); ASSERT_TRUE(preprocessor.init(count, input, nullptr));
preprocessor.lex(token); preprocessor.lex(token);
} }
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "MockDiagnostics.h" #include "MockDiagnostics.h"
#include "MockDirectiveHandler.h" #include "MockDirectiveHandler.h"
#include "compiler/preprocessor/Preprocessor.h" #include "compiler/preprocessor/Preprocessor.h"
#include "compiler/preprocessor/SourceLocation.h"
#ifndef PREPROCESSOR_TESTS_PREPROCESSOR_TEST_H_ #ifndef PREPROCESSOR_TESTS_PREPROCESSOR_TEST_H_
#define PREPROCESSOR_TESTS_PREPROCESSOR_TEST_H_ #define PREPROCESSOR_TESTS_PREPROCESSOR_TEST_H_
...@@ -19,8 +20,8 @@ namespace angle ...@@ -19,8 +20,8 @@ namespace angle
class PreprocessorTest : public testing::Test class PreprocessorTest : public testing::Test
{ {
protected: protected:
PreprocessorTest() PreprocessorTest(ShShaderSpec shaderSpec)
: mPreprocessor(&mDiagnostics, &mDirectiveHandler, pp::PreprocessorSettings()) : mPreprocessor(&mDiagnostics, &mDirectiveHandler, pp::PreprocessorSettings(shaderSpec))
{ {
} }
...@@ -52,4 +53,9 @@ class SimplePreprocessorTest : public testing::Test ...@@ -52,4 +53,9 @@ class SimplePreprocessorTest : public testing::Test
} // namespace angle } // namespace angle
inline std::ostream &operator<<(std::ostream &os, const angle::pp::SourceLocation &sourceLoc)
{
return os << "(" << sourceLoc.file << ":" << sourceLoc.line << ")";
}
#endif // PREPROCESSOR_TESTS_PREPROCESSOR_TEST_H_ #endif // PREPROCESSOR_TESTS_PREPROCESSOR_TEST_H_
...@@ -16,6 +16,8 @@ namespace angle ...@@ -16,6 +16,8 @@ namespace angle
class CharTest : public PreprocessorTest, class CharTest : public PreprocessorTest,
public testing::WithParamInterface<int> public testing::WithParamInterface<int>
{ {
public:
CharTest() : PreprocessorTest(SH_GLES2_SPEC) {}
}; };
static const char kPunctuators[] = { static const char kPunctuators[] = {
......
...@@ -1027,7 +1027,7 @@ TEST_F(DefineTest, LongMacroInvocationChain) ...@@ -1027,7 +1027,7 @@ TEST_F(DefineTest, LongMacroInvocationChain)
EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_MACRO_INVOCATION_CHAIN_TOO_DEEP, EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_MACRO_INVOCATION_CHAIN_TOO_DEEP,
pp::SourceLocation(0, 22), _)); pp::SourceLocation(0, 22), _));
pp::PreprocessorSettings settings; pp::PreprocessorSettings settings(SH_GLES2_SPEC);
settings.maxMacroExpansionDepth = 19; settings.maxMacroExpansionDepth = 19;
preprocess(inputStream.str().c_str(), settings); preprocess(inputStream.str().c_str(), settings);
......
...@@ -874,17 +874,95 @@ TEST_F(IfTest, DefinedOperatorValidAfterMacroExpansion) ...@@ -874,17 +874,95 @@ TEST_F(IfTest, DefinedOperatorValidAfterMacroExpansion)
"pass\n" "pass\n"
"#endif\n"; "#endif\n";
preprocess(str, "\n\npass\n\n");
}
// Validate the defined operator is evaluated when the macro is called, not when defined.
TEST_F(IfTest, DefinedOperatorValidWhenUsed)
{
constexpr char str[] = R"(#define BBB 1
#define AAA defined(BBB)
#undef BBB
#if !AAA
pass
#endif
)";
preprocess(str, "\n\n\n\n\npass\n\n");
}
// Validate the defined operator is evaluated when the macro is called, not when defined.
TEST_F(IfTest, DefinedOperatorAfterMacro)
{
constexpr char str[] = R"(#define AAA defined(BBB)
#define BBB 1
#if AAA
pass
#endif
)";
preprocess(str, "\n\n\n\npass\n\n");
}
// Test generating "defined" by concatenation when a macro is called. This is not allowed.
TEST_F(IfTest, DefinedInMacroConcatenationNotAllowed)
{
constexpr char str[] = R"(#define BBB 1
#define AAA(defi, ned) defi ## ned(BBB)
#if !AAA(defi, ned)
pass
#endif
)";
EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
pp::SourceLocation(0, 4), "defi"));
EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
pp::SourceLocation(0, 4), "#"));
preprocess(str);
}
// Test using defined in a macro parameter name. This is not allowed.
TEST_F(IfTest, DefinedAsParameterNameNotAllowed)
{
constexpr char str[] = R"(#define BBB 1
#define AAA(defined) defined(BBB)
#if AAA(defined)
pass
#endif
)";
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::PP_UNEXPECTED_TOKEN, pp::SourceLocation(0, 0), ""));
preprocess(str);
}
// This behavour is disabled in WebGL.
TEST_F(IfTest, DefinedOperatorInvalidAfterMacroExpansionInWebGL)
{
const char *str =
"#define foo defined\n"
"#if !foo bar\n"
"pass\n"
"#endif\n";
EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
pp::SourceLocation(0, 2), "defined")); pp::SourceLocation(0, 2), "defined"));
EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
pp::SourceLocation(0, 2), "bar")); pp::SourceLocation(0, 2), "bar"));
preprocess(str); preprocess(str, pp::PreprocessorSettings(SH_WEBGL_SPEC));
} }
// Defined operator produced by macro expansion has undefined behavior according to C++ spec, // Defined operator produced by macro expansion has undefined behavior according to C++ spec,
// which the GLSL spec references (see C++14 draft spec section 16.1.4), but this behavior is // which the GLSL spec references (see C++14 draft spec section 16.1.4). Some edge case
// needed for passing dEQP tests, which enforce stricter compatibility between implementations. // behaviours with defined are not portable between implementations and thus are not required
// to pass dEQP Tests.
TEST_F(IfTest, UnterminatedDefinedInMacro) TEST_F(IfTest, UnterminatedDefinedInMacro)
{ {
const char *str = const char *str =
...@@ -892,17 +970,18 @@ TEST_F(IfTest, UnterminatedDefinedInMacro) ...@@ -892,17 +970,18 @@ TEST_F(IfTest, UnterminatedDefinedInMacro)
"#if foo\n" "#if foo\n"
"#endif\n"; "#endif\n";
EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, EXPECT_CALL(mDiagnostics,
pp::SourceLocation(0, 2), "defined")); print(pp::Diagnostics::PP_UNEXPECTED_TOKEN, pp::SourceLocation(0, 2), "\n"));
EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_INVALID_EXPRESSION,
pp::SourceLocation(0, 2), "(")); pp::SourceLocation(0, 2), "syntax error"));
preprocess(str); preprocess(str);
} }
// Defined operator produced by macro expansion has undefined behavior according to C++ spec, // Defined operator produced by macro expansion has undefined behavior according to C++ spec,
// which the GLSL spec references (see C++14 draft spec section 16.1.4), but this behavior is // which the GLSL spec references (see C++14 draft spec section 16.1.4). Some edge case
// needed for passing dEQP tests, which enforce stricter compatibility between implementations. // behaviours with defined are not portable between implementations and thus are not required
// to pass dEQP Tests.
TEST_F(IfTest, UnterminatedDefinedInMacro2) TEST_F(IfTest, UnterminatedDefinedInMacro2)
{ {
const char *str = const char *str =
...@@ -910,10 +989,10 @@ TEST_F(IfTest, UnterminatedDefinedInMacro2) ...@@ -910,10 +989,10 @@ TEST_F(IfTest, UnterminatedDefinedInMacro2)
"#if foo\n" "#if foo\n"
"#endif\n"; "#endif\n";
EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, EXPECT_CALL(mDiagnostics,
pp::SourceLocation(0, 2), "defined")); print(pp::Diagnostics::PP_UNEXPECTED_TOKEN, pp::SourceLocation(0, 2), "\n"));
EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_INVALID_EXPRESSION,
pp::SourceLocation(0, 2), "(")); pp::SourceLocation(0, 2), "syntax error"));
preprocess(str); preprocess(str);
} }
......
...@@ -13,6 +13,8 @@ namespace angle ...@@ -13,6 +13,8 @@ namespace angle
class InitTest : public PreprocessorTest class InitTest : public PreprocessorTest
{ {
public:
InitTest() : PreprocessorTest(SH_GLES2_SPEC) {}
}; };
TEST_F(InitTest, ZeroCount) TEST_F(InitTest, ZeroCount)
......
...@@ -13,20 +13,22 @@ namespace angle ...@@ -13,20 +13,22 @@ namespace angle
class LocationTest : public PreprocessorTest class LocationTest : public PreprocessorTest
{ {
protected: protected:
void expectLocation(int count, LocationTest() : PreprocessorTest(SH_GLES2_SPEC) {}
const char* const string[],
const int length[], void expectLocation(int count,
const pp::SourceLocation& location) const char *const string[],
{ const int length[],
ASSERT_TRUE(mPreprocessor.init(count, string, length)); const pp::SourceLocation &location)
{
pp::Token token; ASSERT_TRUE(mPreprocessor.init(count, string, length));
mPreprocessor.lex(&token);
EXPECT_EQ(pp::Token::IDENTIFIER, token.type); pp::Token token;
EXPECT_EQ("foo", token.text); mPreprocessor.lex(&token);
EXPECT_EQ(pp::Token::IDENTIFIER, token.type);
EXPECT_EQ(location.file, token.location.file); EXPECT_EQ("foo", token.text);
EXPECT_EQ(location.line, token.location.line);
EXPECT_EQ(location.file, token.location.file);
EXPECT_EQ(location.line, token.location.line);
} }
}; };
......
...@@ -15,6 +15,8 @@ namespace angle ...@@ -15,6 +15,8 @@ namespace angle
class SpaceTest : public PreprocessorTest class SpaceTest : public PreprocessorTest
{ {
protected: protected:
SpaceTest() : PreprocessorTest(SH_GLES2_SPEC) {}
void expectSpace(const std::string& str) void expectSpace(const std::string& str)
{ {
const char* cstr = str.c_str(); const char* cstr = str.c_str();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment