Add type comparison, type retrieval, original name retrieval to ShaderVariable.

This is needed to effectively use the new APIs to get shader variable info.

BUG=angle::770
TEST=ShaderVariableTest

Change-Id: Ia591eb567868ebe898f4a7449c64167ad212f59b
Reviewed-on: https://chromium-review.googlesource.com/221388
Tested-by: Zhenyao Mo <zmo@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Kenneth Russell <kbr@chromium.org>
Reviewed-by: Shannon Woods <shannonwoods@chromium.org>
diff --git a/include/GLSLANG/ShaderVars.h b/include/GLSLANG/ShaderVars.h
index 9c38647..da21c3e 100644
--- a/include/GLSLANG/ShaderVars.h
+++ b/include/GLSLANG/ShaderVars.h
@@ -52,6 +52,21 @@
     unsigned int elementCount() const { return std::max(1u, arraySize); }
     bool isStruct() const { return !fields.empty(); }
 
+    // All of the shader's variables are described using nested data
+    // structures. This is needed in order to disambiguate similar looking
+    // types, such as two structs containing the same fields, but in
+    // different orders. "findInfoByMappedName" provides an easy query for
+    // users to dive into the data structure and fetch the unique variable
+    // instance corresponding to a dereferencing chain of the top-level
+    // variable.
+    // Given a mapped name like 'a[0].b.c[0]', return the ShaderVariable
+    // that defines 'c' in |leafVar|, and the original name 'A[0].B.C[0]'
+    // in |originalName|, based on the assumption that |this| defines 'a'.
+    // If no match is found, return false.
+    bool findInfoByMappedName(const std::string &mappedFullName,
+                              const ShaderVariable **leafVar,
+                              std::string* originalFullName) const;
+
     GLenum type;
     GLenum precision;
     std::string name;
@@ -60,6 +75,16 @@
     bool staticUse;
     std::vector<ShaderVariable> fields;
     std::string structName;
+
+  protected:
+    bool isSameVariableAtLinkTime(const ShaderVariable &other,
+                                  bool matchPrecision) const;
+
+    bool operator==(const ShaderVariable &other) const;
+    bool operator!=(const ShaderVariable &other) const
+    {
+        return !operator==(other);
+    }
 };
 
 struct COMPILER_EXPORT Uniform : public ShaderVariable
@@ -68,6 +93,16 @@
     ~Uniform();
     Uniform(const Uniform &other);
     Uniform &operator=(const Uniform &other);
+    bool operator==(const Uniform &other) const;
+    bool operator!=(const Uniform &other) const
+    {
+        return !operator==(other);
+    }
+
+    // Decide whether two uniforms are the same at shader link time,
+    // assuming one from vertex shader and the other from fragment shader.
+    // See GLSL ES Spec 3.00.3, sec 4.3.5.
+    bool isSameUniformAtLinkTime(const Uniform &other) const;
 };
 
 struct COMPILER_EXPORT Attribute : public ShaderVariable
@@ -76,6 +111,11 @@
     ~Attribute();
     Attribute(const Attribute &other);
     Attribute &operator=(const Attribute &other);
+    bool operator==(const Attribute &other) const;
+    bool operator!=(const Attribute &other) const
+    {
+        return !operator==(other);
+    }
 
     int location;
 };
@@ -86,6 +126,18 @@
     ~InterfaceBlockField();
     InterfaceBlockField(const InterfaceBlockField &other);
     InterfaceBlockField &operator=(const InterfaceBlockField &other);
+    bool operator==(const InterfaceBlockField &other) const;
+    bool operator!=(const InterfaceBlockField &other) const
+    {
+        return !operator==(other);
+    }
+
+    // Decide whether two InterfaceBlock fields are the same at shader
+    // link time, assuming one from vertex shader and the other from
+    // fragment shader.
+    // See GLSL ES Spec 3.00.3, sec 4.3.7.
+    bool isSameInterfaceBlockFieldAtLinkTime(
+        const InterfaceBlockField &other) const;
 
     bool isRowMajorLayout;
 };
