Commit f1cf5e63 by Olli Etuaho Committed by Commit Bot

Prevent stack overflow in macro expansion

Add a configurable limit for how many nested MacroExpander objects can be created in the preprocessor, so that stack overflow can be prevented in case of malicious shaders. By default the limit is set to 1000. In unit tests the limit is set lower to make the test run faster. Includes refactoring of most of the preprocessor tests so that they use utility functions provided by the test class instead of repeating the same code for initializing the preprocessor. BUG=angleproject:1600 TEST=angle_unittests Change-Id: I23b5140d9f2dc52df96111650db63150f7238494 Reviewed-on: https://chromium-review.googlesource.com/413986 Commit-Queue: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent 82d3e03d
...@@ -82,6 +82,8 @@ std::string Diagnostics::message(ID id) ...@@ -82,6 +82,8 @@ std::string Diagnostics::message(ID id)
return "Too many arguments for macro"; return "Too many arguments for macro";
case PP_MACRO_DUPLICATE_PARAMETER_NAMES: case PP_MACRO_DUPLICATE_PARAMETER_NAMES:
return "duplicate macro parameter name"; return "duplicate macro parameter name";
case PP_MACRO_INVOCATION_CHAIN_TOO_DEEP:
return "macro invocation chain too deep";
case PP_CONDITIONAL_ENDIF_WITHOUT_IF: case PP_CONDITIONAL_ENDIF_WITHOUT_IF:
return "unexpected #endif found without a matching #if"; return "unexpected #endif found without a matching #if";
case PP_CONDITIONAL_ELSE_WITHOUT_IF: case PP_CONDITIONAL_ELSE_WITHOUT_IF:
......
...@@ -48,6 +48,7 @@ class Diagnostics ...@@ -48,6 +48,7 @@ class Diagnostics
PP_MACRO_TOO_FEW_ARGS, PP_MACRO_TOO_FEW_ARGS,
PP_MACRO_TOO_MANY_ARGS, PP_MACRO_TOO_MANY_ARGS,
PP_MACRO_DUPLICATE_PARAMETER_NAMES, PP_MACRO_DUPLICATE_PARAMETER_NAMES,
PP_MACRO_INVOCATION_CHAIN_TOO_DEEP,
PP_CONDITIONAL_ENDIF_WITHOUT_IF, PP_CONDITIONAL_ENDIF_WITHOUT_IF,
PP_CONDITIONAL_ELSE_WITHOUT_IF, PP_CONDITIONAL_ELSE_WITHOUT_IF,
PP_CONDITIONAL_ELSE_AFTER_ELSE, PP_CONDITIONAL_ELSE_AFTER_ELSE,
......
...@@ -202,14 +202,16 @@ class DefinedParser : public Lexer ...@@ -202,14 +202,16 @@ class DefinedParser : public Lexer
DirectiveParser::DirectiveParser(Tokenizer *tokenizer, DirectiveParser::DirectiveParser(Tokenizer *tokenizer,
MacroSet *macroSet, MacroSet *macroSet,
Diagnostics *diagnostics, Diagnostics *diagnostics,
DirectiveHandler *directiveHandler) DirectiveHandler *directiveHandler,
int maxMacroExpansionDepth)
: mPastFirstStatement(false), : mPastFirstStatement(false),
mSeenNonPreprocessorToken(false), mSeenNonPreprocessorToken(false),
mTokenizer(tokenizer), mTokenizer(tokenizer),
mMacroSet(macroSet), mMacroSet(macroSet),
mDiagnostics(diagnostics), mDiagnostics(diagnostics),
mDirectiveHandler(directiveHandler), mDirectiveHandler(directiveHandler),
mShaderVersion(100) mShaderVersion(100),
mMaxMacroExpansionDepth(maxMacroExpansionDepth)
{ {
} }
...@@ -843,7 +845,7 @@ void DirectiveParser::parseLine(Token *token) ...@@ -843,7 +845,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); MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, mMaxMacroExpansionDepth);
// 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);
...@@ -952,7 +954,7 @@ int DirectiveParser::parseExpressionIf(Token *token) ...@@ -952,7 +954,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); DefinedParser definedParser(mTokenizer, mMacroSet, mDiagnostics);
MacroExpander macroExpander(&definedParser, mMacroSet, mDiagnostics); MacroExpander macroExpander(&definedParser, mMacroSet, mDiagnostics, mMaxMacroExpansionDepth);
ExpressionParser expressionParser(&macroExpander, mDiagnostics); ExpressionParser expressionParser(&macroExpander, mDiagnostics);
int expression = 0; int expression = 0;
......
...@@ -24,7 +24,8 @@ class DirectiveParser : public Lexer ...@@ -24,7 +24,8 @@ class DirectiveParser : public Lexer
DirectiveParser(Tokenizer *tokenizer, DirectiveParser(Tokenizer *tokenizer,
MacroSet *macroSet, MacroSet *macroSet,
Diagnostics *diagnostics, Diagnostics *diagnostics,
DirectiveHandler *directiveHandler); DirectiveHandler *directiveHandler,
int maxMacroExpansionDepth);
void lex(Token *token) override; void lex(Token *token) override;
...@@ -76,6 +77,7 @@ class DirectiveParser : public Lexer ...@@ -76,6 +77,7 @@ class DirectiveParser : public Lexer
Diagnostics *mDiagnostics; Diagnostics *mDiagnostics;
DirectiveHandler *mDirectiveHandler; DirectiveHandler *mDirectiveHandler;
int mShaderVersion; int mShaderVersion;
int mMaxMacroExpansionDepth;
}; };
} // namespace pp } // namespace pp
......
...@@ -77,11 +77,15 @@ MacroExpander::ScopedMacroReenabler::~ScopedMacroReenabler() ...@@ -77,11 +77,15 @@ MacroExpander::ScopedMacroReenabler::~ScopedMacroReenabler()
mExpander->mMacrosToReenable.clear(); mExpander->mMacrosToReenable.clear();
} }
MacroExpander::MacroExpander(Lexer *lexer, MacroSet *macroSet, Diagnostics *diagnostics) MacroExpander::MacroExpander(Lexer *lexer,
MacroSet *macroSet,
Diagnostics *diagnostics,
int allowedMacroExpansionDepth)
: mLexer(lexer), : mLexer(lexer),
mMacroSet(macroSet), mMacroSet(macroSet),
mDiagnostics(diagnostics), mDiagnostics(diagnostics),
mTotalTokensInContexts(0), mTotalTokensInContexts(0),
mAllowedMacroExpansionDepth(allowedMacroExpansionDepth),
mDeferReenablingMacros(false) mDeferReenablingMacros(false)
{ {
} }
...@@ -377,7 +381,13 @@ bool MacroExpander::collectMacroArgs(const Macro &macro, ...@@ -377,7 +381,13 @@ bool MacroExpander::collectMacroArgs(const Macro &macro,
for (auto &arg : *args) for (auto &arg : *args)
{ {
TokenLexer lexer(&arg); TokenLexer lexer(&arg);
MacroExpander expander(&lexer, mMacroSet, mDiagnostics); if (mAllowedMacroExpansionDepth < 1)
{
mDiagnostics->report(Diagnostics::PP_MACRO_INVOCATION_CHAIN_TOO_DEEP, token.location,
token.text);
return false;
}
MacroExpander expander(&lexer, mMacroSet, mDiagnostics, mAllowedMacroExpansionDepth - 1);
arg.clear(); arg.clear();
expander.lex(&token); expander.lex(&token);
......
...@@ -22,7 +22,10 @@ struct SourceLocation; ...@@ -22,7 +22,10 @@ struct SourceLocation;
class MacroExpander : public Lexer class MacroExpander : public Lexer
{ {
public: public:
MacroExpander(Lexer *lexer, MacroSet *macroSet, Diagnostics *diagnostics); MacroExpander(Lexer *lexer,
MacroSet *macroSet,
Diagnostics *diagnostics,
int allowedMacroExpansionDepth);
~MacroExpander() override; ~MacroExpander() override;
void lex(Token *token) override; void lex(Token *token) override;
...@@ -68,6 +71,8 @@ class MacroExpander : public Lexer ...@@ -68,6 +71,8 @@ class MacroExpander : public Lexer
std::vector<MacroContext *> mContextStack; std::vector<MacroContext *> mContextStack;
size_t mTotalTokensInContexts; size_t mTotalTokensInContexts;
int mAllowedMacroExpansionDepth;
bool mDeferReenablingMacros; bool mDeferReenablingMacros;
std::vector<const Macro *> mMacrosToReenable; std::vector<const Macro *> mMacrosToReenable;
......
...@@ -25,19 +25,26 @@ struct PreprocessorImpl ...@@ -25,19 +25,26 @@ struct PreprocessorImpl
DirectiveParser directiveParser; DirectiveParser directiveParser;
MacroExpander macroExpander; MacroExpander macroExpander;
PreprocessorImpl(Diagnostics *diag, DirectiveHandler *directiveHandler) PreprocessorImpl(Diagnostics *diag,
DirectiveHandler *directiveHandler,
const PreprocessorSettings &settings)
: diagnostics(diag), : diagnostics(diag),
tokenizer(diag), tokenizer(diag),
directiveParser(&tokenizer, &macroSet, diag, directiveHandler), directiveParser(&tokenizer,
macroExpander(&directiveParser, &macroSet, diag) &macroSet,
diag,
directiveHandler,
settings.maxMacroExpansionDepth),
macroExpander(&directiveParser, &macroSet, diag, settings.maxMacroExpansionDepth)
{ {
} }
}; };
Preprocessor::Preprocessor(Diagnostics *diagnostics, Preprocessor::Preprocessor(Diagnostics *diagnostics,
DirectiveHandler *directiveHandler) DirectiveHandler *directiveHandler,
const PreprocessorSettings &settings)
{ {
mImpl = new PreprocessorImpl(diagnostics, directiveHandler); mImpl = new PreprocessorImpl(diagnostics, directiveHandler, settings);
} }
Preprocessor::~Preprocessor() Preprocessor::~Preprocessor()
......
...@@ -19,10 +19,18 @@ class DirectiveHandler; ...@@ -19,10 +19,18 @@ class DirectiveHandler;
struct PreprocessorImpl; struct PreprocessorImpl;
struct Token; struct Token;
struct PreprocessorSettings : angle::NonCopyable
{
PreprocessorSettings() : maxMacroExpansionDepth(1000) {}
int maxMacroExpansionDepth;
};
class Preprocessor : angle::NonCopyable class Preprocessor : angle::NonCopyable
{ {
public: public:
Preprocessor(Diagnostics *diagnostics, DirectiveHandler *directiveHandler); Preprocessor(Diagnostics *diagnostics,
DirectiveHandler *directiveHandler,
const PreprocessorSettings &settings);
~Preprocessor(); ~Preprocessor();
// count: specifies the number of elements in the string and length arrays. // count: specifies the number of elements in the string and length arrays.
......
...@@ -99,7 +99,7 @@ TParseContext::TParseContext(TSymbolTable &symt, ...@@ -99,7 +99,7 @@ TParseContext::TParseContext(TSymbolTable &symt,
mShaderVersion, mShaderVersion,
mShaderType, mShaderType,
resources.WEBGL_debug_shader_precision == 1), resources.WEBGL_debug_shader_precision == 1),
mPreprocessor(&mDiagnostics, &mDirectiveHandler), mPreprocessor(&mDiagnostics, &mDirectiveHandler, pp::PreprocessorSettings()),
mScanner(nullptr), mScanner(nullptr),
mUsesFragData(false), mUsesFragData(false),
mUsesFragColor(false), mUsesFragColor(false),
......
...@@ -7,23 +7,61 @@ ...@@ -7,23 +7,61 @@
#include "PreprocessorTest.h" #include "PreprocessorTest.h"
#include "compiler/preprocessor/Token.h" #include "compiler/preprocessor/Token.h"
void PreprocessorTest::preprocess(const char* input, const char* expected) void SimplePreprocessorTest::preprocess(const char *input,
std::stringstream *output,
pp::Preprocessor *preprocessor)
{ {
ASSERT_TRUE(mPreprocessor.init(1, &input, NULL)); ASSERT_TRUE(preprocessor->init(1, &input, NULL));
int line = 1; int line = 1;
pp::Token token; pp::Token token;
std::stringstream stream;
do do
{ {
mPreprocessor.lex(&token); preprocessor->lex(&token);
for (; line < token.location.line; ++line) if (output)
{ {
stream << "\n"; for (; line < token.location.line; ++line)
{
*output << "\n";
}
*output << token;
} }
stream << token;
} while (token.type != pp::Token::LAST); } while (token.type != pp::Token::LAST);
}
void SimplePreprocessorTest::preprocess(const char *input, const pp::PreprocessorSettings &settings)
{
pp::Preprocessor preprocessor(&mDiagnostics, &mDirectiveHandler, settings);
preprocess(input, nullptr, &preprocessor);
}
void SimplePreprocessorTest::preprocess(const char *input)
{
preprocess(input, pp::PreprocessorSettings());
}
std::string actual = stream.str(); void SimplePreprocessorTest::preprocess(const char *input, const char *expected)
{
pp::Preprocessor preprocessor(&mDiagnostics, &mDirectiveHandler, pp::PreprocessorSettings());
std::stringstream output;
preprocess(input, &output, &preprocessor);
std::string actual = output.str();
EXPECT_STREQ(expected, actual.c_str()); EXPECT_STREQ(expected, actual.c_str());
} }
void SimplePreprocessorTest::lexSingleToken(const char *input, pp::Token *token)
{
pp::Preprocessor preprocessor(&mDiagnostics, &mDirectiveHandler, pp::PreprocessorSettings());
ASSERT_TRUE(preprocessor.init(1, &input, nullptr));
preprocessor.lex(token);
}
void SimplePreprocessorTest::lexSingleToken(size_t count,
const char *const input[],
pp::Token *token)
{
pp::Preprocessor preprocessor(&mDiagnostics, &mDirectiveHandler, pp::PreprocessorSettings());
ASSERT_TRUE(preprocessor.init(count, input, nullptr));
preprocessor.lex(token);
}
\ No newline at end of file
...@@ -16,15 +16,35 @@ ...@@ -16,15 +16,35 @@
class PreprocessorTest : public testing::Test class PreprocessorTest : public testing::Test
{ {
protected: protected:
PreprocessorTest() : mPreprocessor(&mDiagnostics, &mDirectiveHandler) { } PreprocessorTest()
: mPreprocessor(&mDiagnostics, &mDirectiveHandler, pp::PreprocessorSettings())
// Preprocesses the input string and verifies that it matches {
// expected output. }
void preprocess(const char* input, const char* expected);
MockDiagnostics mDiagnostics; MockDiagnostics mDiagnostics;
MockDirectiveHandler mDirectiveHandler; MockDirectiveHandler mDirectiveHandler;
pp::Preprocessor mPreprocessor; pp::Preprocessor mPreprocessor;
}; };
class SimplePreprocessorTest : public testing::Test
{
protected:
// Preprocesses the input string.
void preprocess(const char *input);
void preprocess(const char *input, const pp::PreprocessorSettings &settings);
// Preprocesses the input string and verifies that it matches expected output.
void preprocess(const char *input, const char *expected);
// Lexes a single token from input and writes it to token.
void lexSingleToken(const char *input, pp::Token *token);
void lexSingleToken(size_t count, const char *const input[], pp::Token *token);
MockDiagnostics mDiagnostics;
MockDirectiveHandler mDirectiveHandler;
private:
void preprocess(const char *input, std::stringstream *output, pp::Preprocessor *preprocessor);
};
#endif // PREPROCESSOR_TESTS_PREPROCESSOR_TEST_H_ #endif // PREPROCESSOR_TESTS_PREPROCESSOR_TEST_H_
...@@ -7,8 +7,7 @@ ...@@ -7,8 +7,7 @@
#include "PreprocessorTest.h" #include "PreprocessorTest.h"
#include "compiler/preprocessor/Token.h" #include "compiler/preprocessor/Token.h"
class CommentTest : public PreprocessorTest, class CommentTest : public SimplePreprocessorTest, public testing::WithParamInterface<const char *>
public testing::WithParamInterface<const char*>
{ {
}; };
...@@ -16,10 +15,8 @@ TEST_P(CommentTest, CommentIgnored) ...@@ -16,10 +15,8 @@ TEST_P(CommentTest, CommentIgnored)
{ {
const char* str = GetParam(); const char* str = GetParam();
ASSERT_TRUE(mPreprocessor.init(1, &str, 0));
pp::Token token; pp::Token token;
mPreprocessor.lex(&token); lexSingleToken(str, &token);
EXPECT_EQ(pp::Token::LAST, token.type); EXPECT_EQ(pp::Token::LAST, token.type);
} }
...@@ -38,7 +35,7 @@ INSTANTIATE_TEST_CASE_P(BlockComment, CommentTest, ...@@ -38,7 +35,7 @@ INSTANTIATE_TEST_CASE_P(BlockComment, CommentTest,
"/***/", // With lone '*'. "/***/", // With lone '*'.
"/*\"*/")); // Invalid character. "/*\"*/")); // Invalid character.
class BlockCommentTest : public PreprocessorTest class BlockCommentTest : public SimplePreprocessorTest
{ {
}; };
...@@ -46,10 +43,8 @@ TEST_F(BlockCommentTest, CommentReplacedWithSpace) ...@@ -46,10 +43,8 @@ TEST_F(BlockCommentTest, CommentReplacedWithSpace)
{ {
const char* str = "/*foo*/bar"; const char* str = "/*foo*/bar";
ASSERT_TRUE(mPreprocessor.init(1, &str, 0));
pp::Token token; pp::Token token;
mPreprocessor.lex(&token); lexSingleToken(str, &token);
EXPECT_EQ(pp::Token::IDENTIFIER, token.type); EXPECT_EQ(pp::Token::IDENTIFIER, token.type);
EXPECT_EQ("bar", token.text); EXPECT_EQ("bar", token.text);
EXPECT_TRUE(token.hasLeadingSpace()); EXPECT_TRUE(token.hasLeadingSpace());
...@@ -59,11 +54,8 @@ TEST_F(BlockCommentTest, UnterminatedComment) ...@@ -59,11 +54,8 @@ TEST_F(BlockCommentTest, UnterminatedComment)
{ {
const char* str = "/*foo"; const char* str = "/*foo";
ASSERT_TRUE(mPreprocessor.init(1, &str, 0));
using testing::_; using testing::_;
EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_EOF_IN_COMMENT, _, _)); EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_EOF_IN_COMMENT, _, _));
pp::Token token; preprocess(str);
mPreprocessor.lex(&token);
} }
...@@ -4,12 +4,14 @@ ...@@ -4,12 +4,14 @@
// found in the LICENSE file. // found in the LICENSE file.
// //
#include <sstream>
#include "PreprocessorTest.h" #include "PreprocessorTest.h"
#include "compiler/preprocessor/Token.h" #include "compiler/preprocessor/Token.h"
using testing::_; using testing::_;
class DefineTest : public PreprocessorTest class DefineTest : public SimplePreprocessorTest
{ {
}; };
...@@ -849,10 +851,9 @@ TEST_F(DefineTest, Predefined_VERSION) ...@@ -849,10 +851,9 @@ TEST_F(DefineTest, Predefined_VERSION)
TEST_F(DefineTest, Predefined_LINE1) TEST_F(DefineTest, Predefined_LINE1)
{ {
const char* str = "\n\n__LINE__"; const char* str = "\n\n__LINE__";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
pp::Token token; pp::Token token;
mPreprocessor.lex(&token); lexSingleToken(str, &token);
EXPECT_EQ(pp::Token::CONST_INT, token.type); EXPECT_EQ(pp::Token::CONST_INT, token.type);
EXPECT_EQ("3", token.text); EXPECT_EQ("3", token.text);
} }
...@@ -861,10 +862,9 @@ TEST_F(DefineTest, Predefined_LINE2) ...@@ -861,10 +862,9 @@ TEST_F(DefineTest, Predefined_LINE2)
{ {
const char* str = "#line 10\n" const char* str = "#line 10\n"
"__LINE__\n"; "__LINE__\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
pp::Token token; pp::Token token;
mPreprocessor.lex(&token); lexSingleToken(str, &token);
EXPECT_EQ(pp::Token::CONST_INT, token.type); EXPECT_EQ(pp::Token::CONST_INT, token.type);
EXPECT_EQ("10", token.text); EXPECT_EQ("10", token.text);
} }
...@@ -872,10 +872,9 @@ TEST_F(DefineTest, Predefined_LINE2) ...@@ -872,10 +872,9 @@ TEST_F(DefineTest, Predefined_LINE2)
TEST_F(DefineTest, Predefined_FILE1) TEST_F(DefineTest, Predefined_FILE1)
{ {
const char* const str[] = {"", "", "__FILE__"}; const char* const str[] = {"", "", "__FILE__"};
ASSERT_TRUE(mPreprocessor.init(3, str, NULL));
pp::Token token; pp::Token token;
mPreprocessor.lex(&token); lexSingleToken(3, str, &token);
EXPECT_EQ(pp::Token::CONST_INT, token.type); EXPECT_EQ(pp::Token::CONST_INT, token.type);
EXPECT_EQ("2", token.text); EXPECT_EQ("2", token.text);
} }
...@@ -883,10 +882,9 @@ TEST_F(DefineTest, Predefined_FILE1) ...@@ -883,10 +882,9 @@ TEST_F(DefineTest, Predefined_FILE1)
TEST_F(DefineTest, Predefined_FILE2) TEST_F(DefineTest, Predefined_FILE2)
{ {
const char* const str[] = {"#line 10 20\n", "__FILE__"}; const char* const str[] = {"#line 10 20\n", "__FILE__"};
ASSERT_TRUE(mPreprocessor.init(2, str, NULL));
pp::Token token; pp::Token token;
mPreprocessor.lex(&token); lexSingleToken(2, str, &token);
EXPECT_EQ(pp::Token::CONST_INT, token.type); EXPECT_EQ(pp::Token::CONST_INT, token.type);
EXPECT_EQ("21", token.text); EXPECT_EQ("21", token.text);
} }
...@@ -986,3 +984,27 @@ TEST_F(DefineTest, RecursiveMacroNameInsideIncompleteMacroInvocationInMacroExpan ...@@ -986,3 +984,27 @@ TEST_F(DefineTest, RecursiveMacroNameInsideIncompleteMacroInvocationInMacroExpan
"\n"; "\n";
preprocess(input, expected); preprocess(input, expected);
} }
// The macro invocations form a long chain. The macro expander should protect against stack overflow
// and generate an error in this case.
TEST_F(DefineTest, LongMacroInvocationChain)
{
std::stringstream inputStream;
std::stringstream expectedStream;
inputStream << "#define b(x) x\n";
inputStream << "#define a0(x) foo x\n";
for (int i = 1; i < 20; ++i)
{
inputStream << "#define a" << i << "(x) b(a" << (i - 1) << "(x))\n";
}
inputStream << "a19(y)\n";
EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_MACRO_INVOCATION_CHAIN_TOO_DEEP,
pp::SourceLocation(0, 22), _));
pp::PreprocessorSettings settings;
settings.maxMacroExpansionDepth = 19;
preprocess(inputStream.str().c_str(), settings);
}
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
#include "PreprocessorTest.h" #include "PreprocessorTest.h"
#include "compiler/preprocessor/Token.h" #include "compiler/preprocessor/Token.h"
class ErrorTest : public PreprocessorTest class ErrorTest : public SimplePreprocessorTest
{ {
}; };
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
#include "PreprocessorTest.h" #include "PreprocessorTest.h"
#include "compiler/preprocessor/Token.h" #include "compiler/preprocessor/Token.h"
class ExtensionTest : public PreprocessorTest class ExtensionTest : public SimplePreprocessorTest
{ {
}; };
......
...@@ -9,16 +9,15 @@ ...@@ -9,16 +9,15 @@
#define CLOSED_RANGE(x, y) testing::Range(x, static_cast<char>((y) + 1)) #define CLOSED_RANGE(x, y) testing::Range(x, static_cast<char>((y) + 1))
class IdentifierTest : public PreprocessorTest class IdentifierTest : public SimplePreprocessorTest
{ {
protected: protected:
void expectIdentifier(const std::string& str) void expectIdentifier(const std::string& str)
{ {
const char* cstr = str.c_str(); const char* cstr = str.c_str();
ASSERT_TRUE(mPreprocessor.init(1, &cstr, 0));
pp::Token token; pp::Token token;
mPreprocessor.lex(&token); lexSingleToken(cstr, &token);
EXPECT_EQ(pp::Token::IDENTIFIER, token.type); EXPECT_EQ(pp::Token::IDENTIFIER, token.type);
EXPECT_EQ(str, token.text); EXPECT_EQ(str, token.text);
} }
......
...@@ -9,21 +9,19 @@ ...@@ -9,21 +9,19 @@
#define CLOSED_RANGE(x, y) testing::Range(x, static_cast<char>((y) + 1)) #define CLOSED_RANGE(x, y) testing::Range(x, static_cast<char>((y) + 1))
class InvalidNumberTest : public PreprocessorTest, class InvalidNumberTest : public SimplePreprocessorTest,
public testing::WithParamInterface<const char*> public testing::WithParamInterface<const char *>
{ {
}; };
TEST_P(InvalidNumberTest, InvalidNumberIdentified) TEST_P(InvalidNumberTest, InvalidNumberIdentified)
{ {
const char* str = GetParam(); const char* str = GetParam();
ASSERT_TRUE(mPreprocessor.init(1, &str, 0));
using testing::_; using testing::_;
EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_INVALID_NUMBER, _, str)); EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_INVALID_NUMBER, _, str));
pp::Token token; preprocess(str);
mPreprocessor.lex(&token);
} }
INSTANTIATE_TEST_CASE_P(InvalidIntegers, InvalidNumberTest, INSTANTIATE_TEST_CASE_P(InvalidIntegers, InvalidNumberTest,
...@@ -34,8 +32,7 @@ INSTANTIATE_TEST_CASE_P(InvalidFloats, InvalidNumberTest, ...@@ -34,8 +32,7 @@ INSTANTIATE_TEST_CASE_P(InvalidFloats, InvalidNumberTest,
testing::Values("1eg", "0.a", "0.1.2", ".0a", ".0.1")); testing::Values("1eg", "0.a", "0.1.2", ".0a", ".0.1"));
typedef std::tr1::tuple<const char*, char> IntegerParams; typedef std::tr1::tuple<const char*, char> IntegerParams;
class IntegerTest : public PreprocessorTest, class IntegerTest : public SimplePreprocessorTest, public testing::WithParamInterface<IntegerParams>
public testing::WithParamInterface<IntegerParams>
{ {
}; };
...@@ -45,10 +42,8 @@ TEST_P(IntegerTest, Identified) ...@@ -45,10 +42,8 @@ TEST_P(IntegerTest, Identified)
str.push_back(std::tr1::get<1>(GetParam())); // digit. str.push_back(std::tr1::get<1>(GetParam())); // digit.
const char* cstr = str.c_str(); const char* cstr = str.c_str();
ASSERT_TRUE(mPreprocessor.init(1, &cstr, 0));
pp::Token token; pp::Token token;
mPreprocessor.lex(&token); lexSingleToken(cstr, &token);
EXPECT_EQ(pp::Token::CONST_INT, token.type); EXPECT_EQ(pp::Token::CONST_INT, token.type);
EXPECT_EQ(str, token.text); EXPECT_EQ(str, token.text);
} }
...@@ -78,16 +73,15 @@ INSTANTIATE_TEST_CASE_P(HexadecimalInteger_A_F, ...@@ -78,16 +73,15 @@ INSTANTIATE_TEST_CASE_P(HexadecimalInteger_A_F,
testing::Combine(testing::Values("0x", "0X"), testing::Combine(testing::Values("0x", "0X"),
CLOSED_RANGE('A', 'F'))); CLOSED_RANGE('A', 'F')));
class FloatTest : public PreprocessorTest class FloatTest : public SimplePreprocessorTest
{ {
protected: protected:
void expectFloat(const std::string& str) void expectFloat(const std::string& str)
{ {
const char* cstr = str.c_str(); const char* cstr = str.c_str();
ASSERT_TRUE(mPreprocessor.init(1, &cstr, 0));
pp::Token token; pp::Token token;
mPreprocessor.lex(&token); lexSingleToken(cstr, &token);
EXPECT_EQ(pp::Token::CONST_FLOAT, token.type); EXPECT_EQ(pp::Token::CONST_FLOAT, token.type);
EXPECT_EQ(str, token.text); EXPECT_EQ(str, token.text);
} }
......
...@@ -13,7 +13,7 @@ struct OperatorTestParam ...@@ -13,7 +13,7 @@ struct OperatorTestParam
int op; int op;
}; };
class OperatorTest : public PreprocessorTest, class OperatorTest : public SimplePreprocessorTest,
public testing::WithParamInterface<OperatorTestParam> public testing::WithParamInterface<OperatorTestParam>
{ {
}; };
...@@ -22,10 +22,8 @@ TEST_P(OperatorTest, Identified) ...@@ -22,10 +22,8 @@ TEST_P(OperatorTest, Identified)
{ {
OperatorTestParam param = GetParam(); OperatorTestParam param = GetParam();
ASSERT_TRUE(mPreprocessor.init(1, &param.str, 0));
pp::Token token; pp::Token token;
mPreprocessor.lex(&token); lexSingleToken(param.str, &token);
EXPECT_EQ(param.op, token.type); EXPECT_EQ(param.op, token.type);
EXPECT_EQ(param.str, token.text); EXPECT_EQ(param.str, token.text);
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
#include "PreprocessorTest.h" #include "PreprocessorTest.h"
#include "compiler/preprocessor/Token.h" #include "compiler/preprocessor/Token.h"
class PragmaTest : public PreprocessorTest class PragmaTest : public SimplePreprocessorTest
{ {
}; };
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
#include "PreprocessorTest.h" #include "PreprocessorTest.h"
#include "compiler/preprocessor/Token.h" #include "compiler/preprocessor/Token.h"
class VersionTest : public PreprocessorTest class VersionTest : public SimplePreprocessorTest
{ {
}; };
...@@ -100,25 +100,19 @@ TEST_F(VersionTest, AfterValidToken) ...@@ -100,25 +100,19 @@ TEST_F(VersionTest, AfterValidToken)
{ {
const char* str = "foo\n" const char* str = "foo\n"
"#version 200\n"; "#version 200\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
using testing::_; using testing::_;
EXPECT_CALL(mDiagnostics, EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::PP_VERSION_NOT_FIRST_STATEMENT, print(pp::Diagnostics::PP_VERSION_NOT_FIRST_STATEMENT,
pp::SourceLocation(0, 2), _)); pp::SourceLocation(0, 2), _));
pp::Token token; preprocess(str);
do
{
mPreprocessor.lex(&token);
} while (token.type != pp::Token::LAST);
} }
TEST_F(VersionTest, AfterInvalidToken) TEST_F(VersionTest, AfterInvalidToken)
{ {
const char* str = "$\n" const char* str = "$\n"
"#version 200\n"; "#version 200\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
using testing::_; using testing::_;
EXPECT_CALL(mDiagnostics, EXPECT_CALL(mDiagnostics,
...@@ -128,36 +122,26 @@ TEST_F(VersionTest, AfterInvalidToken) ...@@ -128,36 +122,26 @@ TEST_F(VersionTest, AfterInvalidToken)
print(pp::Diagnostics::PP_VERSION_NOT_FIRST_STATEMENT, print(pp::Diagnostics::PP_VERSION_NOT_FIRST_STATEMENT,
pp::SourceLocation(0, 2), _)); pp::SourceLocation(0, 2), _));
pp::Token token; preprocess(str);
do
{
mPreprocessor.lex(&token);
} while (token.type != pp::Token::LAST);
} }
TEST_F(VersionTest, AfterValidDirective) TEST_F(VersionTest, AfterValidDirective)
{ {
const char* str = "#\n" const char* str = "#\n"
"#version 200\n"; "#version 200\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
using testing::_; using testing::_;
EXPECT_CALL(mDiagnostics, EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::PP_VERSION_NOT_FIRST_STATEMENT, print(pp::Diagnostics::PP_VERSION_NOT_FIRST_STATEMENT,
pp::SourceLocation(0, 2), _)); pp::SourceLocation(0, 2), _));
pp::Token token; preprocess(str);
do
{
mPreprocessor.lex(&token);
} while (token.type != pp::Token::LAST);
} }
TEST_F(VersionTest, AfterInvalidDirective) TEST_F(VersionTest, AfterInvalidDirective)
{ {
const char* str = "#foo\n" const char* str = "#foo\n"
"#version 200\n"; "#version 200\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
using testing::_; using testing::_;
EXPECT_CALL(mDiagnostics, EXPECT_CALL(mDiagnostics,
...@@ -167,11 +151,7 @@ TEST_F(VersionTest, AfterInvalidDirective) ...@@ -167,11 +151,7 @@ TEST_F(VersionTest, AfterInvalidDirective)
print(pp::Diagnostics::PP_VERSION_NOT_FIRST_STATEMENT, print(pp::Diagnostics::PP_VERSION_NOT_FIRST_STATEMENT,
pp::SourceLocation(0, 2), _)); pp::SourceLocation(0, 2), _));
pp::Token token; preprocess(str);
do
{
mPreprocessor.lex(&token);
} while (token.type != pp::Token::LAST);
} }
TEST_F(VersionTest, AfterExcludedBlock) TEST_F(VersionTest, AfterExcludedBlock)
...@@ -180,18 +160,13 @@ TEST_F(VersionTest, AfterExcludedBlock) ...@@ -180,18 +160,13 @@ TEST_F(VersionTest, AfterExcludedBlock)
"foo\n" "foo\n"
"#endif\n" "#endif\n"
"#version 200\n"; "#version 200\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
using testing::_; using testing::_;
EXPECT_CALL(mDiagnostics, EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::PP_VERSION_NOT_FIRST_STATEMENT, print(pp::Diagnostics::PP_VERSION_NOT_FIRST_STATEMENT,
pp::SourceLocation(0, 4), _)); pp::SourceLocation(0, 4), _));
pp::Token token; preprocess(str);
do
{
mPreprocessor.lex(&token);
} while (token.type != pp::Token::LAST);
} }
struct VersionTestParam struct VersionTestParam
......
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