diff options
author | Erik Verbruggen <erik.verbruggen@me.com> | 2013-03-18 15:30:55 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-03-19 01:57:27 +0100 |
commit | a89a652bf5a9cb01ad2babc4c1374ba0b4d07edd (patch) | |
tree | 894fabdae7898cf02b5bb8b050506b5806585d8e | |
parent | 9237c2ecb5c6fc8820c28a636d1112990c950bbe (diff) |
Fix hexadecimal escape sequence validation in strings.
Give an error message when the sequence does not conform to the grammar.
Although the specification does not explicitly state that this is an
error, this is the behaviour of both JSC and V8.
Change-Id: I34d189f07628bc6cc40b13bfbb8d09bee7810ced
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
7 files changed, 55 insertions, 24 deletions
diff --git a/src/qml/qml/parser/qqmljslexer.cpp b/src/qml/qml/parser/qqmljslexer.cpp index edd85ec878..0df49279e2 100644 --- a/src/qml/qml/parser/qqmljslexer.cpp +++ b/src/qml/qml/parser/qqmljslexer.cpp @@ -54,7 +54,7 @@ QT_END_NAMESPACE using namespace QQmlJS; -static int regExpFlagFromChar(const QChar &ch) +static inline int regExpFlagFromChar(const QChar &ch) { switch (ch.unicode()) { case 'g': return Lexer::RegExp_Global; @@ -64,7 +64,7 @@ static int regExpFlagFromChar(const QChar &ch) return 0; } -static unsigned char convertHex(ushort c) +static inline unsigned char convertHex(ushort c) { if (c >= '0' && c <= '9') return (c - '0'); @@ -74,12 +74,12 @@ static unsigned char convertHex(ushort c) return (c - 'A' + 10); } -static QChar convertHex(QChar c1, QChar c2) +static inline QChar convertHex(QChar c1, QChar c2) { return QChar((convertHex(c1.unicode()) << 4) + convertHex(c2.unicode())); } -static QChar convertUnicode(QChar c1, QChar c2, QChar c3, QChar c4) +static inline QChar convertUnicode(QChar c1, QChar c2, QChar c3, QChar c4) { return QChar((convertHex(c3.unicode()) << 4) + convertHex(c4.unicode()), (convertHex(c1.unicode()) << 4) + convertHex(c2.unicode())); @@ -329,6 +329,27 @@ QChar Lexer::decodeUnicodeEscapeCharacter(bool *ok) return QChar(); } +QChar Lexer::decodeHexEscapeCharacter(bool *ok) +{ + if (isHexDigit(_codePtr[0]) && isHexDigit(_codePtr[1])) { + scanChar(); + + const QChar c1 = _char; + scanChar(); + + const QChar c2 = _char; + scanChar(); + + if (ok) + *ok = true; + + return convertHex(c1, c2); + } + + *ok = false; + return QChar(); +} + static inline bool isIdentifierStart(QChar ch) { // fast path for ascii @@ -705,35 +726,29 @@ again: scanChar(); QChar u; - bool ok = false; switch (_char.unicode()) { // unicode escape sequence - case 'u': + case 'u': { + bool ok = false; u = decodeUnicodeEscapeCharacter(&ok); if (! ok) { _errorCode = IllegalUnicodeEscapeSequence; _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence"); return T_ERROR; } - break; + } break; // hex escape sequence - case 'x': - if (isHexDigit(_codePtr[0]) && isHexDigit(_codePtr[1])) { - scanChar(); - - const QChar c1 = _char; - scanChar(); - - const QChar c2 = _char; - scanChar(); - - u = convertHex(c1, c2); - } else { - u = _char; + case 'x': { + bool ok = false; + u = decodeHexEscapeCharacter(&ok); + if (!ok) { + _errorCode = IllegalHexadecimalEscapeSequence; + _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal hexadecimal escape sequence"); + return T_ERROR; } - break; + } break; // single character escape sequence case '\\': u = QLatin1Char('\\'); scanChar(); break; diff --git a/src/qml/qml/parser/qqmljslexer_p.h b/src/qml/qml/parser/qqmljslexer_p.h index e1b51da92b..23af61d650 100644 --- a/src/qml/qml/parser/qqmljslexer_p.h +++ b/src/qml/qml/parser/qqmljslexer_p.h @@ -128,7 +128,8 @@ public: IllegalUnicodeEscapeSequence, UnclosedComment, IllegalExponentIndicator, - IllegalIdentifier + IllegalIdentifier, + IllegalHexadecimalEscapeSequence }; enum RegExpBodyPrefix { @@ -203,6 +204,7 @@ private: void syncProhibitAutomaticSemicolon(); QChar decodeUnicodeEscapeCharacter(bool *ok); + QChar decodeHexEscapeCharacter(bool *ok); private: Engine *_engine; diff --git a/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.4.js b/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.4.js index 4a3173db6c..4b799f8df1 100644 --- a/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.4.js +++ b/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.4.js @@ -135,9 +135,10 @@ new TestCase( SECTION, "\\x1E1", String.fromCharCode(30)+"1", "\x1E new TestCase( SECTION, "\\x0F0", String.fromCharCode(15)+"0", "\x0F0" ); // G is out of hex range - +/* Invalid testcase: we no longer silently ignore invalid hexadecimal escape sequences. new TestCase( SECTION, "\\xG", "xG", "\xG" ); new TestCase( SECTION, "\\xCG", "xCG", "\xCG" ); +*/ // DoubleStringCharacter::EscapeSequence::CharacterEscapeSequence::\ NonEscapeCharacter new TestCase( SECTION, "\\a", "a", "\a" ); diff --git a/tests/auto/qml/parserstress/tests/ecma_2/RegExp/hex-001.js b/tests/auto/qml/parserstress/tests/ecma_2/RegExp/hex-001.js index 3e85ac7abf..f2dccd9267 100644 --- a/tests/auto/qml/parserstress/tests/ecma_2/RegExp/hex-001.js +++ b/tests/auto/qml/parserstress/tests/ecma_2/RegExp/hex-001.js @@ -55,7 +55,10 @@ startTest(); AddRegExpCases( new RegExp("\x41"), "new RegExp('\\x41')", "A", "A", 1, 0, ["A"] ); AddRegExpCases( new RegExp("\x412"),"new RegExp('\\x412')", "A2", "A2", 1, 0, ["A2"] ); + +/* Invalid testcase: we no longer silently ignore invalid hexadecimal escape sequences. AddRegExpCases( new RegExp("\x1g"), "new RegExp('\\x1g')", "x1g","x1g", 1, 0, ["x1g"] ); +*/ AddRegExpCases( new RegExp("A"), "new RegExp('A')", "\x41", "\\x41", 1, 0, ["A"] ); AddRegExpCases( new RegExp("A"), "new RegExp('A')", "\x412", "\\x412", 1, 0, ["A"] ); diff --git a/tests/auto/qml/qmlmin/tst_qmlmin.cpp b/tests/auto/qml/qmlmin/tst_qmlmin.cpp index 2957a70d04..65549efddc 100644 --- a/tests/auto/qml/qmlmin/tst_qmlmin.cpp +++ b/tests/auto/qml/qmlmin/tst_qmlmin.cpp @@ -126,6 +126,7 @@ void tst_qmlmin::initTestCase() invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.3.qml"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.4.qml"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.5.qml"; + invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.6.qml"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.1.qml"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.2.qml"; } diff --git a/tests/auto/qml/qqmlecmascript/data/stringParsing_error.6.qml b/tests/auto/qml/qqmlecmascript/data/stringParsing_error.6.qml new file mode 100644 index 0000000000..8ee5b59d9e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/stringParsing_error.6.qml @@ -0,0 +1,9 @@ + +import QtQuick 2.0 + +QtObject { + function code() { + "\x0G" + } +} + diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index d022e9dedc..06590f0ad6 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -7383,7 +7383,7 @@ void tst_qqmlecmascript::numberParsing() void tst_qqmlecmascript::stringParsing() { - for (int i = 1; i < 6; ++i) { + for (int i = 1; i < 7; ++i) { QString file("stringParsing_error.%1.qml"); file = file.arg(i); QQmlComponent component(&engine, testFileUrl(file)); |