@@ -94,8 +146,18 @@
 {
     Varying();
     ~Varying();
-    Varying(const Varying &other);
+    Varying(const Varying &otherg);
     Varying &operator=(const Varying &other);
+    bool operator==(const Varying &other) const;
+    bool operator!=(const Varying &other) const
+    {
+        return !operator==(other);
+    }
+
+    // Decide whether two varyings are the same at shader link time,
+    // assuming one from vertex shader and the other from fragment shader.
+    // See GLSL ES Spec 3.00.3, sec 4.3.9.
+    bool isSameVaryingAtLinkTime(const Varying &other) const;
 
     InterpolationType interpolation;
     bool isInvariant;
diff --git a/src/compiler/translator/ShaderVars.cpp b/src/compiler/translator/ShaderVars.cpp
index 822c558..3098a7f 100644
--- a/src/compiler/translator/ShaderVars.cpp
+++ b/src/compiler/translator/ShaderVars.cpp
@@ -9,6 +9,8 @@
 
 #include <GLSLANG/ShaderLang.h>
 
+#include "compiler/translator/compilerdebug.h"
+
 namespace sh
 {
 
@@ -53,6 +55,126 @@
     return *this;
 }
 
+bool ShaderVariable::operator==(const ShaderVariable &other) const
+{
+    if (type != other.type ||
+        precision != other.precision ||
+        name != other.name ||
+        mappedName != other.mappedName ||
+        arraySize != other.arraySize ||
+        staticUse != other.staticUse ||
+        fields.size() != other.fields.size() ||
+        structName != other.structName)
+    {
+        return false;
+    }
+    for (size_t ii = 0; ii < fields.size(); ++ii)
+    {
+        if (fields[ii] != other.fields[ii])
+            return false;
+    }
+    return true;
+}
+
+bool ShaderVariable::findInfoByMappedName(
+    const std::string &mappedFullName,
+    const ShaderVariable **leafVar, std::string *originalFullName) const
+{
+    ASSERT(leafVar && originalFullName);
+    // There are three cases:
+    // 1) the top variable is of struct type;
+    // 2) the top variable is an array;
+    // 3) otherwise.
+    size_t pos = mappedFullName.find_first_of(".[");
+    std::string topName;
+
+    if (pos == std::string::npos)
+    {
+        // Case 3.
+        if (mappedFullName != this->mappedName)
+            return false;
+        *originalFullName = this->name;
+        *leafVar = this;
+        return true;
+    }
+    else
+    {
+        std::string topName = mappedFullName.substr(0, pos);
+        if (topName != this->mappedName)
+            return false;
+        std::string originalName = this->name;
+        std::string remaining;
+        if (mappedFullName[pos] == '[')
+        {
+            // Case 2.
+            size_t closePos = mappedFullName.find_first_of(']');
+            if (closePos < pos || closePos == std::string::npos)
+                return false;
+            // Append '[index]'.
+            originalName += mappedFullName.substr(pos, closePos - pos + 1);
+            if (closePos + 1 == mappedFullName.size())
+            {
+                *originalFullName = originalName;
+                *leafVar = this;
+                return true;
+            }
+            else
+            {
+                // In the form of 'a[0].b', so after ']', '.' is expected.
+                if (mappedFullName[closePos + 1]  != '.')
+                    return false;
+                remaining = mappedFullName.substr(closePos + 2);  // Skip "]."
+            }
+        }
+        else
+        {
+            // Case 1.
+            remaining = mappedFullName.substr(pos + 1);  // Skip "."
+        }
+        for (size_t ii = 0; ii < this->fields.size(); ++ii)
+        {
+            const ShaderVariable *fieldVar = NULL;
+            std::string originalFieldName;
+            bool found = fields[ii].findInfoByMappedName(
+                remaining, &fieldVar, &originalFieldName);
+            if (found)
+            {
+                *originalFullName = originalName + "." + originalFieldName;
+                *leafVar = fieldVar;
+                return true;
+            }
+        }
+        return false;
+    }
+}
+
+bool ShaderVariable::isSameVariableAtLinkTime(
+    const ShaderVariable &other, bool matchPrecision) const
+{
+    if (type != other.type)
+        return false;
+    if (matchPrecision && precision != other.precision)
+        return false;
+    if (name != other.name)
+        return false;
+    ASSERT(mappedName == other.mappedName);
+    if (arraySize != other.arraySize)
+        return false;
+    if (fields.size() != other.fields.size())
+        return false;
+    for (size_t ii = 0; ii < fields.size(); ++ii)
+    {
+        if (!fields[ii].isSameVariableAtLinkTime(other.fields[ii],
+                                                 matchPrecision))
+        {
+            return false;
+        }
+    }
+    if (structName != other.structName)
+        return false;
+    return true;
+}
+
 Uniform::Uniform()
 {}
 
@@ -69,6 +191,16 @@
     return *this;
 }
 
+bool Uniform::operator==(const Uniform &other) const
+{
+    return ShaderVariable::operator==(other);
+}
+
+bool Uniform::isSameUniformAtLinkTime(const Uniform &other) const
+{
+    return ShaderVariable::isSameVariableAtLinkTime(other, true);
+}
+
 Attribute::Attribute()
     : location(-1)
 {}
@@ -88,6 +220,12 @@
     return *this;
 }
 
