Expose the IntermNode tree generated in the compiler for testing

This refactoring makes it possible for tests to access the IntermNode
tree produced by compilation by calling compileTree(). Removing
ParseContext usage from OutputHLSL has the additional benefit of better
separation between parsing and output.

BUG=angle:916

Change-Id: Ib40954832316328772a5c1dcbbe6b46b238e4e65
Reviewed-on: https://chromium-review.googlesource.com/249723
Reviewed-by: Olli Etuaho <oetuaho@nvidia.com>
Tested-by: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index 9e64bcb..b8eebc9 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -125,7 +125,8 @@
       maxCallStackDepth(0),
       fragmentPrecisionHigh(false),
       clampingStrategy(SH_CLAMP_WITH_CLAMP_INTRINSIC),
-      builtInFunctionEmulator(type)
+      builtInFunctionEmulator(type),
+      mSourcePath(NULL)
 {
 }
 
@@ -158,15 +159,19 @@
     return true;
 }
 
-bool TCompiler::compile(const char* const shaderStrings[],
-                        size_t numStrings,
-                        int compileOptions)
+TIntermNode *TCompiler::compileTreeForTesting(const char* const shaderStrings[],
+    size_t numStrings, int compileOptions)
 {
-    TScopedPoolAllocator scopedAlloc(&allocator);
+    return compileTreeImpl(shaderStrings, numStrings, compileOptions);
+}
+
+TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[],
+    size_t numStrings, int compileOptions)
+{
     clearResults();
 
-    if (numStrings == 0)
-        return true;
+    ASSERT(numStrings > 0);
+    ASSERT(GetGlobalPoolAllocator());
 
     // Reset the extension behavior for each compilation unit.
     ResetExtensionBehavior(extensionBehavior);
@@ -176,11 +181,10 @@
         compileOptions |= SH_VALIDATE_LOOP_INDEXING;
 
     // First string is path of source file if flag is set. The actual source follows.
-    const char* sourcePath = NULL;
     size_t firstSource = 0;
     if (compileOptions & SH_SOURCE_PATH)
     {
-        sourcePath = shaderStrings[0];
+        mSourcePath = shaderStrings[0];
         ++firstSource;
     }
 
@@ -188,7 +192,7 @@
     TIntermediate intermediate(infoSink);
     TParseContext parseContext(symbolTable, extensionBehavior, intermediate,
                                shaderType, shaderSpec, compileOptions, true,
-                               sourcePath, infoSink, debugShaderPrecision);
+                               infoSink, debugShaderPrecision);
 
     parseContext.fragmentPrecisionHigh = fragmentPrecisionHigh;
     SetGlobalParseContext(&parseContext);
@@ -210,6 +214,8 @@
         success = false;
     }
 
+    TIntermNode *root = NULL;
+
     if (success)
     {
         mPragma = parseContext.pragma();
@@ -218,7 +224,7 @@
             symbolTable.setGlobalInvariant();
         }
 
-        TIntermNode* root = parseContext.treeRoot;
+        root = parseContext.treeRoot;
         success = intermediate.postProcess(root);
 
         // Disallow expressions deemed too complex.
@@ -305,18 +311,37 @@
             RegenerateStructNames gen(symbolTable, shaderVersion);
             root->traverse(&gen);
         }
-
-        if (success && (compileOptions & SH_INTERMEDIATE_TREE))
-            intermediate.outputTree(root);
-
-        if (success && (compileOptions & SH_OBJECT_CODE))
-            translate(root);
     }
 
-    // Cleanup. The IntermNode tree doesn't need to be deleted here, since the
-    // memory will be freed in a big chunk by the PoolAllocator.
     SetGlobalParseContext(NULL);
-    return success;
+    if (success)
+        return root;
+
+    return NULL;
+}
+
+bool TCompiler::compile(const char* const shaderStrings[],
+    size_t numStrings, int compileOptions)
+{
+    if (numStrings == 0)
+        return true;
+
+    TScopedPoolAllocator scopedAlloc(&allocator);
+    TIntermNode *root = compileTreeImpl(shaderStrings, numStrings, compileOptions);
+
+    if (root)
+    {
+        if (compileOptions & SH_INTERMEDIATE_TREE)
+            TIntermediate::outputTree(root, infoSink.info);
+
+        if (compileOptions & SH_OBJECT_CODE)
+            translate(root, compileOptions);
+
+        // The IntermNode tree doesn't need to be deleted here, since the
+        // memory will be freed in a big chunk by the PoolAllocator.
+        return true;
+    }
+    return false;
 }
 
 bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources)
@@ -424,6 +449,8 @@
     builtInFunctionEmulator.Cleanup();
 
     nameMap.clear();
+
+    mSourcePath = NULL;
 }
 
 bool TCompiler::detectCallDepth(TIntermNode* inputRoot, TInfoSink& inputInfoSink, bool limitCallStackDepth)
@@ -602,6 +629,11 @@
     return extensionBehavior;
 }
 
+const char *TCompiler::getSourcePath() const
+{
+    return mSourcePath;
+}
+
 const ShBuiltInResources& TCompiler::getResources() const
 {
     return compileResources;