1//
2// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7#include "compiler/Initialize.h"
8#include "compiler/ParseHelper.h"
9#include "compiler/ShHandle.h"
10#include "compiler/ValidateLimitations.h"
11
12namespace {
13bool InitializeSymbolTable(
14    const TBuiltInStrings& builtInStrings,
15    ShShaderType type, ShShaderSpec spec, const ShBuiltInResources& resources,
16    TInfoSink& infoSink, TSymbolTable& symbolTable)
17{
18    TIntermediate intermediate(infoSink);
19    TExtensionBehavior extBehavior;
20    TParseContext parseContext(symbolTable, extBehavior, intermediate, type, spec, infoSink);
21
22    GlobalParseContext = &parseContext;
23
24    assert(symbolTable.isEmpty());
25    //
26    // Parse the built-ins.  This should only happen once per
27    // language symbol table.
28    //
29    // Push the symbol table to give it an initial scope.  This
30    // push should not have a corresponding pop, so that built-ins
31    // are preserved, and the test for an empty table fails.
32    //
33    symbolTable.push();
34
35    for (TBuiltInStrings::const_iterator i = builtInStrings.begin(); i != builtInStrings.end(); ++i)
36    {
37        const char* builtInShaders = i->c_str();
38        int builtInLengths = static_cast<int>(i->size());
39        if (builtInLengths <= 0)
40          continue;
41
42        if (PaParseStrings(1, &builtInShaders, &builtInLengths, &parseContext) != 0)
43        {
44            infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins");
45            return false;
46        }
47    }
48
49    IdentifyBuiltIns(type, spec, resources, symbolTable);
50
51    return true;
52}
53
54class TScopedPoolAllocator {
55public:
56    TScopedPoolAllocator(TPoolAllocator* allocator, bool pushPop)
57        : mAllocator(allocator), mPushPopAllocator(pushPop) {
58        if (mPushPopAllocator) mAllocator->push();
59        SetGlobalPoolAllocator(mAllocator);
60    }
61    ~TScopedPoolAllocator() {
62        SetGlobalPoolAllocator(NULL);
63        if (mPushPopAllocator) mAllocator->pop();
64    }
65
66private:
67    TPoolAllocator* mAllocator;
68    bool mPushPopAllocator;
69};
70}  // namespace
71
72TShHandleBase::TShHandleBase() {
73    allocator.push();
74    SetGlobalPoolAllocator(&allocator);
75}
76
77TShHandleBase::~TShHandleBase() {
78    SetGlobalPoolAllocator(NULL);
79    allocator.popAll();
80}
81
82TCompiler::TCompiler(ShShaderType type, ShShaderSpec spec)
83    : shaderType(type),
84      shaderSpec(spec)
85{
86}
87
88TCompiler::~TCompiler()
89{
90}
91
92bool TCompiler::Init(const ShBuiltInResources& resources)
93{
94    TScopedPoolAllocator scopedAlloc(&allocator, false);
95
96    // Generate built-in symbol table.
97    if (!InitBuiltInSymbolTable(resources))
98        return false;
99    InitExtensionBehavior(resources, extensionBehavior);
100
101    return true;
102}
103
104bool TCompiler::compile(const char* const shaderStrings[],
105                        const int numStrings,
106                        int compileOptions)
107{
108    TScopedPoolAllocator scopedAlloc(&allocator, true);
109    clearResults();
110
111    if (numStrings == 0)
112        return true;
113
114    // If compiling for WebGL, validate loop and indexing as well.
115    if (shaderSpec == SH_WEBGL_SPEC)
116        compileOptions |= SH_VALIDATE_LOOP_INDEXING;
117
118    TIntermediate intermediate(infoSink);
119    TParseContext parseContext(symbolTable, extensionBehavior, intermediate,
120                               shaderType, shaderSpec, infoSink);
121    GlobalParseContext = &parseContext;
122
123    // We preserve symbols at the built-in level from compile-to-compile.
124    // Start pushing the user-defined symbols at global level.
125    symbolTable.push();
126    if (!symbolTable.atGlobalLevel())
127        infoSink.info.message(EPrefixInternalError, "Wrong symbol table level");
128
129    // Parse shader.
130    bool success =
131        (PaParseStrings(numStrings, shaderStrings, NULL, &parseContext) == 0) &&
132        (parseContext.treeRoot != NULL);
133    if (success) {
134        TIntermNode* root = parseContext.treeRoot;
135        success = intermediate.postProcess(root);
136
137        if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING))
138            success = validateLimitations(root);
139
140        if (success && (compileOptions & SH_INTERMEDIATE_TREE))
141            intermediate.outputTree(root);
142
143        if (success && (compileOptions & SH_OBJECT_CODE))
144            translate(root);
145
146        if (success && (compileOptions & SH_ATTRIBUTES_UNIFORMS))
147            collectAttribsUniforms(root);
148    }
149
150    // Cleanup memory.
151    intermediate.remove(parseContext.treeRoot);
152    // Ensure symbol table is returned to the built-in level,
153    // throwing away all but the built-ins.
154    while (!symbolTable.atBuiltInLevel())
155        symbolTable.pop();
156
157    return success;
158}
159
160bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources& resources)
161{
162    TBuiltIns builtIns;
163
164    builtIns.initialize(shaderType, shaderSpec, resources);
165    return InitializeSymbolTable(builtIns.getBuiltInStrings(),
166        shaderType, shaderSpec, resources, infoSink, symbolTable);
167}
168
169void TCompiler::clearResults()
170{
171    infoSink.info.erase();
172    infoSink.obj.erase();
173    infoSink.debug.erase();
174
175    attribs.clear();
176    uniforms.clear();
177}
178
179bool TCompiler::validateLimitations(TIntermNode* root) {
180    ValidateLimitations validate(shaderType, infoSink.info);
181    root->traverse(&validate);
182    return validate.numErrors() == 0;
183}
184
185void TCompiler::collectAttribsUniforms(TIntermNode* root)
186{
187    CollectAttribsUniforms collect(attribs, uniforms);
188    root->traverse(&collect);
189}
190