Prevent stack overflow due to recursive swizzle of an l-value

Long chains of recursive swizzling could previously cause a stack
overflow in checkCanBeLValue. Fold recursive swizzling when it is
parsed to prevent this.

BUG=angleproject:2439
TEST=angle_unittests

Change-Id: I83b4c27442185709f6762d5ec23b93244010da05
Reviewed-on: https://chromium-review.googlesource.com/983593
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/compiler/translator/IntermNode.cpp b/src/compiler/translator/IntermNode.cpp
index 1004f27..4e25c44 100644
--- a/src/compiler/translator/IntermNode.cpp
+++ b/src/compiler/translator/IntermNode.cpp
@@ -795,6 +795,7 @@
     ASSERT(operandCopy != nullptr);
     mOperand = operandCopy;
     mSwizzleOffsets = node.mSwizzleOffsets;
+    mHasFoldedDuplicateOffsets = node.mHasFoldedDuplicateOffsets;
 }
 
 TIntermBinary::TIntermBinary(const TIntermBinary &node)
@@ -1041,7 +1042,8 @@
 TIntermSwizzle::TIntermSwizzle(TIntermTyped *operand, const TVector<int> &swizzleOffsets)
     : TIntermExpression(TType(EbtFloat, EbpUndefined)),
       mOperand(operand),
-      mSwizzleOffsets(swizzleOffsets)
+      mSwizzleOffsets(swizzleOffsets),
+      mHasFoldedDuplicateOffsets(false)
 {
     ASSERT(mSwizzleOffsets.size() <= 4);
     promote();
@@ -1167,6 +1169,10 @@
 
 bool TIntermSwizzle::hasDuplicateOffsets() const
 {
+    if (mHasFoldedDuplicateOffsets)
+    {
+        return true;
+    }
     int offsetCount[4] = {0u, 0u, 0u, 0u};
     for (const auto offset : mSwizzleOffsets)
     {
@@ -1179,6 +1185,11 @@
     return false;
 }
 
+void TIntermSwizzle::setHasFoldedDuplicateOffsets(bool hasFoldedDuplicateOffsets)
+{
+    mHasFoldedDuplicateOffsets = hasFoldedDuplicateOffsets;
+}
+
 bool TIntermSwizzle::offsetsMatch(int offset) const
 {
     return mSwizzleOffsets.size() == 1 && mSwizzleOffsets[0] == offset;
@@ -1472,6 +1483,24 @@
 
 TIntermTyped *TIntermSwizzle::fold(TDiagnostics * /* diagnostics */)
 {
+    TIntermSwizzle *operandSwizzle = mOperand->getAsSwizzleNode();
+    if (operandSwizzle)
+    {
+        // We need to fold the two swizzles into one, so that repeated swizzling can't cause stack
+        // overflow in ParseContext::checkCanBeLValue().
+        bool hadDuplicateOffsets = operandSwizzle->hasDuplicateOffsets();
+        TVector<int> foldedOffsets;
+        for (int offset : mSwizzleOffsets)
+        {
+            // Offset should already be validated.
+            ASSERT(static_cast<size_t>(offset) < operandSwizzle->mSwizzleOffsets.size());
+            foldedOffsets.push_back(operandSwizzle->mSwizzleOffsets[offset]);
+        }
+        operandSwizzle->mSwizzleOffsets = foldedOffsets;
+        operandSwizzle->setType(getType());
+        operandSwizzle->setHasFoldedDuplicateOffsets(hadDuplicateOffsets);
+        return operandSwizzle;
+    }
     TIntermConstantUnion *operandConstant = mOperand->getAsConstantUnion();
     if (operandConstant == nullptr)
     {