Bug 1325995 - Rewrite shader comment truncation. - r=daoshengmu

MozReview-Commit-ID: KvgQhxAnDQl
This commit is contained in:
Jeff Gilbert 2016-12-27 17:47:19 -08:00
Родитель 20bc668a02
Коммит 43c4b02d0c
3 изменённых файлов: 121 добавлений и 212 удалений

Просмотреть файл

@ -158,19 +158,24 @@ WebGLShader::~WebGLShader()
void
WebGLShader::ShaderSource(const nsAString& source)
{
StripComments stripComments(source);
const nsAString& cleanSource = Substring(stripComments.result().Elements(),
stripComments.length());
if (!ValidateGLSLString(cleanSource, mContext, "shaderSource"))
const char funcName[] = "shaderSource";
nsString sourceWithoutComments;
if (!TruncateComments(source, &sourceWithoutComments)) {
mContext->ErrorOutOfMemory("%s: Failed to alloc for empting comment contents.",
funcName);
return;
}
if (!ValidateGLSLString(sourceWithoutComments, mContext, funcName))
return;
// We checked that the source stripped of comments is in the
// 7-bit ASCII range, so we can skip the NS_IsAscii() check.
const NS_LossyConvertUTF16toASCII sourceCString(cleanSource);
const NS_LossyConvertUTF16toASCII cleanSource(sourceWithoutComments);
if (mContext->gl->WorkAroundDriverBugs()) {
const size_t maxSourceLength = 0x3ffff;
if (sourceCString.Length() > maxSourceLength) {
if (cleanSource.Length() > maxSourceLength) {
mContext->ErrorInvalidValue("shaderSource: Source has more than %d"
" characters. (Driver workaround)",
maxSourceLength);
@ -185,20 +190,28 @@ WebGLShader::ShaderSource(const nsAString& source)
// Wow - Roll Your Own Foreach-Lines because printf_stderr has a hard-coded
// internal size, so long strings are truncated.
int32_t start = 0;
int32_t end = sourceCString.Find("\n", false, start, -1);
while (end > -1) {
const nsCString line(sourceCString.BeginReading() + start, end - start);
printf_stderr("%s\n", line.BeginReading());
start = end + 1;
end = sourceCString.Find("\n", false, start, -1);
const size_t maxChunkSize = 1024-1; // -1 for null-term.
const UniqueBuffer buf(moz_xmalloc(maxChunkSize+1)); // +1 for null-term
const auto bufBegin = (char*)buf.get();
size_t chunkStart = 0;
while (chunkStart != cleanSource.Length()) {
const auto chunkEnd = std::min(chunkStart + maxChunkSize,
size_t(cleanSource.Length()));
const auto chunkSize = chunkEnd - chunkStart;
memcpy(bufBegin, cleanSource.BeginReading() + chunkStart, chunkSize);
bufBegin[chunkSize + 1] = '\0';
printf_stderr("%s", bufBegin);
chunkStart += chunkSize;
}
printf_stderr("////////////////////////////////////////\n");
}
mSource = source;
mCleanSource = sourceCString;
mCleanSource = cleanSource;
}
void

Просмотреть файл

@ -31,96 +31,100 @@ bool IsValidGLSLCharacter(char16_t c)
return false;
}
void StripComments::process(char16_t c)
{
if (isNewline(c)) {
// No matter what state we are in, pass through newlines
// so we preserve line numbers.
emit(c);
if (m_parseState != InMultiLineComment)
m_parseState = BeginningOfLine;
return;
}
char16_t temp = 0;
switch (m_parseState) {
case BeginningOfLine:
// If it's an ASCII space.
if (c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9))) {
emit(c);
break;
}
if (c == '#') {
m_parseState = InPreprocessorDirective;
emit(c);
break;
}
// Transition to normal state and re-handle character.
m_parseState = MiddleOfLine;
process(c);
break;
case MiddleOfLine:
if (c == '/' && peek(temp)) {
if (temp == '/') {
m_parseState = InSingleLineComment;
emit(' ');
advance();
break;
}
if (temp == '*') {
m_parseState = InMultiLineComment;
// Emit the comment start in case the user has
// an unclosed comment and we want to later
// signal an error.
emit('/');
emit('*');
advance();
break;
}
}
emit(c);
break;
case InPreprocessorDirective:
// No matter what the character is, just pass it
// through. Do not parse comments in this state. This
// might not be the right thing to do long term, but it
// should handle the #error preprocessor directive.
emit(c);
break;
case InSingleLineComment:
// The newline code at the top of this function takes care
// of resetting our state when we get out of the
// single-line comment. Swallow all other characters.
break;
case InMultiLineComment:
if (c == '*' && peek(temp) && temp == '/') {
emit('*');
emit('/');
m_parseState = MiddleOfLine;
advance();
break;
}
// Swallow all other characters. Unclear whether we may
// want or need to just emit a space per character to try
// to preserve column numbers for debugging purposes.
break;
}
}
/****** END CODE TAKEN FROM WEBKIT ******/
bool
TruncateComments(const nsAString& src, nsAString* const out)
{
const size_t dstByteCount = src.Length() * sizeof(src[0]);
const UniqueBuffer dst(malloc(dstByteCount));
if (!dst)
return false;
auto srcItr = src.BeginReading();
const auto srcEnd = src.EndReading();
const auto dstBegin = (decltype(src[0])*)dst.get();
auto dstItr = dstBegin;
const auto fnEmitUntil = [&](const decltype(srcItr)& nextSrcItr) {
while (srcItr != nextSrcItr) {
*dstItr = *srcItr;
++srcItr;
++dstItr;
}
};
const auto fnFindSoonestOf = [&](const nsString* needles, size_t needleCount,
size_t* const out_foundId)
{
auto foundItr = srcItr;
while (foundItr != srcEnd) {
const auto haystack = Substring(foundItr, srcEnd);
for (size_t i = 0; i < needleCount; i++) {
if (StringBeginsWith(haystack, needles[i])) {
*out_foundId = i;
return foundItr;
}
}
++foundItr;
}
*out_foundId = needleCount;
return foundItr;
};
////
const nsString commentBeginnings[] = { NS_LITERAL_STRING("//"),
NS_LITERAL_STRING("/*"),
nsString() };
const nsString lineCommentEndings[] = { NS_LITERAL_STRING("\\\n"),
NS_LITERAL_STRING("\n"),
nsString() };
const nsString blockCommentEndings[] = { NS_LITERAL_STRING("\n"),
NS_LITERAL_STRING("*/"),
nsString() };
while (srcItr != srcEnd) {
size_t foundId;
fnEmitUntil( fnFindSoonestOf(commentBeginnings, 2, &foundId) );
fnEmitUntil(srcItr + commentBeginnings[foundId].Length());
switch (foundId) {
case 0: // line comment
while (true) {
size_t endId;
srcItr = fnFindSoonestOf(lineCommentEndings, 2, &endId);
fnEmitUntil(srcItr + lineCommentEndings[endId].Length());
if (endId == 0)
continue;
break;
}
break;
case 1: // block comment
while (true) {
size_t endId;
srcItr = fnFindSoonestOf(blockCommentEndings, 2, &endId);
fnEmitUntil(srcItr + blockCommentEndings[endId].Length());
if (endId == 0)
continue;
break;
}
break;
default: // not found
break;
}
}
MOZ_ASSERT((dstBegin+1) - dstBegin == 1);
const uint32_t dstCharLen = dstItr - dstBegin;
if (!out->Assign(dstBegin, dstCharLen, mozilla::fallible))
return false;
return true;
}
bool
ValidateGLSLString(const nsAString& string, WebGLContext* webgl, const char* funcName)
{

Просмотреть файл

@ -34,116 +34,8 @@ namespace mozilla {
class WebGLContext;
// The following code was taken from the WebKit WebGL implementation,
// which can be found here:
// http://trac.webkit.org/browser/trunk/Source/WebCore/html/canvas/WebGLRenderingContext.cpp?rev=93625#L121
// Note that some modifications were done to adapt it to Mozilla.
/****** BEGIN CODE TAKEN FROM WEBKIT ******/
// Strips comments from shader text. This allows non-ASCII characters
// to be used in comments without potentially breaking OpenGL
// implementations not expecting characters outside the GLSL ES set.
class StripComments {
public:
explicit StripComments(const nsAString& str)
: m_parseState(BeginningOfLine)
, m_end(str.EndReading())
, m_current(str.BeginReading())
, m_position(0)
{
m_result.SetLength(str.Length());
parse();
}
const nsTArray<char16_t>& result()
{
return m_result;
}
size_t length()
{
return m_position;
}
private:
bool hasMoreCharacters()
{
return (m_current < m_end);
}
void parse()
{
while (hasMoreCharacters()) {
process(current());
// process() might advance the position.
if (hasMoreCharacters())
advance();
}
}
void process(char16_t);
bool peek(char16_t& character)
{
if (m_current + 1 >= m_end)
return false;
character = *(m_current + 1);
return true;
}
char16_t current()
{
//ASSERT(m_position < m_length);
return *m_current;
}
void advance()
{
++m_current;
}
bool isNewline(char16_t character)
{
// Don't attempt to canonicalize newline related characters.
return (character == '\n' || character == '\r');
}
void emit(char16_t character)
{
m_result[m_position++] = character;
}
enum ParseState {
// Have not seen an ASCII non-whitespace character yet on
// this line. Possible that we might see a preprocessor
// directive.
BeginningOfLine,
// Have seen at least one ASCII non-whitespace character
// on this line.
MiddleOfLine,
// Handling a preprocessor directive. Passes through all
// characters up to the end of the line. Disables comment
// processing.
InPreprocessorDirective,
// Handling a single-line comment. The comment text is
// replaced with a single space.
InSingleLineComment,
// Handling a multi-line comment. Newlines are passed
// through to preserve line numbers.
InMultiLineComment
};
ParseState m_parseState;
const char16_t* m_end;
const char16_t* m_current;
size_t m_position;
nsTArray<char16_t> m_result;
};
/****** END CODE TAKEN FROM WEBKIT ******/
bool
TruncateComments(const nsAString& src, nsAString* const out);
bool ValidateGLSLString(const nsAString& string, WebGLContext* webgl,
const char* funcName);