+bool Attribute::operator==(const Attribute &other) const
+{
+    return (ShaderVariable::operator==(other) &&
+            location == other.location);
+}
+
 InterfaceBlockField::InterfaceBlockField()
     : isRowMajorLayout(false)
 {}
@@ -107,6 +245,19 @@
     return *this;
 }
 
+bool InterfaceBlockField::operator==(const InterfaceBlockField &other) const
+{
+    return (ShaderVariable::operator==(other) &&
+            isRowMajorLayout == other.isRowMajorLayout);
+}
+
+bool InterfaceBlockField::isSameInterfaceBlockFieldAtLinkTime(
+    const InterfaceBlockField &other) const
+{
+    return (ShaderVariable::isSameVariableAtLinkTime(other, true) &&
+            isRowMajorLayout == other.isRowMajorLayout);
+}
+
 Varying::Varying()
     : interpolation(INTERPOLATION_SMOOTH),
       isInvariant(false)
@@ -129,6 +280,20 @@
     return *this;
 }
 
+bool Varying::operator==(const Varying &other) const
+{
+    return (ShaderVariable::operator==(other) &&
+            interpolation == other.interpolation &&
+            isInvariant == other.isInvariant);
+}
+
+bool Varying::isSameVaryingAtLinkTime(const Varying &other) const
+{
+    return (ShaderVariable::isSameVariableAtLinkTime(other, false) &&
+            interpolation == other.interpolation &&
+            isInvariant == other.isInvariant);
+}
+
 InterfaceBlock::InterfaceBlock()
     : arraySize(0),
       layout(BLOCKLAYOUT_PACKED),
