Edit

kc3-lang/angle/src/compiler/translator/gen_builtin_symbols.py

Branch :

  • Show log

    Commit

  • Author : Clemen Deng
    Date : 2019-08-21 15:22:49
    Hash : d7d42540
    Message : Don't build symbol table for GLSL built-ins if on Android The GLSL + ESSL autogenerated symbol table is too large for android, and android also doesn't need desktop GL functionality If on android, compile the ESSL only symbol table Bug: chromium:996286 Change-Id: I14dfc7748dae389e78c35f82a390c67962665356 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1757372 Commit-Queue: Clemen Deng <clemendeng@google.com> Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Geoff Lang <geofflang@chromium.org>

  • src/compiler/translator/gen_builtin_symbols.py
  • #!/usr/bin/env vpython
    #
    # [VPYTHON:BEGIN]
    # wheel: <
    #   name: "infra/python/wheels/perfect-hash-py2_py3"
    #   version: "version:0.2.1"
    # >
    # [VPYTHON:END]
    #
    # Copyright 2018 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.
    #
    # gen_builtin_symbols.py:
    #  Code generation for the built-in symbol tables.
    
    from collections import OrderedDict
    from datetime import date
    from perfect_hash import generate_hash, Hash2
    import argparse
    import hashlib
    import json
    import re
    import os
    import sys
    import random
    
    template_immutablestring_cpp = """// GENERATED FILE - DO NOT EDIT.
    // Generated by {script_name} using data from {variable_data_source_name} and
    // {function_data_source_name}.
    //
    // Copyright {copyright_year} 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.
    //
    // ImmutableString_{source_label}autogen.cpp: Wrapper for static or pool allocated char arrays, that are guaranteed to be
    // valid and unchanged for the duration of the compilation.
    // Implements mangledNameHash using perfect hash function from gen_builtin_symbols.py
    
    #include "compiler/translator/ImmutableString.h"
    
    std::ostream &operator<<(std::ostream &os, const sh::ImmutableString &str)
    {{
        return os.write(str.data(), str.length());
    }}
    
    #if defined(_MSC_VER)
    #    pragma warning(disable : 4309)  // truncation of constant value
    #endif
    
    
    namespace
    {{
    
    constexpr int mangledkT1[] = {{{mangled_S1}}};
    constexpr int mangledkT2[] = {{{mangled_S2}}};
    constexpr int mangledkG[] = {{{mangled_G}}};
    
    int MangledHashG(const char *key, const int *T)
    {{
        int sum = 0;
    
        for (int i = 0; key[i] != '\\0'; i++)
        {{
            sum += T[i] * key[i];
            sum %= {mangled_NG};
        }}
        return mangledkG[sum];
    }}
    
    int MangledPerfectHash(const char *key)
    {{
        if (strlen(key) > {mangled_NS})
            return 0;
    
        return (MangledHashG(key, mangledkT1) + MangledHashG(key, mangledkT2)) % {mangled_NG};
    }}
    
    constexpr int unmangledkT1[] = {{{unmangled_S1}}};
    constexpr int unmangledkT2[] = {{{unmangled_S2}}};
    constexpr int unmangledkG[] = {{{unmangled_G}}};
    
    int UnmangledHashG(const char *key, const int *T)
    {{
        int sum = 0;
    
        for (int i = 0; key[i] != '\\0'; i++)
        {{
            sum += T[i] * key[i];
            sum %= {unmangled_NG};
        }}
        return unmangledkG[sum];
    }}
    
    int UnmangledPerfectHash(const char *key)
    {{
        if (strlen(key) > {unmangled_NS})
            return 0;
    
        return (UnmangledHashG(key, unmangledkT1) + UnmangledHashG(key, unmangledkT2)) % {unmangled_NG};
    }}
    
    }}
    
    namespace sh
    {{
    
    template <>
    const size_t ImmutableString::FowlerNollVoHash<4>::kFnvPrime = 16777619u;
    
    template <>
    const size_t ImmutableString::FowlerNollVoHash<4>::kFnvOffsetBasis = 0x811c9dc5u;
    
    template <>
    const size_t ImmutableString::FowlerNollVoHash<8>::kFnvPrime =
        static_cast<size_t>(1099511628211ull);
    
    template <>
    const size_t ImmutableString::FowlerNollVoHash<8>::kFnvOffsetBasis =
        static_cast<size_t>(0xcbf29ce484222325ull);
    
    uint32_t ImmutableString::mangledNameHash() const
    {{
        return MangledPerfectHash(data());
    }}
    
    uint32_t ImmutableString::unmangledNameHash() const
    {{
        return UnmangledPerfectHash(data());
    }}
    
    }}  // namespace sh
    """
    
    template_immutablestringtest_cpp = """// GENERATED FILE - DO NOT EDIT.
    // Generated by {script_name} using data from {function_data_source_name}.
    //
    // Copyright {copyright_year} 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.
    //
    // ImmutableString_test_{source_label}autogen.cpp:
    //   Tests for matching script-generated hashes with runtime computed hashes.
    
    #include "compiler/translator/ImmutableString.h"
    #include "gtest/gtest.h"
    
    namespace sh
    {{
    
    TEST(ImmutableStringTest, ScriptGeneratedHashesMatch)
    {{
    {script_generated_hash_tests}
    {unmangled_script_generated_hash_tests}
    }}
    
    }}  // namespace sh
    """
    
    # The header file has a "get" function for each variable. They are used in traversers.
    # It also declares id values of built-ins with human readable names, so they can be used to identify built-ins.
    template_builtin_header = """// GENERATED FILE - DO NOT EDIT.
    // Generated by {script_name} using data from {variable_data_source_name} and
    // {function_data_source_name}.
    //
    // Copyright {copyright_year} 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.
    //
    // BuiltIn_{header_label}autogen.h:
    //   Compile-time initialized built-ins.
    
    #ifndef COMPILER_TRANSLATOR_TREEUTIL_BUILTIN_AUTOGEN_H_
    #define COMPILER_TRANSLATOR_TREEUTIL_BUILTIN_AUTOGEN_H_
    
    #include "compiler/translator/SymbolUniqueId.h"
    
    namespace sh
    {{
    
    class TVariable;
    
    class BuiltInId
    {{
    public:
    
    {builtin_id_declarations}
    
    }};  // class BuiltInId
    
    namespace BuiltInVariable
    {{
    
    {get_variable_declarations}
    
    }}  // namespace BuiltInVariable
    
    }}  // namespace sh
    
    #endif  // COMPILER_TRANSLATOR_TREEUTIL_BUILTIN_AUTOGEN_H_
    """
    
    template_symboltable_header = """// GENERATED FILE - DO NOT EDIT.
    // Generated by {script_name} using data from {variable_data_source_name} and
    // {function_data_source_name}.
    //
    // Copyright {copyright_year} 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.
    //
    // SymbolTable_autogen.h:
    //   Autogenerated member variables of TSymbolTable.
    
    #ifndef COMPILER_TRANSLATOR_SYMBOLTABLE_AUTOGEN_H_
    #define COMPILER_TRANSLATOR_SYMBOLTABLE_AUTOGEN_H_
    
    namespace sh
    {{
    
    class TSymbolTableBase
    {{
      public:
        TSymbolTableBase() = default;
    {declare_member_variables}
    }};
    
    }}  // namespace sh
    
    #endif  // COMPILER_TRANSLATOR_SYMBOLTABLE_AUTOGEN_H_
    """
    
    # By having the variables defined in a cpp file we ensure that there's just one instance of each of the declared variables.
    template_symboltable_cpp = """// GENERATED FILE - DO NOT EDIT.
    // Generated by {script_name} using data from {variable_data_source_name} and
    // {function_data_source_name}.
    //
    // Copyright {copyright_year} 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.
    //
    // SymbolTable_{source_label}autogen.cpp:
    //   Compile-time initialized built-ins.
    
    #include "compiler/translator/SymbolTable.h"
    
    #include "angle_gl.h"
    #include "compiler/translator/tree_util/BuiltIn.h"
    #include "compiler/translator/ImmutableString.h"
    #include "compiler/translator/StaticType.h"
    #include "compiler/translator/Symbol.h"
    #include "compiler/translator/SymbolTable.h"
    
    namespace sh
    {{
    
    // Since some of the BuiltInId declarations are used outside of constexpr expressions, we need to
    // have these definitions without an initializer. C++17 should eventually remove the need for this.
    {builtin_id_definitions}
    
    const int TSymbolTable::kLastBuiltInId = {last_builtin_id};
    
    namespace BuiltInName
    {{
    
    constexpr const ImmutableString _empty("");
    {name_declarations}
    
    }}  // namespace BuiltInName
    
    // TODO(oetuaho): Would be nice to make this a class instead of a namespace so that we could friend
    // this from TVariable. Now symbol constructors taking an id have to be public even though they're
    // not supposed to be accessible from outside of here. http://anglebug.com/2390
    namespace BuiltInVariable
    {{
    
    {variable_declarations}
    
    {get_variable_definitions}
    
    }}  // namespace BuiltInVariable
    
    namespace BuiltInParameters
    {{
    
    {parameter_declarations}
    
    }}  // namespace BuiltInParameters
    
    namespace UnmangledBuiltIns
    {{
    
    {unmangled_builtin_declarations}
    
    }}  // namespace UnmangledBuiltIns
    
    // TODO(oetuaho): Would be nice to make this a class instead of a namespace so that we could friend
    // this from TFunction. Now symbol constructors taking an id have to be public even though they're
    // not supposed to be accessible from outside of here. http://anglebug.com/2390
    namespace BuiltInFunction
    {{
    
    {function_declarations}
    
    }}  // namespace BuiltInFunction
    
    namespace BuiltInArray
    {{
    
    constexpr SymbolEntry mangled[] = {{ {mangled_array} }};
    
    constexpr UnmangledEntry unmangled[] = {{ {unmangled_array} }};
    
    }}
    
    void TSymbolTable::initializeBuiltInVariables(sh::GLenum shaderType,
                                                  ShShaderSpec spec,
                                                  const ShBuiltInResources &resources)
    {{
        const TSourceLoc zeroSourceLoc = {{0, 0, 0, 0}};
    {init_member_variables}
    }}
    
    namespace {{
    
    bool CheckESSLVersion(int expected, int actual)
    {{
        // Not supported
        if (expected == -1)
            return false;
    
        // Only ESSL100
        if (expected == 100)
            return actual == 100;
    
        return actual >= expected;
    }}
    
    bool CheckGLSLVersion(int expected, int actual)
    {{
        // Not supported
        if (expected == -1)
            return false;
    
        return actual >= expected;
    }}
    
    bool CheckShaderType(Shader expected, GLenum actual)
    {{
        switch(expected)
        {{
            case Shader::ALL:
                return true;
            case Shader::FRAGMENT:
                return actual == GL_FRAGMENT_SHADER;
            case Shader::VERTEX:
                return actual == GL_VERTEX_SHADER;
            case Shader::COMPUTE:
                return actual == GL_COMPUTE_SHADER;
            case Shader::GEOMETRY:
                return actual == GL_GEOMETRY_SHADER;
            case Shader::GEOMETRY_EXT:
                return actual == GL_GEOMETRY_SHADER_EXT;
            case Shader::NOT_COMPUTE:
                return actual != GL_COMPUTE_SHADER;
            default:
                UNREACHABLE();
                return false;
        }}
    }}
    
    }}
    
    const TSymbol *TSymbolTable::getSymbol(SymbolEntry entry, const ImmutableString &name, int version) const
    {{
        if (entry.name != name)
        {{
            return nullptr;
        }}
        if ((entry.symbol || entry.var) && CheckShaderType(entry.shaderType, mShaderType))
        {{
            // Non-extension symbol or variable
            if (!IsDesktopGLSpec(mShaderSpec) && CheckESSLVersion(entry.esslVersion, version))
            {{
                return entry.symbol ? entry.symbol : this->*(entry.var);
            }}
            if (IsDesktopGLSpec(mShaderSpec) && CheckGLSLVersion(entry.glslVersion, version))
            {{
                return entry.symbol ? entry.symbol : this->*(entry.var);
            }}
        }}
        if (entry.esslExtension && mResources.*(entry.esslExtension) && !IsDesktopGLSpec(mShaderSpec) &&
            CheckESSLVersion(entry.esslExtVersion, version) &&
            CheckShaderType(entry.esslExtShaderType, mShaderType))
        {{
            return entry.esslExtSymbol ? entry.esslExtSymbol : this->*(entry.esslExtVar);
        }}
        if (entry.glslExtension && mResources.*(entry.glslExtension) && IsDesktopGLSpec(mShaderSpec) &&
            CheckGLSLVersion(entry.glslExtVersion, version) &&
            CheckShaderType(entry.glslExtShaderType, mShaderType))
        {{
            return entry.glslExtSymbol ? entry.glslExtSymbol : this->*(entry.glslExtVar);
        }}
        if (entry.esslExtension2 && mResources.*(entry.esslExtension2) && !IsDesktopGLSpec(mShaderSpec) &&
            CheckESSLVersion(entry.esslExtVersion2, version) &&
            CheckShaderType(entry.esslExtShaderType2, mShaderType))
        {{
            return entry.esslExtSymbol2 ? entry.esslExtSymbol2 : this->*(entry.esslExtVar2);
        }}
        return nullptr;
    }}
    
    const TSymbol *TSymbolTable::findBuiltIn(const ImmutableString &name,
                                             int shaderVersion) const
    {{
        if (name.length() > {max_mangled_name_length})
            return nullptr;
    
        uint32_t nameHash = name.mangledNameHash();
        if (nameHash >= {num_mangled_names})
            return nullptr;
        return getSymbol(BuiltInArray::mangled[nameHash], name, shaderVersion);
    }}
    
    const UnmangledBuiltIn *TSymbolTable::getUnmangled(UnmangledEntry entry, const ImmutableString &name, int version) const
    {{
        if (!CheckShaderType(entry.shaderType, mShaderType))
        {{
            return nullptr;
        }}
        if (entry.name == name)
        {{
            if (!IsDesktopGLSpec(mShaderSpec) && CheckESSLVersion(entry.esslVersion, version))
            {{
                return entry.esslUnmangled;
            }}
            if (IsDesktopGLSpec(mShaderSpec) && CheckGLSLVersion(entry.glslVersion, version))
            {{
                return entry.glslUnmangled;
            }}
        }}
        return nullptr;
    }}
    
    const UnmangledBuiltIn *TSymbolTable::getUnmangledBuiltInForShaderVersion(const ImmutableString &name, int shaderVersion)
    {{
        if (name.length() > {max_unmangled_name_length})
            return nullptr;
    
        uint32_t nameHash = name.unmangledNameHash();
        if (nameHash >= {num_unmangled_names})
            return nullptr;
        return getUnmangled(BuiltInArray::unmangled[nameHash], name, shaderVersion);
    }}
    
    }}  // namespace sh
    """
    
    template_parsecontext_header = """// GENERATED FILE - DO NOT EDIT.
    // Generated by {script_name} using data from {variable_data_source_name} and
    // {function_data_source_name}.
    //
    // Copyright {copyright_year} 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.
    //
    // ParseContext_{header_label}autogen.h:
    //   Helpers for built-in related checks.
    
    #ifndef COMPILER_TRANSLATOR_PARSECONTEXT_AUTOGEN_H_
    #define COMPILER_TRANSLATOR_PARSECONTEXT_AUTOGEN_H_
    
    namespace sh
    {{
    
    namespace BuiltInGroup
    {{
    
    {is_in_group_definitions}
    
    }}  // namespace BuiltInGroup
    
    }}  // namespace sh
    
    #endif  // COMPILER_TRANSLATOR_PARSECONTEXT_AUTOGEN_H_
    
    """
    
    basic_types_enumeration = [
        'Void',
        'Float',
        'Double',
        'Int',
        'UInt',
        'Bool',
        'AtomicCounter',
        'YuvCscStandardEXT',
        'Sampler2D',
        'Sampler3D',
        'SamplerCube',
        'Sampler2DArray',
        'SamplerExternalOES',
        'SamplerExternal2DY2YEXT',
        'Sampler2DRect',
        'Sampler2DMS',
        'Sampler2DMSArray',
        'ISampler2D',
        'ISampler3D',
        'ISamplerCube',
        'ISampler2DArray',
        'ISampler2DMS',
        'ISampler2DMSArray',
        'USampler2D',
        'USampler3D',
        'USamplerCube',
        'USampler2DArray',
        'USampler2DMS',
        'USampler2DMSArray',
        'Sampler2DShadow',
        'SamplerCubeShadow',
        'Sampler2DArrayShadow',
        'Sampler1D',
        'Sampler1DArray',
        'Sampler1DArrayShadow',
        'SamplerBuffer',
        'SamplerCubeArray',
        'SamplerCubeArrayShadow',
        'Sampler1DShadow',
        'Sampler2DRectShadow',
        'ISampler1D',
        'ISampler1DArray',
        'ISampler2DRect',
        'ISamplerBuffer',
        'ISamplerCubeArray',
        'USampler1D',
        'USampler1DArray',
        'USampler2DRect',
        'USamplerBuffer',
        'USamplerCubeArray',
        'Image2D',
        'IImage2D',
        'UImage2D',
        'Image3D',
        'IImage3D',
        'UImage3D',
        'Image2DArray',
        'IImage2DArray',
        'UImage2DArray',
        'ImageCube',
        'IImageCube',
        'UImageCube',
        'Image1D',
        'IImage1D',
        'UImage1D',
        'Image1DArray',
        'IImage1DArray',
        'UImage1DArray',
        'Image2DMS',
        'IImage2DMS',
        'UImage2DMS',
        'Image2DMSArray',
        'IImage2DMSArray',
        'UImage2DMSArray',
        'Image2DRect',
        'IImage2DRect',
        'UImage2DRect',
        'ImageCubeArray',
        'IImageCubeArray',
        'UImageCubeArray',
        'ImageRect',
        'IImageRect',
        'UImageRect',
        'ImageBuffer',
        'IImageBuffer',
        'UImageBuffer',
        'SubpassInput',
        'ISubpassInput',
        'USubpassInput',
        'SubpassInputMS',
        'ISubpassInputMS',
        'USubpassInputMS',
    ]
    
    id_counter = 0
    
    
    def set_working_dir():
        script_dir = os.path.dirname(os.path.abspath(__file__))
        os.chdir(script_dir)
    
    
    def get_basic_mangled_name(basic):
        index = basic_types_enumeration.index(basic)
        if index < 26:
            return '0' + chr(ord('A') + index)
        if index < 52:
            return '0' + chr(ord('a') + index - 26)
        if index < 78:
            return '1' + chr(ord('A') + index - 52)
        return '1' + chr(ord('a') + index - 78)
    
    
    essl_levels = ['ESSL3_1_BUILTINS', 'ESSL3_BUILTINS', 'ESSL1_BUILTINS', 'COMMON_BUILTINS']
    
    glsl_levels = [
        'GLSL4_6_BUILTINS', 'GLSL4_5_BUILTINS', 'GLSL4_4_BUILTINS', 'GLSL4_3_BUILTINS',
        'GLSL4_2_BUILTINS', 'GLSL4_1_BUILTINS', 'GLSL4_BUILTINS', 'GLSL3_3_BUILTINS',
        'GLSL1_5_BUILTINS', 'GLSL1_4_BUILTINS', 'GLSL1_3_BUILTINS', 'GLSL1_2_BUILTINS',
        'COMMON_BUILTINS'
    ]
    
    
    def get_essl_shader_version_for_level(level):
        if level == None:
            return '-1'
        elif level == 'ESSL3_1_BUILTINS':
            return '310'
        elif level == 'ESSL3_BUILTINS':
            return '300'
        elif level == 'ESSL1_BUILTINS':
            return '100'
        elif level == 'COMMON_BUILTINS':
            return '0'
        else:
            raise Exception('Unsupported symbol table level')
    
    
    def get_glsl_shader_version_for_level(level):
        if level == None:
            return '-1'
        elif level == 'GLSL1_2_BUILTINS':
            return '120'
        elif level == 'GLSL1_3_BUILTINS':
            return '130'
        elif level == 'GLSL1_4_BUILTINS':
            return '140'
        elif level == 'GLSL1_5_BUILTINS':
            return '150'
        elif level == 'GLSL3_3_BUILTINS':
            return '330'
        elif level == 'GLSL4_BUILTINS':
            return '400'
        elif level == 'GLSL4_1_BUILTINS':
            return '410'
        elif level == 'GLSL4_2_BUILTINS':
            return '420'
        elif level == 'GLSL4_3_BUILTINS':
            return '430'
        elif level == 'GLSL4_4_BUILTINS':
            return '440'
        elif level == 'GLSL4_5_BUILTINS':
            return '450'
        elif level == 'GLSL4_6_BUILTINS':
            return '460'
        elif level == 'COMMON_BUILTINS':
            return '0'
        else:
            raise Exception('Unsupported symbol table level')
    
    
    class GroupedList:
        """"Class for storing a list of objects grouped by symbol table level and condition."""
    
        def __init__(self, hashfn, num_names):
            self.objs = OrderedDict()
            self.max_name_length = 0
            self.hashfn = hashfn
            self.num_names = num_names
    
        def add_obj(self,
                    essl_level,
                    glsl_level,
                    shader_type,
                    name,
                    obj,
                    essl_extension,
                    glsl_extension,
                    script_generated_hash_tests,
                    var_obj=False):
            if essl_level and essl_level not in essl_levels:
                raise Exception('Unexpected essl level: ' + str(essl_level))
            if glsl_level and glsl_level not in glsl_levels:
                raise Exception('Unexpected glsl level: ' + str(glsl_level))
            if len(name) > self.max_name_length:
                self.max_name_length = len(name)
    
            name_hash = mangledNameHash(name, self.hashfn, script_generated_hash_tests, False)
            if name_hash not in self.objs:
                self.objs[name_hash] = OrderedDict()
    
            self.objs[name_hash]['name'] = name
    
            if essl_extension == 'UNDEFINED' and glsl_extension == 'UNDEFINED':
                if essl_level:
                    self.objs[name_hash]['essl_level'] = essl_level
                if glsl_level:
                    self.objs[name_hash]['glsl_level'] = glsl_level
                if var_obj:
                    self.objs[name_hash]['var'] = obj
                else:
                    self.objs[name_hash]['obj'] = obj
                self.objs[name_hash]['shader_type'] = shader_type
    
            if essl_extension != 'UNDEFINED':
                if (var_obj and 'essl_ext_var' in self.objs[name_hash] and
                        self.objs[name_hash]['essl_ext_var'] != obj) or (
                            not var_obj and 'essl_ext_obj' in self.objs[name_hash] and
                            self.objs[name_hash]['essl_ext_obj'] != obj):
                    # Adding a variable that is part of two ESSL extensions
                    if essl_extension != 'UNDEFINED':
                        self.objs[name_hash]['essl_extension2'] = essl_extension
                        self.objs[name_hash]['essl_ext_level2'] = essl_level
                        if var_obj:
                            self.objs[name_hash]['essl_ext_var2'] = obj
                        else:
                            self.objs[name_hash]['essl_ext_obj2'] = obj
                            self.objs[name_hash]['essl_ext_shader_type2'] = shader_type
                else:
                    self.objs[name_hash]['essl_extension'] = essl_extension
                    self.objs[name_hash]['essl_ext_level'] = essl_level
                    if var_obj:
                        self.objs[name_hash]['essl_ext_var'] = obj
                    else:
                        self.objs[name_hash]['essl_ext_obj'] = obj
                        self.objs[name_hash]['essl_ext_shader_type'] = shader_type
    
            if glsl_extension != 'UNDEFINED':
                self.objs[name_hash]['glsl_extension'] = glsl_extension
                self.objs[name_hash]['glsl_ext_level'] = glsl_level
                if var_obj:
                    self.objs[name_hash]['glsl_ext_var'] = obj
                else:
                    self.objs[name_hash]['glsl_ext_obj'] = obj
                self.objs[name_hash]['glsl_ext_shader_type'] = shader_type
    
        def get_max_name_length(self):
            return self.max_name_length
    
        def get_array(self):
            code = []
            for hash_val in range(0, self.num_names):
                if hash_val in self.objs:
                    obj_data = self.objs[hash_val]
    
                    data = []
                    data.append('ImmutableString("{name}")'.format(name=obj_data['name']))
    
                    symbol = obj_data['obj'] if 'obj' in obj_data else 'nullptr'
                    var = '&TSymbolTableBase::{}'.format(
                        obj_data['var']) if 'var' in obj_data else 'nullptr'
                    essl_level = obj_data['essl_level'] if 'essl_level' in obj_data else None
                    glsl_level = obj_data['glsl_level'] if 'glsl_level' in obj_data else None
                    essl_version = get_essl_shader_version_for_level(essl_level)
                    glsl_version = get_glsl_shader_version_for_level(glsl_level)
                    shader_type = 'Shader::' + obj_data[
                        'shader_type'] if 'shader_type' in obj_data and obj_data[
                            'shader_type'] != 'NONE' else 'Shader::ALL'
    
                    data.append(symbol)
                    data.append(var)
                    data.append(essl_version)
                    data.append(glsl_version)
                    data.append(shader_type)
    
                    essl_ext_symbol = obj_data[
                        'essl_ext_obj'] if 'essl_ext_obj' in obj_data else 'nullptr'
                    essl_ext_var = '&TSymbolTableBase::{}'.format(
                        obj_data['essl_ext_var']) if 'essl_ext_var' in obj_data else 'nullptr'
                    essl_ext_version = get_essl_shader_version_for_level(
                        obj_data['essl_ext_level']) if 'essl_extension' in obj_data else '-1'
                    essl_ext_shader_type = 'Shader::' + obj_data[
                        'essl_ext_shader_type'] if 'essl_ext_shader_type' in obj_data and obj_data[
                            'essl_ext_shader_type'] != 'NONE' else 'Shader::ALL'
                    essl_extension = '&ShBuiltInResources::{}'.format(
                        obj_data['essl_extension']) if 'essl_extension' in obj_data else 'nullptr'
    
                    data.append(essl_ext_symbol)
                    data.append(essl_ext_var)
                    data.append(essl_ext_version)
                    data.append(essl_ext_shader_type)
                    data.append(essl_extension)
    
                    glsl_ext_symbol = obj_data[
                        'glsl_ext_obj'] if 'glsl_ext_obj' in obj_data else 'nullptr'
                    glsl_ext_var = '&TSymbolTableBase::{}'.format(
                        obj_data['glsl_ext_var']) if 'glsl_ext_var' in obj_data else 'nullptr'
                    glsl_ext_version = get_glsl_shader_version_for_level(
                        obj_data['glsl_ext_level']) if 'glsl_extension' in obj_data else '-1'
                    glsl_ext_shader_type = 'Shader::' + obj_data[
                        'glsl_ext_shader_type'] if 'glsl_ext_shader_type' in obj_data and obj_data[
                            'glsl_ext_shader_type'] != 'NONE' else 'Shader::ALL'
                    glsl_extension = '&ShBuiltInResources::{}'.format(
                        obj_data['glsl_extension']) if 'glsl_extension' in obj_data else 'nullptr'
    
                    data.append(glsl_ext_symbol)
                    data.append(glsl_ext_var)
                    data.append(glsl_ext_version)
                    data.append(glsl_ext_shader_type)
                    data.append(glsl_extension)
    
                    if 'essl_extension2' in obj_data:
                        essl_ext_symbol2 = obj_data[
                            'essl_ext_obj2'] if 'essl_ext_obj2' in obj_data else 'nullptr'
                        essl_ext_var2 = '&TSymbolTableBase::{}'.format(
                            obj_data['essl_ext_var2']) if 'essl_ext_var2' in obj_data else 'nullptr'
                        essl_ext_version2 = get_essl_shader_version_for_level(
                            obj_data['essl_ext_level2'])
                        essl_ext_shader_type2 = 'Shader::' + obj_data[
                            'essl_ext_shader_type2'] if 'essl_ext_shader_type2' in obj_data and obj_data[
                                'essl_ext_shader_type2'] != 'NONE' else 'Shader::ALL'
                        essl_extension2 = '&ShBuiltInResources::{}'.format(obj_data['essl_extension2'])
                        data.append(essl_ext_symbol2)
                        data.append(essl_ext_var2)
                        data.append(essl_ext_version2)
                        data.append(essl_ext_shader_type2)
                        data.append(essl_extension2)
    
                    code.append('SymbolEntry(' + ', '.join(data) + ')')
                else:
                    code.append("""SymbolEntry(ImmutableString(""),
                           nullptr, nullptr, -1, -1, Shader::ALL,
                           nullptr, nullptr, -1, Shader::ALL, nullptr,
                           nullptr, nullptr, -1, Shader::ALL, nullptr)""")
            return ', '.join(code)
    
    
    class UnmangledGroupedList:
        """"Class for storing a list of unmangled objects grouped by symbol table level and condition."""
    
        def __init__(self, hashfn, num_names):
            self.objs = OrderedDict()
            self.max_name_length = 0
            self.hashfn = hashfn
            self.num_names = num_names
    
        def add_obj(self, essl_level, glsl_level, shader_type, name, essl_obj, glsl_obj,
                    essl_extension, glsl_extension, essl_unmangled_builtin_declaration,
                    glsl_unmangled_builtin_declaration, unmangled_script_generated_hash_tests):
            if essl_level and essl_level not in essl_levels:
                raise Exception('Unexpected essl level: ' + str(essl_level))
            if glsl_level and glsl_level not in glsl_levels:
                raise Exception('Unexpected glsl level: ' + str(glsl_level))
            if len(name) > self.max_name_length:
                self.max_name_length = len(name)
    
            name_hash = mangledNameHash(name, self.hashfn, unmangled_script_generated_hash_tests, True)
            self.objs[name_hash] = OrderedDict()
            self.objs[name_hash]['name'] = name
            self.objs[name_hash]['essl_level'] = essl_level
            self.objs[name_hash]['glsl_level'] = glsl_level
            self.objs[name_hash]['shader_type'] = shader_type
            self.objs[name_hash]['essl_obj'] = essl_obj
            self.objs[name_hash]['glsl_obj'] = glsl_obj
            self.objs[name_hash]['essl_extension'] = essl_extension
            self.objs[name_hash]['glsl_extension'] = glsl_extension
            self.objs[name_hash]['essl_declaration'] = essl_unmangled_builtin_declaration
            self.objs[name_hash]['glsl_declaration'] = glsl_unmangled_builtin_declaration
    
        def has_key(self, essl_level, glsl_level, shader_type, name):
            name_hash = mangledNameHash(name, self.hashfn, None, True, False)
            if name_hash not in self.objs:
                return False
            entry = self.objs[name_hash]
            if entry['essl_level'] != essl_level:
                return False
            if entry['glsl_level'] != glsl_level:
                return False
            if entry['shader_type'] != shader_type:
                return False
            return True
    
        def get(self, essl_level, glsl_level, shader_type, name):
            if self.has_key(essl_level, glsl_level, shader_type, name):
                name_hash = mangledNameHash(name, self.hashfn, None, True, False)
                return self.objs[name_hash]
            return None
    
        def get_max_name_length(self):
            return self.max_name_length
    
        def get_array(self, unmangled_builtin_declarations):
            code = []
            for hash_val in range(0, self.num_names):
                obj_data = self.objs[hash_val]
                essl_level = obj_data['essl_level']
                glsl_level = obj_data['glsl_level']
                shader_type = 'Shader::' + obj_data['shader_type'] if obj_data[
                    'shader_type'] != 'NONE' else 'Shader::ALL'
                data = []
                data.append('ImmutableString("{name}")'.format(name=obj_data['name']))
                data.append(obj_data['essl_obj'])
                data.append(obj_data['glsl_obj'])
                data.append(get_essl_shader_version_for_level(essl_level))
                data.append(get_glsl_shader_version_for_level(glsl_level))
                data.append(shader_type)
    
                code.append('UnmangledEntry(' + ', '.join(data) + ')')
    
                unmangled_builtin_declarations.add(obj_data['essl_declaration'])
                unmangled_builtin_declarations.add(obj_data['glsl_declaration'])
            return ', '.join(code)
    
    
    class TType:
    
        def __init__(self, glsl_header_type):
            if isinstance(glsl_header_type, basestring):
                self.data = self.parse_type(glsl_header_type)
            else:
                self.data = glsl_header_type
            self.normalize()
    
        def normalize(self):
            # Note that this will set primarySize and secondarySize also on genTypes. In that case they
            # are overridden when the specific types are generated.
            if 'primarySize' not in self.data:
                if ('secondarySize' in self.data):
                    raise Exception(
                        'Unexpected secondarySize on type that does not have primarySize set')
                self.data['primarySize'] = 1
            if 'secondarySize' not in self.data:
                self.data['secondarySize'] = 1
            if 'precision' not in self.data:
                self.data['precision'] = 'Undefined'
            if 'qualifier' not in self.data:
                self.data['qualifier'] = 'Global'
    
        def get_statictype_string(self):
            template_type = 'StaticType::Get<Ebt{basic}, Ebp{precision}, Evq{qualifier}, {primarySize}, {secondarySize}>()'
            return template_type.format(**self.data)
    
        def get_dynamic_type_string(self):
            template_type = 'new TType(Ebt{basic}, Ebp{precision}, Evq{qualifier}, {primarySize}, {secondarySize})'
            return template_type.format(**self.data)
    
        def get_mangled_name(self):
            mangled_name = ''
    
            size_key = (self.data['secondarySize'] - 1) * 4 + self.data['primarySize'] - 1
            if size_key < 10:
                mangled_name += chr(ord('0') + size_key)
            else:
                mangled_name += chr(ord('A') + size_key - 10)
            mangled_name += get_basic_mangled_name(self.data['basic'])
            return mangled_name
    
        def get_human_readable_name(self):
            name = self.data['basic']
            name += str(self.data['primarySize'])
            if self.data['secondarySize'] > 1:
                name += 'x' + str(self.data['secondarySize'])
            return name
    
        def is_vector(self):
            return self.data['primarySize'] > 1 and self.data['secondarySize'] == 1
    
        def is_matrix(self):
            return self.data['secondarySize'] > 1
    
        def get_object_size(self):
            return self.data['primarySize'] * self.data['secondarySize']
    
        def specific_sampler_or_image_or_subpass_type(self, basic_type_prefix):
            if 'genType' in self.data and self.data['genType'] == 'sampler_or_image_or_subpass':
                type = {}
                if 'basic' not in self.data:
                    type['basic'] = {'': 'Float', 'I': 'Int', 'U': 'UInt'}[basic_type_prefix]
                    type['primarySize'] = self.data['primarySize']
                else:
                    type['basic'] = basic_type_prefix + self.data['basic']
                    type['primarySize'] = 1
                type['precision'] = 'Undefined'
                return TType(type)
            return self
    
        def specific_type(self, vec_size):
            type = {}
            if 'genType' in self.data:
                type['basic'] = self.data['basic']
                type['precision'] = self.data['precision']
                type['qualifier'] = self.data['qualifier']
                type['primarySize'] = vec_size
                type['secondarySize'] = 1
                return TType(type)
            return self
    
        def parse_type(self, glsl_header_type):
            # TODO(http://anglebug.com/3833): handle readonly, writeonly qualifiers
            if glsl_header_type.startswith('readonly writeonly '):
                type_obj = self.parse_type(glsl_header_type[19:])
                type_obj['qualifier'] = 'Readonly Writeonly'
                return type_obj
            if glsl_header_type.startswith('readonly '):
                type_obj = self.parse_type(glsl_header_type[9:])
                type_obj['qualifier'] = 'Readonly'
                return type_obj
            if glsl_header_type.startswith('writeonly '):
                type_obj = self.parse_type(glsl_header_type[10:])
                type_obj['qualifier'] = 'Writeonly'
                return type_obj
            if glsl_header_type.startswith('out '):
                type_obj = self.parse_type(glsl_header_type[4:])
                type_obj['qualifier'] = 'Out'
                return type_obj
            if glsl_header_type.startswith('inout '):
                type_obj = self.parse_type(glsl_header_type[6:])
                type_obj['qualifier'] = 'InOut'
                return type_obj
    
            basic_type_map = {
                'float': 'Float',
                'int': 'Int',
                'uint': 'UInt',
                'double': 'Double',
                'bool': 'Bool',
                'void': 'Void',
                'atomic_uint': 'AtomicCounter',
                'yuvCscStandardEXT': 'YuvCscStandardEXT'
            }
    
            if glsl_header_type in basic_type_map:
                return {'basic': basic_type_map[glsl_header_type]}
    
            type_obj = {}
    
            basic_type_prefix_map = {
                '': 'Float',
                'i': 'Int',
                'u': 'UInt',
                'd': 'Double',
                'b': 'Bool',
                'v': 'Void'
            }
    
            # TODO(http://anglebug.com/3833): textureGatherOffsets in GLSL has an ivec2[4] parameter
            # Need to parse, currently treating it as mat4x2
            vec_re = re.compile(r'^([iudb]?)vec([234]?)((\[[234]\])?)$')
            vec_match = vec_re.match(glsl_header_type)
            if vec_match:
                type_obj['basic'] = basic_type_prefix_map[vec_match.group(1)]
                if vec_match.group(2) == '':
                    # Type like "ivec" that represents either ivec2, ivec3 or ivec4
                    type_obj['genType'] = 'vec'
                else:
                    # vec with specific size
                    if vec_match.group(3) != '':
                        # vec array
                        type_obj['primarySize'] = int(vec_match.group(3)[1])
                        type_obj['secondarySize'] = int(vec_match.group(2))
                    else:
                        type_obj['primarySize'] = int(vec_match.group(2))
                return type_obj
    
            mat_re = re.compile(r'^mat([234])(x([234]))?$')
            mat_match = mat_re.match(glsl_header_type)
            if mat_match:
                type_obj['basic'] = 'Float'
                if len(glsl_header_type) == 4:
                    mat_size = int(mat_match.group(1))
                    type_obj['primarySize'] = mat_size
                    type_obj['secondarySize'] = mat_size
                else:
                    type_obj['primarySize'] = int(mat_match.group(1))
                    type_obj['secondarySize'] = int(mat_match.group(3))
                return type_obj
    
            gen_re = re.compile(r'^gen([IUDB]?)Type$')
            gen_match = gen_re.match(glsl_header_type)
            if gen_match:
                type_obj['basic'] = basic_type_prefix_map[gen_match.group(1).lower()]
                type_obj['genType'] = 'yes'
                return type_obj
    
            if glsl_header_type.startswith('sampler'):
                type_obj['basic'] = glsl_header_type[0].upper() + glsl_header_type[1:]
                return type_obj
    
            if glsl_header_type.startswith('gsampler') or glsl_header_type.startswith(
                    'gimage') or glsl_header_type.startswith('gsubpassInput'):
                type_obj['basic'] = glsl_header_type[1].upper() + glsl_header_type[2:]
                type_obj['genType'] = 'sampler_or_image_or_subpass'
                return type_obj
    
            if glsl_header_type == 'gvec4':
                return {'primarySize': 4, 'genType': 'sampler_or_image_or_subpass'}
            if glsl_header_type == 'gvec3':
                return {'primarySize': 3, 'genType': 'sampler_or_image_or_subpass'}
    
            if glsl_header_type == 'IMAGE_PARAMS':
                return {'genType': 'image_params'}
    
            raise Exception('Unrecognized type: ' + str(glsl_header_type))
    
    
    class HashFunction:
    
        def __init__(self, f1, f2, G):
            self.f1 = f1
            self.f2 = f2
            self.G = G
    
        def hash(self, key):
            return (self.G[self.f1(key)] + self.G[self.f2(key)]) % len(self.G)
    
    
    def get_parsed_functions(functions_txt_filename, essl_only):
    
        def parse_function_parameters(parameters):
            if parameters == '':
                return []
            parametersOut = []
            parameters = parameters.split(', ')
            for parameter in parameters:
                parametersOut.append(TType(parameter.strip()))
            return parametersOut
    
        lines = []
        with open(functions_txt_filename) as f:
            lines = f.readlines()
        lines = [
            line.strip() for line in lines if line.strip() != '' and not line.strip().startswith('//')
        ]
    
        fun_re = re.compile(r'^(\w+) (\w+)\((.*)\);$')
    
        parsed_functions = OrderedDict()
        group_stack = []
        default_metadata = {}
    
        for line in lines:
            fun_match = fun_re.match(line)
            if line.startswith('GROUP BEGIN '):
                group_rest = line[12:].strip()
                group_parts = group_rest.split(' ', 1)
                current_group = {'functions': [], 'name': group_parts[0], 'subgroups': {}}
                if len(group_parts) > 1:
                    group_metadata = json.loads(group_parts[1])
                    current_group.update(group_metadata)
                group_stack.append(current_group)
            elif line.startswith('GROUP END '):
                group_end_name = line[10:].strip()
                current_group = group_stack[-1]
                if current_group['name'] != group_end_name:
                    raise Exception('GROUP END: Unexpected function group name "' + group_end_name +
                                    '" was expecting "' + current_group['name'] + '"')
                group_stack.pop()
                is_top_level_group = (len(group_stack) == 0)
                if is_top_level_group:
                    parsed_functions[current_group['name']] = current_group
                    default_metadata = {}
                else:
                    super_group = group_stack[-1]
                    super_group['subgroups'][current_group['name']] = current_group
            elif line.startswith('DEFAULT METADATA'):
                line_rest = line[16:].strip()
                default_metadata = json.loads(line_rest)
            elif fun_match:
                return_type = fun_match.group(1)
                name = fun_match.group(2)
                parameters = fun_match.group(3)
                function_props = {
                    'name': name,
                    'returnType': TType(return_type),
                    'parameters': parse_function_parameters(parameters)
                }
                function_props.update(default_metadata)
                if essl_only:
                    # Skip GLSL-only functions
                    if 'essl_level' in function_props:
                        group_stack[-1]['functions'].append(function_props)
                else:
                    group_stack[-1]['functions'].append(function_props)
            else:
                raise Exception('Unexpected function input line: ' + line)
    
        return parsed_functions
    
    
    def mangledNameHash(str, hashfn, script_generated_hash_tests, unmangled, save_test=True):
        hash = hashfn.hash(str)
        if save_test:
            sanity_check = ''
            if unmangled:
                sanity_check = '    ASSERT_EQ(0x{hash}u, ImmutableString("{str}").unmangledNameHash());'.format(
                    hash=('%08x' % hash), str=str)
            else:
                sanity_check = '    ASSERT_EQ(0x{hash}u, ImmutableString("{str}").mangledNameHash());'.format(
                    hash=('%08x' % hash), str=str)
            script_generated_hash_tests.update({sanity_check: None})
        return hash
    
    
    def get_function_names(group, mangled_names, unmangled_names):
        if 'functions' in group:
            for function_props in group['functions']:
                function_name = function_props['name']
                unmangled_names.append(function_name)
                function_variants = gen_function_variants(function_props)
                for function_props in function_variants:
                    parameters = get_parameters(function_props)
                    mangled_names.append(get_function_mangled_name(function_name, parameters))
        if 'subgroups' in group:
            for subgroup_name, subgroup in group['subgroups'].iteritems():
                get_function_names(subgroup, mangled_names, unmangled_names)
    
    
    def get_variable_names(group, mangled_names):
        if 'variables' in group:
            for variable_name, props in group['variables'].iteritems():
                mangled_names.append(variable_name)
        if 'subgroups' in group:
            for subgroup_name, subgroup in group['subgroups'].iteritems():
                get_variable_names(subgroup, mangled_names)
    
    
    def get_suffix(props):
        if 'suffix' in props:
            return props['suffix']
        return ''
    
    
    def get_essl_extension(props):
        if 'essl_extension' in props:
            return props['essl_extension']
        return 'UNDEFINED'
    
    
    def get_glsl_extension(props):
        if 'glsl_extension' in props:
            return props['glsl_extension']
        return 'UNDEFINED'
    
    
    def get_op(name, function_props):
        if 'op' not in function_props:
            raise Exception('function op not defined')
        if function_props['op'] == 'auto':
            return name[0].upper() + name[1:]
        return function_props['op']
    
    
    def get_known_to_not_have_side_effects(function_props):
        if 'op' in function_props and function_props['op'] != 'CallBuiltInFunction':
            if 'hasSideEffects' in function_props:
                return 'false'
            else:
                for param in get_parameters(function_props):
                    if 'qualifier' in param.data and (param.data['qualifier'] == 'Out' or
                                                      param.data['qualifier'] == 'InOut'):
                        return 'false'
                return 'true'
        return 'false'
    
    
    def get_parameters(function_props):
        if 'parameters' in function_props:
            return function_props['parameters']
        return []
    
    
    def get_function_mangled_name(function_name, parameters):
        mangled_name = function_name + '('
        for param in parameters:
            mangled_name += param.get_mangled_name()
        return mangled_name
    
    
    def get_function_human_readable_name(function_name, parameters):
        name = function_name
        for param in parameters:
            name += '_' + param.get_human_readable_name()
        return name
    
    
    def get_unique_identifier_name(function_name, parameters):
        unique_name = function_name + '_'
        for param in parameters:
            unique_name += param.get_mangled_name()
        return unique_name
    
    
    def get_variable_name_to_store_parameter(param):
        unique_name = 'pt'
        if 'qualifier' in param.data:
            if param.data['qualifier'] == 'Out':
                unique_name += '_o_'
            if param.data['qualifier'] == 'InOut':
                unique_name += '_io_'
        unique_name += param.get_mangled_name()
        return unique_name
    
    
    def get_variable_name_to_store_parameters(parameters):
        if len(parameters) == 0:
            return 'empty'
        unique_name = 'p'
        for param in parameters:
            if 'qualifier' in param.data:
                if param.data['qualifier'] == 'Out':
                    unique_name += '_o_'
                if param.data['qualifier'] == 'InOut':
                    unique_name += '_io_'
            unique_name += param.get_mangled_name()
        return unique_name
    
    
    def define_constexpr_variable(template_args, variable_declarations):
        template_variable_declaration = 'constexpr const TVariable kVar_{name_with_suffix}(BuiltInId::{name_with_suffix}, BuiltInName::{name}, SymbolType::BuiltIn, TExtension::{extension}, {type});'
        variable_declarations.append(template_variable_declaration.format(**template_args))
    
    
    def gen_function_variants(function_props):
        function_variants = []
        parameters = get_parameters(function_props)
        function_is_gen_type = False
        gen_type = set()
        image_params_index = 0
        for param in parameters + [function_props['returnType']]:
            if 'genType' in param.data:
                if param.data['genType'] not in [
                        'sampler_or_image_or_subpass', 'vec', 'yes', 'image_params'
                ]:
                    raise Exception(
                        'Unexpected value of genType "' + str(param.data['genType']) +
                        '" should be "sampler_or_image_or_subpass", "vec", "yes", or "image_params"')
                gen_type.add(param.data['genType'])
                if param.data['genType'] == 'image_params':
                    image_params_index = parameters.index(param)
    
        if len(gen_type) == 0:
            function_variants.append(function_props)
            return function_variants
    
        # If we have image_params then we're generating variants for 33 separate functions,
        # each for a different type of image variable
        if 'image_params' in gen_type:
            variants = [['gimage2D', 'ivec2'], ['gimage3D', 'ivec3'], ['gimageCube', 'ivec3'],
                        ['gimageBuffer', 'int'], ['gimage2DArray', 'ivec3'],
                        ['gimageCubeArray', 'ivec3'], ['gimage1D', 'int'], ['gimage1DArray', 'ivec2'],
                        ['gimage2DRect', 'ivec2'], ['gimage2DMS', 'ivec2', 'int'],
                        ['gimage2DMSArray', 'ivec3', 'int']]
            for variant in variants:
                image_variant_parameters = []
                for param in parameters:
                    if parameters.index(param) == image_params_index:
                        for variant_param in variant:
                            image_variant_parameters.append(TType(variant_param))
                    else:
                        image_variant_parameters.append(param)
                types = ['', 'I', 'U']
                for type in types:
                    variant_props = function_props.copy()
                    variant_parameters = []
                    for param in image_variant_parameters:
                        variant_parameters.append(
                            param.specific_sampler_or_image_or_subpass_type(type))
                    variant_props['parameters'] = variant_parameters
                    variant_props['returnType'] = function_props[
                        'returnType'].specific_sampler_or_image_or_subpass_type(type)
                    function_variants.append(variant_props)
            return function_variants
    
        # If we have a gsampler_or_image_or_subpass then we're generating variants for float, int and uint
        # samplers.
        if 'sampler_or_image_or_subpass' in gen_type:
            types = ['', 'I', 'U']
            for type in types:
                variant_props = function_props.copy()
                variant_parameters = []
                for param in parameters:
                    variant_parameters.append(param.specific_sampler_or_image_or_subpass_type(type))
                variant_props['parameters'] = variant_parameters
                variant_props['returnType'] = function_props[
                    'returnType'].specific_sampler_or_image_or_subpass_type(type)
                function_variants.append(variant_props)
            return function_variants
    
        # If we have a normal gentype then we're generating variants for different sizes of vectors.
        sizes = range(1, 5)
        if 'vec' in gen_type:
            sizes = range(2, 5)
        for size in sizes:
            variant_props = function_props.copy()
            variant_parameters = []
            for param in parameters:
                variant_parameters.append(param.specific_type(size))
            variant_props['parameters'] = variant_parameters
            variant_props['returnType'] = function_props['returnType'].specific_type(size)
            function_variants.append(variant_props)
        return function_variants
    
    
    def process_single_function_group(
            shader_type, group_name, group, parameter_declarations, name_declarations,
            unmangled_function_if_statements, unmangled_builtin_declarations,
            defined_function_variants, builtin_id_declarations, builtin_id_definitions,
            defined_parameter_names, variable_declarations, function_declarations,
            script_generated_hash_tests, unmangled_script_generated_hash_tests,
            get_builtin_if_statements):
        global id_counter
    
        if 'functions' not in group:
            return
    
        for function_props in group['functions']:
            function_name = function_props['name']
            essl_level = function_props['essl_level'] if 'essl_level' in function_props else None
            glsl_level = function_props['glsl_level'] if 'glsl_level' in function_props else None
            essl_extension = get_essl_extension(function_props)
            glsl_extension = get_glsl_extension(function_props)
            extension = essl_extension if essl_extension != 'UNDEFINED' else glsl_extension
            template_args = {
                'name': function_name,
                'name_with_suffix': function_name + get_suffix(function_props),
                'essl_level': essl_level,
                'glsl_level': glsl_level,
                'essl_extension': essl_extension,
                'glsl_extension': glsl_extension,
                # This assumes that functions cannot be part of an ESSL and GLSL extension
                # Will need to update after adding GLSL extension functions if this is not the case
                'extension': essl_extension if essl_extension != 'UNDEFINED' else glsl_extension,
                'op': get_op(function_name, function_props),
                'known_to_not_have_side_effects': get_known_to_not_have_side_effects(function_props)
            }
    
            function_variants = gen_function_variants(function_props)
    
            template_name_declaration = 'constexpr const ImmutableString {name_with_suffix}("{name}");'
            name_declaration = template_name_declaration.format(**template_args)
            if not name_declaration in name_declarations:
                name_declarations.add(name_declaration)
    
            essl_obj = '&UnmangledBuiltIns::{essl_extension}'.format(**template_args)
            glsl_obj = '&UnmangledBuiltIns::{glsl_extension}'.format(**template_args)
            unmangled_builtin_no_shader_type = unmangled_function_if_statements.get(
                essl_level, glsl_level, 'NONE', function_name)
            if unmangled_builtin_no_shader_type != None and unmangled_builtin_no_shader_type[
                    'essl_extension'] == 'UNDEFINED' and unmangled_builtin_no_shader_type[
                        'glsl_extension'] == 'UNDEFINED':
                # We already have this unmangled name without a shader type nor extension on the same level.
                # No need to add a duplicate with a type.
                pass
            elif (not unmangled_function_if_statements.has_key(
                    essl_level, glsl_level, shader_type, function_name)) or (
                        unmangled_builtin_no_shader_type and
                        ((essl_extension == 'UNDEFINED' and
                          unmangled_builtin_no_shader_type['essl_extension'] != 'UNDEFINED') or
                         (glsl_extension == 'UNDEFINED' and
                          unmangled_builtin_no_shader_type['glsl_extension'] != 'UNDEFINED'))):
                # We don't have this unmangled builtin recorded yet or we replace an unmangled builtin from an extension with one from core.
                essl_unmangled_builtin_declaration = 'constexpr const UnmangledBuiltIn {essl_extension}(TExtension::{essl_extension});'.format(
                    **template_args)
                glsl_unmangled_builtin_declaration = 'constexpr const UnmangledBuiltIn {glsl_extension}(TExtension::{glsl_extension});'.format(
                    **template_args)
                unmangled_function_if_statements.add_obj(
                    essl_level, glsl_level, shader_type, function_name, essl_obj, glsl_obj,
                    essl_extension, glsl_extension, essl_unmangled_builtin_declaration,
                    glsl_unmangled_builtin_declaration, unmangled_script_generated_hash_tests)
    
            for function_props in function_variants:
                template_args['id'] = id_counter
    
                parameters = get_parameters(function_props)
    
                template_args['unique_name'] = get_unique_identifier_name(
                    template_args['name_with_suffix'], parameters)
                template_args['param_count'] = len(parameters)
                template_args['return_type'] = function_props['returnType'].get_statictype_string()
                template_args['mangled_name'] = get_function_mangled_name(function_name, parameters)
                template_args['human_readable_name'] = get_function_human_readable_name(
                    template_args['name_with_suffix'], parameters)
                template_args['mangled_name_length'] = len(template_args['mangled_name'])
    
                obj = '&BuiltInFunction::function_{unique_name}'.format(**template_args)
                get_builtin_if_statements.add_obj(
                    essl_level, glsl_level, shader_type, template_args['mangled_name'], obj,
                    template_args['essl_extension'], template_args['glsl_extension'],
                    script_generated_hash_tests)
    
                if template_args['unique_name'] in defined_function_variants:
                    continue
                defined_function_variants.add(template_args['unique_name'])
    
                template_builtin_id_declaration = '    static constexpr const TSymbolUniqueId {human_readable_name} = TSymbolUniqueId({id});'
                builtin_id_declarations.append(template_builtin_id_declaration.format(**template_args))
                template_builtin_id_definition = 'constexpr const TSymbolUniqueId BuiltInId::{human_readable_name};'
                builtin_id_definitions.append(template_builtin_id_definition.format(**template_args))
    
                parameters_list = []
                for param in parameters:
                    unique_param_name = get_variable_name_to_store_parameter(param)
                    param_template_args = {
                        'name': '_empty',
                        'name_with_suffix': unique_param_name,
                        'type': param.get_statictype_string(),
                        'extension': 'UNDEFINED'
                    }
                    if unique_param_name not in defined_parameter_names:
                        id_counter += 1
                        param_template_args['id'] = id_counter
                        template_builtin_id_declaration = '    static constexpr const TSymbolUniqueId {name_with_suffix} = TSymbolUniqueId({id});'
                        builtin_id_declarations.append(
                            template_builtin_id_declaration.format(**param_template_args))
                        define_constexpr_variable(param_template_args, variable_declarations)
                        defined_parameter_names.add(unique_param_name)
                    parameters_list.append(
                        '&BuiltInVariable::kVar_{name_with_suffix}'.format(**param_template_args))
    
                template_args['parameters_var_name'] = get_variable_name_to_store_parameters(
                    parameters)
                if len(parameters) > 0:
                    template_args['parameters_list'] = ', '.join(parameters_list)
                    template_parameter_list_declaration = 'constexpr const TVariable *{parameters_var_name}[{param_count}] = {{ {parameters_list} }};'
                    parameter_declarations[template_args[
                        'parameters_var_name']] = template_parameter_list_declaration.format(
                            **template_args)
                else:
                    template_parameter_list_declaration = 'constexpr const TVariable **{parameters_var_name} = nullptr;'
                    parameter_declarations[template_args[
                        'parameters_var_name']] = template_parameter_list_declaration.format(
                            **template_args)
    
                template_function_declaration = 'constexpr const TFunction function_{unique_name}(BuiltInId::{human_readable_name}, BuiltInName::{name_with_suffix}, TExtension::{extension}, BuiltInParameters::{parameters_var_name}, {param_count}, {return_type}, EOp{op}, {known_to_not_have_side_effects});'
                function_declarations.append(template_function_declaration.format(**template_args))
    
                id_counter += 1
    
    
    def process_function_group(group_name, group, parameter_declarations, name_declarations,
                               unmangled_function_if_statements, unmangled_builtin_declarations,
                               defined_function_variants, builtin_id_declarations,
                               builtin_id_definitions, defined_parameter_names, variable_declarations,
                               function_declarations, script_generated_hash_tests,
                               unmangled_script_generated_hash_tests, get_builtin_if_statements,
                               is_in_group_definitions):
        global id_counter
        first_id = id_counter
    
        shader_type = 'NONE'
        if 'shader_type' in group:
            shader_type = group['shader_type']
    
        process_single_function_group(shader_type, group_name, group, parameter_declarations,
                                      name_declarations, unmangled_function_if_statements,
                                      unmangled_builtin_declarations, defined_function_variants,
                                      builtin_id_declarations, builtin_id_definitions,
                                      defined_parameter_names, variable_declarations,
                                      function_declarations, script_generated_hash_tests,
                                      unmangled_script_generated_hash_tests, get_builtin_if_statements)
    
        if 'subgroups' in group:
            for subgroup_name, subgroup in group['subgroups'].iteritems():
                process_function_group(
                    group_name + subgroup_name, subgroup, parameter_declarations, name_declarations,
                    unmangled_function_if_statements, unmangled_builtin_declarations,
                    defined_function_variants, builtin_id_declarations, builtin_id_definitions,
                    defined_parameter_names, variable_declarations, function_declarations,
                    script_generated_hash_tests, unmangled_script_generated_hash_tests,
                    get_builtin_if_statements, is_in_group_definitions)
    
        if 'queryFunction' in group:
            template_args = {'first_id': first_id, 'last_id': id_counter - 1, 'group_name': group_name}
            template_is_in_group_definition = """bool is{group_name}(const TFunction *func)
    {{
        int id = func->uniqueId().get();
        return id >= {first_id} && id <= {last_id};
    }}"""
            is_in_group_definitions.append(template_is_in_group_definition.format(**template_args))
    
    
    def prune_parameters_arrays(parameter_declarations, function_declarations):
        # We can share parameters arrays between functions in case one array is a subarray of another.
        parameter_variable_name_replacements = {}
        used_param_variable_names = set()
        for param_variable_name, param_declaration in sorted(
                parameter_declarations.iteritems(), key=lambda item: -len(item[0])):
            replaced = False
            for used in used_param_variable_names:
                if used.startswith(param_variable_name):
                    parameter_variable_name_replacements[param_variable_name] = used
                    replaced = True
                    break
            if not replaced:
                used_param_variable_names.add(param_variable_name)
    
        for i in xrange(len(function_declarations)):
            for replaced, replacement in parameter_variable_name_replacements.iteritems():
                function_declarations[i] = function_declarations[i].replace(
                    'BuiltInParameters::' + replaced + ',', 'BuiltInParameters::' + replacement + ',')
    
        return [
            value for key, value in parameter_declarations.iteritems()
            if key in used_param_variable_names
        ]
    
    
    def process_single_variable_group(shader_type, group_name, group, builtin_id_declarations,
                                      builtin_id_definitions, name_declarations, init_member_variables,
                                      get_variable_declarations, get_builtin_if_statements,
                                      declare_member_variables, variable_declarations,
                                      get_variable_definitions, script_generated_hash_tests):
        global id_counter
        if 'variables' not in group:
            return
        for variable_name, props in group['variables'].iteritems():
            essl_level = props['essl_level'] if 'essl_level' in props else None
            glsl_level = props['glsl_level'] if 'glsl_level' in props else None
            template_args = {
                'id':
                    id_counter,
                'name':
                    variable_name,
                'name_with_suffix':
                    variable_name + get_suffix(props),
                'essl_level':
                    essl_level,
                'glsl_level':
                    glsl_level,
                'essl_extension':
                    get_essl_extension(props),
                'glsl_extension':
                    get_glsl_extension(props),
                # This assumes that variables cannot be part of an ESSL and GLSL extension
                # Will need to update after adding GLSL extension variables if this is not the case
                'extension':
                    get_essl_extension(props)
                    if get_essl_extension(props) != 'UNDEFINED' else get_glsl_extension(props),
                'class':
                    'TVariable'
            }
    
            template_builtin_id_declaration = '    static constexpr const TSymbolUniqueId {name_with_suffix} = TSymbolUniqueId({id});'
            builtin_id_declarations.append(template_builtin_id_declaration.format(**template_args))
            template_builtin_id_definition = 'constexpr const TSymbolUniqueId BuiltInId::{name_with_suffix};'
            builtin_id_definitions.append(template_builtin_id_definition.format(**template_args))
    
            template_name_declaration = 'constexpr const ImmutableString {name}("{name}");'
            name_declarations.add(template_name_declaration.format(**template_args))
    
            is_member = True
            template_init_variable = ''
    
            if 'type' in props:
                if props['type']['basic'] != 'Bool' and 'precision' not in props['type']:
                    raise Exception('Missing precision for variable ' + variable_name)
                template_args['type'] = TType(props['type']).get_statictype_string()
    
            if 'fields' in props:
                # Handle struct and interface block definitions.
                template_args['class'] = props['class']
                template_args['fields'] = 'fields_{name_with_suffix}'.format(**template_args)
                init_member_variables.append(
                    '    TFieldList *{fields} = new TFieldList();'.format(**template_args))
                for field_name, field_type in props['fields'].iteritems():
                    template_args['field_name'] = field_name
                    template_args['field_type'] = TType(field_type).get_dynamic_type_string()
                    template_name_declaration = 'constexpr const ImmutableString {field_name}("{field_name}");'
                    name_declarations.add(template_name_declaration.format(**template_args))
                    template_add_field = '    {fields}->push_back(new TField({field_type}, BuiltInName::{field_name}, zeroSourceLoc, SymbolType::BuiltIn));'
                    init_member_variables.append(template_add_field.format(**template_args))
                template_init_temp_variable = '    {class} *{name_with_suffix} = new {class}(BuiltInId::{name_with_suffix}, BuiltInName::{name}, TExtension::{extension}, {fields});'
                init_member_variables.append(template_init_temp_variable.format(**template_args))
                if 'private' in props and props['private']:
                    is_member = False
                else:
                    template_init_variable = '    mVar_{name_with_suffix} = {name_with_suffix};'
    
            elif 'initDynamicType' in props:
                # Handle variables whose type can't be expressed as TStaticType
                # (type is a struct or has variable array size for example).
                template_args['type_name'] = 'type_{name_with_suffix}'.format(**template_args)
                template_args['type'] = template_args['type_name']
                template_args['initDynamicType'] = props['initDynamicType'].format(**template_args)
                template_init_variable = """    {initDynamicType}
        {type_name}->realize();
        mVar_{name_with_suffix} = new TVariable(BuiltInId::{name_with_suffix}, BuiltInName::{name}, SymbolType::BuiltIn, TExtension::{extension}, {type});"""
    
            elif 'value' in props:
                # Handle variables with constant value, such as gl_MaxDrawBuffers.
                if props['value'] != 'resources':
                    raise Exception('Unrecognized value source in variable properties: ' +
                                    str(props['value']))
                resources_key = variable_name[3:]
                if 'valueKey' in props:
                    resources_key = props['valueKey']
                template_args['value'] = 'resources.' + resources_key
                template_args['object_size'] = TType(props['type']).get_object_size()
                template_init_variable = """    mVar_{name_with_suffix} = new TVariable(BuiltInId::{name_with_suffix}, BuiltInName::{name}, SymbolType::BuiltIn, TExtension::{extension}, {type});
        {{
            TConstantUnion *unionArray = new TConstantUnion[{object_size}];
            unionArray[0].setIConst({value});
            static_cast<TVariable *>(mVar_{name_with_suffix})->shareConstPointer(unionArray);
        }}"""
                if template_args['object_size'] > 1:
                    template_init_variable = """    mVar_{name_with_suffix} = new TVariable(BuiltInId::{name_with_suffix}, BuiltInName::{name}, SymbolType::BuiltIn, TExtension::{extension}, {type});
        {{
            TConstantUnion *unionArray = new TConstantUnion[{object_size}];
            for (size_t index = 0u; index < {object_size}; ++index)
            {{
                unionArray[index].setIConst({value}[index]);
            }}
            static_cast<TVariable *>(mVar_{name_with_suffix})->shareConstPointer(unionArray);
        }}"""
    
            else:
                # Handle variables that can be stored as constexpr TVariable like
                # gl_Position, gl_FragColor etc.
                define_constexpr_variable(template_args, variable_declarations)
                is_member = False
    
                template_get_variable_declaration = 'const TVariable *{name_with_suffix}();'
                get_variable_declarations.append(
                    template_get_variable_declaration.format(**template_args))
    
                template_get_variable_definition = """const TVariable *{name_with_suffix}()
    {{
        return &kVar_{name_with_suffix};
    }}
    """
                get_variable_definitions.append(
                    template_get_variable_definition.format(**template_args))
    
                if essl_level != 'GLSL_BUILTINS':
                    obj = '&BuiltInVariable::kVar_{name_with_suffix}'.format(**template_args)
                    # TODO(http://anglebug.com/3835): Add GLSL level once GLSL built-in vars are added
                    get_builtin_if_statements.add_obj(
                        essl_level, 'COMMON_BUILTINS', shader_type, template_args['name'], obj,
                        template_args['essl_extension'], template_args['glsl_extension'],
                        script_generated_hash_tests)
    
            if is_member:
                init_member_variables.append(template_init_variable.format(**template_args))
    
                template_declare_member_variable = 'TSymbol *mVar_{name_with_suffix} = nullptr;'
                declare_member_variables.append(
                    template_declare_member_variable.format(**template_args))
    
                obj = 'mVar_{name_with_suffix}'.format(**template_args)
    
                # TODO(http://anglebug.com/3835): Add GLSL level once GLSL built-in vars are added
                get_builtin_if_statements.add_obj(
                    essl_level, 'COMMON_BUILTINS', shader_type, template_args['name'], obj,
                    template_args['essl_extension'], template_args['glsl_extension'],
                    script_generated_hash_tests, True)
    
            id_counter += 1
    
    
    def process_variable_group(shader_type, group_name, group, builtin_id_declarations,
                               builtin_id_definitions, name_declarations, init_member_variables,
                               get_variable_declarations, get_builtin_if_statements,
                               declare_member_variables, variable_declarations,
                               get_variable_definitions, script_generated_hash_tests):
        global id_counter
    
        if 'shader_type' in group:
            shader_type = group['shader_type']
    
        process_single_variable_group(shader_type, group_name, group, builtin_id_declarations,
                                      builtin_id_definitions, name_declarations, init_member_variables,
                                      get_variable_declarations, get_builtin_if_statements,
                                      declare_member_variables, variable_declarations,
                                      get_variable_definitions, script_generated_hash_tests)
    
        if 'subgroups' in group:
            for subgroup_name, subgroup in group['subgroups'].iteritems():
                process_variable_group(
                    shader_type, subgroup_name, subgroup, builtin_id_declarations,
                    builtin_id_definitions, name_declarations, init_member_variables,
                    get_variable_declarations, get_builtin_if_statements, declare_member_variables,
                    variable_declarations, get_variable_definitions, script_generated_hash_tests)
    
    
    def generate_files(essl_only, args, functions_txt_filename, variables_json_filename,
                       immutablestring_cpp_filename, immutablestringtest_cpp_filename,
                       builtin_header_filename, symboltable_cpp_filename, parsecontext_header_filename,
                       symboltable_header_filename):
        # Declarations of symbol unique ids
        builtin_id_declarations = []
    
        # Definitions of symbol unique ids needed for those ids used outside of constexpr expressions.
        builtin_id_definitions = []
    
        # Declarations of name string variables
        name_declarations = set()
    
        # Declarations of builtin TVariables
        variable_declarations = []
    
        # Declarations of builtin TFunctions
        function_declarations = []
    
        # Functions for querying the pointer to a specific TVariable.
        get_variable_declarations = []
        get_variable_definitions = []
    
        # Code for defining TVariables stored as members of TSymbolTable.
        declare_member_variables = []
        init_member_variables = []
    
        # Declarations of UnmangledBuiltIn objects
        unmangled_builtin_declarations = set()
    
        # Code for testing that script-generated hashes match with runtime computed hashes.
        script_generated_hash_tests = OrderedDict()
        unmangled_script_generated_hash_tests = OrderedDict()
    
        # Functions for testing whether a builtin belongs in group.
        is_in_group_definitions = []
    
        # Declarations of parameter arrays for builtin TFunctions. Map from C++ variable name to the full
        # declaration.
        parameter_declarations = {}
    
        defined_function_variants = set()
        defined_parameter_names = set()
    
        parsed_functions = get_parsed_functions(functions_txt_filename, essl_only)
    
        if args.dump_intermediate_json:
            with open('builtin_functions_ESSL.json' if essl_only else 'builtin_functions.json',
                      'w') as outfile:
    
                def serialize_obj(obj):
                    if isinstance(obj, TType):
                        return obj.data
                    else:
                        raise "Cannot serialize to JSON: " + str(obj)
    
                json.dump(
                    parsed_functions, outfile, indent=4, separators=(',', ': '), default=serialize_obj)
    
        parsed_variables = None
        with open(variables_json_filename) as f:
            # TODO(http://anglebug.com/3835): skip loading GLSL-only vars when they are added if essl_only
            parsed_variables = json.load(f, object_pairs_hook=OrderedDict)
    
        # This script uses a perfect hash function to avoid dealing with collisions
        mangled_names = []
        unmangled_names = []
        for group_name, group in parsed_functions.iteritems():
            get_function_names(group, mangled_names, unmangled_names)
        for group_name, group in parsed_variables.iteritems():
            get_variable_names(group, mangled_names)
    
        # Hashing mangled names
        mangled_names = list(dict.fromkeys(mangled_names))
        num_mangled_names = len(mangled_names)
        mangled_names_dict = dict(zip(mangled_names, range(0, len(mangled_names))))
        # Generate the perfect hash function
        f1, f2, mangled_G = generate_hash(mangled_names_dict, Hash2)
        mangled_hashfn = HashFunction(f1, f2, mangled_G)
        mangled_S1 = f1.salt
        mangled_S2 = f2.salt
        # Array for querying mangled builtins
        get_builtin_if_statements = GroupedList(mangled_hashfn, num_mangled_names)
    
        # Hashing unmangled names
        unmangled_names = list(dict.fromkeys(unmangled_names))
        num_unmangled_names = len(unmangled_names)
        unmangled_names_dict = dict(zip(unmangled_names, range(0, len(unmangled_names))))
        # Generate the perfect hash function
        f1, f2, unmangled_G = generate_hash(unmangled_names_dict, Hash2)
        unmangled_hashfn = HashFunction(f1, f2, unmangled_G)
        unmangled_S1 = f1.salt
        unmangled_S2 = f2.salt
        # Array for querying unmangled builtins
        unmangled_function_if_statements = UnmangledGroupedList(unmangled_hashfn, num_unmangled_names)
    
        for group_name, group in parsed_functions.iteritems():
            process_function_group(group_name, group, parameter_declarations, name_declarations,
                                   unmangled_function_if_statements, unmangled_builtin_declarations,
                                   defined_function_variants, builtin_id_declarations,
                                   builtin_id_definitions, defined_parameter_names,
                                   variable_declarations, function_declarations,
                                   script_generated_hash_tests, unmangled_script_generated_hash_tests,
                                   get_builtin_if_statements, is_in_group_definitions)
    
        parameter_declarations = prune_parameters_arrays(parameter_declarations, function_declarations)
    
        for group_name, group in parsed_variables.iteritems():
            process_variable_group('NONE', group_name, group, builtin_id_declarations,
                                   builtin_id_definitions, name_declarations, init_member_variables,
                                   get_variable_declarations, get_builtin_if_statements,
                                   declare_member_variables, variable_declarations,
                                   get_variable_definitions, script_generated_hash_tests)
    
        output_strings = {
            'script_name':
                os.path.basename(__file__),
            'copyright_year':
                date.today().year,
            'builtin_id_declarations':
                '\n'.join(builtin_id_declarations),
            'builtin_id_definitions':
                '\n'.join(builtin_id_definitions),
            'last_builtin_id':
                id_counter - 1,
            'name_declarations':
                '\n'.join(sorted(list(name_declarations))),
            'function_data_source_name':
                functions_txt_filename,
            'function_declarations':
                '\n'.join(function_declarations),
            'parameter_declarations':
                '\n'.join(sorted(parameter_declarations)),
            'is_in_group_definitions':
                '\n'.join(is_in_group_definitions),
            'variable_data_source_name':
                variables_json_filename,
            'variable_declarations':
                '\n'.join(sorted(variable_declarations)),
            'get_variable_declarations':
                '\n'.join(sorted(get_variable_declarations)),
            'get_variable_definitions':
                '\n'.join(sorted(get_variable_definitions)),
            'declare_member_variables':
                '\n'.join(declare_member_variables),
            'init_member_variables':
                '\n'.join(init_member_variables),
            'mangled_array':
                get_builtin_if_statements.get_array(),
            'unmangled_array':
                unmangled_function_if_statements.get_array(unmangled_builtin_declarations),
            'max_unmangled_name_length':
                unmangled_function_if_statements.get_max_name_length(),
            'max_mangled_name_length':
                get_builtin_if_statements.get_max_name_length(),
            'num_unmangled_names':
                num_unmangled_names,
            'num_mangled_names':
                num_mangled_names,
            'unmangled_builtin_declarations':
                '\n'.join(sorted(unmangled_builtin_declarations)),
            'script_generated_hash_tests':
                '\n'.join(script_generated_hash_tests.iterkeys()),
            'unmangled_script_generated_hash_tests':
                '\n'.join(unmangled_script_generated_hash_tests.iterkeys()),
            'mangled_S1':
                str(mangled_S1).replace('[', ' ').replace(']', ' '),
            'mangled_S2':
                str(mangled_S2).replace('[', ' ').replace(']', ' '),
            'mangled_G':
                str(mangled_G).replace('[', ' ').replace(']', ' '),
            'mangled_NG':
                len(mangled_G),
            'mangled_NS':
                len(mangled_S1),
            'unmangled_S1':
                str(unmangled_S1).replace('[', ' ').replace(']', ' '),
            'unmangled_S2':
                str(unmangled_S2).replace('[', ' ').replace(']', ' '),
            'unmangled_G':
                str(unmangled_G).replace('[', ' ').replace(']', ' '),
            'unmangled_NG':
                len(unmangled_G),
            'unmangled_NS':
                len(unmangled_S1),
            'header_label':
                'ESSL_' if essl_only else 'complete_',
            'source_label':
                'ESSL_' if essl_only else ''
        }
    
        with open(immutablestring_cpp_filename, 'wt') as outfile_cpp:
            output_cpp = template_immutablestring_cpp.format(**output_strings)
            outfile_cpp.write(output_cpp)
    
        with open(immutablestringtest_cpp_filename, 'wt') as outfile_cpp:
            output_cpp = template_immutablestringtest_cpp.format(**output_strings)
            outfile_cpp.write(output_cpp)
    
        with open(builtin_header_filename, 'wt') as outfile_header:
            output_header = template_builtin_header.format(**output_strings)
            outfile_header.write(output_header)
    
        with open(symboltable_cpp_filename, 'wt') as outfile_cpp:
            output_cpp = template_symboltable_cpp.format(**output_strings)
            outfile_cpp.write(output_cpp)
    
        with open(parsecontext_header_filename, 'wt') as outfile_header:
            output_header = template_parsecontext_header.format(**output_strings)
            outfile_header.write(output_header)
    
        if not essl_only:
            with open(symboltable_header_filename, 'wt') as outfile_h:
                output_h = template_symboltable_header.format(**output_strings)
                outfile_h.write(output_h)
    
    
    def main():
        random.seed(0)
        set_working_dir()
    
        parser = argparse.ArgumentParser()
        parser.add_argument(
            '--dump-intermediate-json',
            help='Dump parsed function data as a JSON file builtin_functions.json',
            action="store_true")
        parser.add_argument('auto_script_command', nargs='?', default='')
        args = parser.parse_args()
    
        test_filename = '../../tests/compiler_tests/ImmutableString_test_autogen.cpp'
        essl_test_filename = '../../tests/compiler_tests/ImmutableString_test_ESSL_autogen.cpp'
        variables_json_filename = 'builtin_variables.json'
        functions_txt_filename = 'builtin_function_declarations.txt'
    
        # auto_script parameters.
        if args.auto_script_command != '':
            inputs = [
                functions_txt_filename,
                variables_json_filename,
            ]
            outputs = [
                'ImmutableString_autogen.cpp',
                'ParseContext_complete_autogen.h',
                'SymbolTable_autogen.cpp',
                'SymbolTable_autogen.h',
                'tree_util/BuiltIn_complete_autogen.h',
                test_filename,
                'ImmutableString_ESSL_autogen.cpp',
                'ParseContext_ESSL_autogen.h',
                'SymbolTable_ESSL_autogen.cpp',
                'tree_util/BuiltIn_ESSL_autogen.h',
                essl_test_filename,
            ]
    
            if args.auto_script_command == 'inputs':
                print ','.join(inputs)
            elif args.auto_script_command == 'outputs':
                print ','.join(outputs)
            else:
                print('Invalid script parameters')
                return 1
            return 0
    
        # Generate files based on GLSL + ESSL symbols
        generate_files(False, args, functions_txt_filename, variables_json_filename,
                       'ImmutableString_autogen.cpp', test_filename,
                       'tree_util/BuiltIn_complete_autogen.h', 'SymbolTable_autogen.cpp',
                       'ParseContext_complete_autogen.h', 'SymbolTable_autogen.h')
    
        # Generate files based on only ESSL symbols
        # Symbol table with GLSL + ESSL symbols is too large for Android
        generate_files(True, args, functions_txt_filename, variables_json_filename,
                       'ImmutableString_ESSL_autogen.cpp', essl_test_filename,
                       'tree_util/BuiltIn_ESSL_autogen.h', 'SymbolTable_ESSL_autogen.cpp',
                       'ParseContext_ESSL_autogen.h', 'SymbolTable_autogen.h')
    
        return 0
    
    
    if __name__ == '__main__':
        sys.exit(main())