diff --git a/tests/compiler_tests/ShaderVariable_test.cpp b/tests/compiler_tests/ShaderVariable_test.cpp
new file mode 100644
index 0000000..b642260
--- /dev/null
+++ b/tests/compiler_tests/ShaderVariable_test.cpp
@@ -0,0 +1,221 @@
+//
+// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// CollectVariables_test.cpp:
+//   Some tests for shader inspection
+//
+
+#include "angle_gl.h"
+#include "gtest/gtest.h"
+#include "GLSLANG/ShaderLang.h"
+
+namespace sh
+{
+
+TEST(ShaderVariableTest, FindInfoByMappedName)
+{
+    // struct A {
+    //   float x[2];
+    //   vec3 y;
+    // };
+    // struct B {
+    //   A a[3];
+    // };
+    // B uni[2];
+    ShaderVariable uni;
+    uni.arraySize = 2;
+    uni.name = "uni";
+    uni.mappedName = "m_uni";
+    uni.structName = "B";
+    {
+        ShaderVariable a;
+        a.arraySize = 3;
+        a.name = "a";
+        a.mappedName = "m_a";
+        a.structName = "A";
+        {
+            ShaderVariable x(GL_FLOAT, 2);
+            x.name = "x";
+            x.mappedName = "m_x";
+            a.fields.push_back(x);
+
+            ShaderVariable y(GL_FLOAT_VEC3, 0);
+            y.name = "y";
+            y.mappedName = "m_y";
+            a.fields.push_back(y);
+        }
+        uni.fields.push_back(a);
+    }
+
+    const ShaderVariable *leafVar = NULL;
+    std::string originalFullName;
+
+    std::string mappedFullName = "wrongName";
+    EXPECT_FALSE(uni.findInfoByMappedName(
+        mappedFullName, &leafVar, &originalFullName));
+
+    mappedFullName = "m_uni";
+    EXPECT_TRUE(uni.findInfoByMappedName(
+        mappedFullName, &leafVar, &originalFullName));
+    EXPECT_EQ(&uni, leafVar);
+    EXPECT_STREQ("uni", originalFullName.c_str());
+
+    mappedFullName = "m_uni[0].m_a[1].wrongName";
+    EXPECT_FALSE(uni.findInfoByMappedName(
+        mappedFullName, &leafVar, &originalFullName));
+
+    mappedFullName = "m_uni[0].m_a[1].m_x";
+    EXPECT_TRUE(uni.findInfoByMappedName(
+        mappedFullName, &leafVar, &originalFullName));
+    EXPECT_EQ(&(uni.fields[0].fields[0]), leafVar);
+    EXPECT_STREQ("uni[0].a[1].x", originalFullName.c_str());
+
+    mappedFullName = "m_uni[0].m_a[1].m_x[0]";
+    EXPECT_TRUE(uni.findInfoByMappedName(
+        mappedFullName, &leafVar, &originalFullName));
+    EXPECT_EQ(&(uni.fields[0].fields[0]), leafVar);
+    EXPECT_STREQ("uni[0].a[1].x[0]", originalFullName.c_str());
+
+    mappedFullName = "m_uni[0].m_a[1].m_y";
+    EXPECT_TRUE(uni.findInfoByMappedName(
+        mappedFullName, &leafVar, &originalFullName));
+    EXPECT_EQ(&(uni.fields[0].fields[1]), leafVar);
+    EXPECT_STREQ("uni[0].a[1].y", originalFullName.c_str());
+}
+
+TEST(ShaderVariableTest, IsSameUniformWithDifferentFieldOrder)
+{
+    // struct A {
+    //   float x;
+    //   float y;
+    // };
+    // uniform A uni;
+    Uniform vx_a;
+    vx_a.arraySize = 0;
+    vx_a.name = "uni";
+    vx_a.mappedName = "m_uni";
+    vx_a.structName = "A";
+    {
+        ShaderVariable x(GL_FLOAT, 0);
+        x.name = "x";
+        x.mappedName = "m_x";
+        vx_a.fields.push_back(x);
+
+        ShaderVariable y(GL_FLOAT, 0);
+        y.name = "y";
+        y.mappedName = "m_y";
+        vx_a.fields.push_back(y);
+    }
+
+    // struct A {
+    //   float y;
+    //   float x;
+    // };
+    // uniform A uni;
+    Uniform fx_a;
+    fx_a.arraySize = 0;
+    fx_a.name = "uni";
+    fx_a.mappedName = "m_uni";
+    fx_a.structName = "A";
+    {
+        ShaderVariable y(GL_FLOAT, 0);
+        y.name = "y";
+        y.mappedName = "m_y";
+        fx_a.fields.push_back(y);
+
+        ShaderVariable x(GL_FLOAT, 0);
+        x.name = "x";
+        x.mappedName = "m_x";
+        fx_a.fields.push_back(x);
+    }
+
+    EXPECT_FALSE(vx_a.isSameUniformAtLinkTime(fx_a));
+}
+
+TEST(ShaderVariableTest, IsSameUniformWithDifferentStructNames)
+{
+    // struct A {
+    //   float x;
+    //   float y;
+    // };
+    // uniform A uni;
+    Uniform vx_a;
+    vx_a.arraySize = 0;
+    vx_a.name = "uni";
+    vx_a.mappedName = "m_uni";
+    vx_a.structName = "A";
+    {
+        ShaderVariable x(GL_FLOAT, 0);
+        x.name = "x";
+        x.mappedName = "m_x";
+        vx_a.fields.push_back(x);
+
+        ShaderVariable y(GL_FLOAT, 0);
+        y.name = "y";
+        y.mappedName = "m_y";
+        vx_a.fields.push_back(y);
+    }
+
+    // struct B {
+    //   float x;
+    //   float y;
+    // };
+    // uniform B uni;
+    Uniform fx_a;
+    fx_a.arraySize = 0;
+    fx_a.name = "uni";
+    fx_a.mappedName = "m_uni";
+    {
+        ShaderVariable x(GL_FLOAT, 0);
+        x.name = "x";
+        x.mappedName = "m_x";
+        fx_a.fields.push_back(x);
+
+        ShaderVariable y(GL_FLOAT, 0);
+        y.name = "y";
+        y.mappedName = "m_y";
+        fx_a.fields.push_back(y);
+    }
+
+    fx_a.structName = "B";
+    EXPECT_FALSE(vx_a.isSameUniformAtLinkTime(fx_a));
+
+    fx_a.structName = "A";
+    EXPECT_TRUE(vx_a.isSameUniformAtLinkTime(fx_a));
+
+    fx_a.structName = "";
+    EXPECT_FALSE(vx_a.isSameUniformAtLinkTime(fx_a));
+}
+
+TEST(ShaderVariableTest, IsSameVaryingWithDifferentInvariance)
+{
+    // invariant varying float vary;
+    Varying vx;
+    vx.type = GL_FLOAT;
+    vx.arraySize = 0;
+    vx.precision = GL_MEDIUM_FLOAT;
+    vx.name = "vary";
+    vx.mappedName = "m_vary";
+    vx.staticUse = true;
+    vx.isInvariant = true;
+
+    // varying float vary;
+    Varying fx;
+    fx.type = GL_FLOAT;
+    fx.arraySize = 0;
+    fx.precision = GL_MEDIUM_FLOAT;
+    fx.name = "vary";
+    fx.mappedName = "m_vary";
+    fx.staticUse = true;
+    fx.isInvariant = false;
+
+    EXPECT_FALSE(vx.isSameVaryingAtLinkTime(fx));
+
+    // invariant varying float vary;
+    fx.isInvariant = true;
+    EXPECT_TRUE(vx.isSameVaryingAtLinkTime(fx));
+}
+
+}  // namespace sh