Moved function creation code from glslang.y to TParseContext.

Added TParseContext::addFunctionCallOrMethod() to move function
creation functionality out of glslang.y.

Change-Id: Ia23e8c2490ba9d2bb1fcd00a1ef06eab5cf60b80
Reviewed-on: https://swiftshader-review.googlesource.com/3640
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <capn@google.com>
diff --git a/src/OpenGL/compiler/ParseHelper.cpp b/src/OpenGL/compiler/ParseHelper.cpp
index a14fd3a..899bac2 100644
--- a/src/OpenGL/compiler/ParseHelper.cpp
+++ b/src/OpenGL/compiler/ParseHelper.cpp
@@ -3249,6 +3249,160 @@
 	return intermediate.addBranch(op, returnValue, loc);
 }
 
+TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall, TIntermNode *paramNode, TIntermNode *thisNode, const TSourceLoc &loc, bool *fatalError)
+{
+	*fatalError = false;
+	TOperator op = fnCall->getBuiltInOp();
+	TIntermTyped *callNode = nullptr;
+
+	if(thisNode != nullptr)
+	{
+		ConstantUnion *unionArray = new ConstantUnion[1];
+		int arraySize = 0;
+		TIntermTyped *typedThis = thisNode->getAsTyped();
+		if(fnCall->getName() != "length")
+		{
+			error(loc, "invalid method", fnCall->getName().c_str());
+			recover();
+		}
+		else if(paramNode != nullptr)
+		{
+			error(loc, "method takes no parameters", "length");
+			recover();
+		}
+		else if(typedThis == nullptr || !typedThis->isArray())
+		{
+			error(loc, "length can only be called on arrays", "length");
+			recover();
+		}
+		else
+		{
+			arraySize = typedThis->getArraySize();
+			if(typedThis->getAsSymbolNode() == nullptr)
+			{
+				// This code path can be hit with expressions like these:
+				// (a = b).length()
+				// (func()).length()
+				// (int[3](0, 1, 2)).length()
+				// ESSL 3.00 section 5.9 defines expressions so that this is not actually a valid expression.
+				// It allows "An array name with the length method applied" in contrast to GLSL 4.4 spec section 5.9
+				// which allows "An array, vector or matrix expression with the length method applied".
+				error(loc, "length can only be called on array names, not on array expressions", "length");
+				recover();
+			}
+		}
+		unionArray->setIConst(arraySize);
+		callNode = intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConstExpr), loc);
+	}
+	else if(op != EOpNull)
+	{
+		//
+		// Then this should be a constructor.
+		// Don't go through the symbol table for constructors.
+		// Their parameters will be verified algorithmically.
+		//
+		TType type(EbtVoid, EbpUndefined);  // use this to get the type back
+		if(!constructorErrorCheck(loc, paramNode, *fnCall, op, &type))
+		{
+			//
+			// It's a constructor, of type 'type'.
+			//
+			callNode = addConstructor(paramNode, &type, op, fnCall, loc);
+		}
+
+		if(callNode == nullptr)
+		{
+			recover();
+			callNode = intermediate.setAggregateOperator(nullptr, op, loc);
+		}
+		callNode->setType(type);
+	}
+	else
+	{
+		//
+		// Not a constructor.  Find it in the symbol table.
+		//
+		const TFunction *fnCandidate;
+		bool builtIn;
+		fnCandidate = findFunction(loc, fnCall, &builtIn);
+		if(fnCandidate)
+		{
+			//
+			// A declared function.
+			//
+			if(builtIn && !fnCandidate->getExtension().empty() &&
+				extensionErrorCheck(loc, fnCandidate->getExtension()))
+			{
+				recover();
+			}
+			op = fnCandidate->getBuiltInOp();
+			if(builtIn && op != EOpNull)
+			{
+				//
+				// A function call mapped to a built-in operation.
+				//
+				if(fnCandidate->getParamCount() == 1)
+				{
+					//
+					// Treat it like a built-in unary operator.
+					//
+					callNode = createUnaryMath(op, paramNode->getAsTyped(), loc, &fnCandidate->getReturnType());
+					if(callNode == nullptr)
+					{
+						std::stringstream extraInfoStream;
+						extraInfoStream << "built in unary operator function.  Type: "
+							<< static_cast<TIntermTyped*>(paramNode)->getCompleteString();
+						std::string extraInfo = extraInfoStream.str();
+						error(paramNode->getLine(), " wrong operand type", "Internal Error", extraInfo.c_str());
+						*fatalError = true;
+						return nullptr;
+					}
+				}
+				else
+				{
+					TIntermAggregate *aggregate = intermediate.setAggregateOperator(paramNode, op, loc);
+					aggregate->setType(fnCandidate->getReturnType());
+
+					// Some built-in functions have out parameters too.
+					functionCallLValueErrorCheck(fnCandidate, aggregate);
+
+					callNode = aggregate;
+				}
+			}
+			else
+			{
+				// This is a real function call
+
+				TIntermAggregate *aggregate = intermediate.setAggregateOperator(paramNode, EOpFunctionCall, loc);
+				aggregate->setType(fnCandidate->getReturnType());
+
+				// this is how we know whether the given function is a builtIn function or a user defined function
+				// if builtIn == false, it's a userDefined -> could be an overloaded builtIn function also
+				// if builtIn == true, it's definitely a builtIn function with EOpNull
+				if(!builtIn)
+					aggregate->setUserDefined();
+				aggregate->setName(fnCandidate->getMangledName());
+
+				callNode = aggregate;
+
+				functionCallLValueErrorCheck(fnCandidate, aggregate);
+			}
+			callNode->setType(fnCandidate->getReturnType());
+		}
+		else
+		{
+			// error message was put out by findFunction()
+			// Put on a dummy node for error recovery
+			ConstantUnion *unionArray = new ConstantUnion[1];
+			unionArray->setFConst(0.0f);
+			callNode = intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpUndefined, EvqConstExpr), loc);
+			recover();
+		}
+	}
+	delete fnCall;
+	return callNode;
+}
+
 //
 // Parse an array of strings using yyparse.
 //
diff --git a/src/OpenGL/compiler/ParseHelper.h b/src/OpenGL/compiler/ParseHelper.h
index f8902ae..145d3f7 100644
--- a/src/OpenGL/compiler/ParseHelper.h
+++ b/src/OpenGL/compiler/ParseHelper.h
@@ -221,6 +221,8 @@
 	TIntermBranch *addBranch(TOperator op, const TSourceLoc &loc);
 	TIntermBranch *addBranch(TOperator op, TIntermTyped *returnValue, const TSourceLoc &loc);
 
+	TIntermTyped *addFunctionCallOrMethod(TFunction *fnCall, TIntermNode *paramNode, TIntermNode *thisNode, const TSourceLoc &loc, bool *fatalError);
+
 private:
 	bool declareVariable(const TSourceLoc &line, const TString &identifier, const TType &type, TVariable **variable);
 
diff --git a/src/OpenGL/compiler/glslang.y b/src/OpenGL/compiler/glslang.y
index a657f49..009f81f 100644
--- a/src/OpenGL/compiler/glslang.y
+++ b/src/OpenGL/compiler/glslang.y
@@ -293,101 +293,12 @@
 
 function_call
     : function_call_or_method {
-        TFunction* fnCall = $1.function;
-        TOperator op = fnCall->getBuiltInOp();
-
-        if (op != EOpNull)
+        bool fatalError = false;

+        $$ = context->addFunctionCallOrMethod($1.function, $1.intermNode, nullptr, @1, &fatalError);

+        if (fatalError)

         {
-            //
-            // Then this should be a constructor.
-            // Don't go through the symbol table for constructors.
-            // Their parameters will be verified algorithmically.
-            //
-            TType type(EbtVoid, EbpUndefined);  // use this to get the type back
-            if (context->constructorErrorCheck(@1, $1.intermNode, *fnCall, op, &type)) {
-                $$ = 0;
-            } else {
-                //
-                // It's a constructor, of type 'type'.
-                //
-                $$ = context->addConstructor($1.intermNode, &type, op, fnCall, @1);
-            }
-
-            if ($$ == 0) {
-                context->recover();
-                $$ = context->intermediate.setAggregateOperator(0, op, @1);
-            }
-            $$->setType(type);
-        } else {
-            //
-            // Not a constructor.  Find it in the symbol table.
-            //
-            const TFunction* fnCandidate;
-            bool builtIn;
-            fnCandidate = context->findFunction(@1, fnCall, &builtIn);
-            if (fnCandidate) {
-                //
-                // A declared function.
-                //
-                if (builtIn && !fnCandidate->getExtension().empty() &&
-                    context->extensionErrorCheck(@1, fnCandidate->getExtension())) {
-                    context->recover();
-                }
-                op = fnCandidate->getBuiltInOp();
-                if (builtIn && op != EOpNull) {
-                    //
-                    // A function call mapped to a built-in operation.
-                    //
-                    if (fnCandidate->getParamCount() == 1) {
-                        //
-                        // Treat it like a built-in unary operator.
-                        //
-                        $$ = context->intermediate.addUnaryMath(op, $1.intermNode, 0);
-                        if ($$ == 0)  {
-                            std::stringstream extraInfoStream;
-                            extraInfoStream << "built in unary operator function.  Type: " << static_cast<TIntermTyped*>($1.intermNode)->getCompleteString();
-                            std::string extraInfo = extraInfoStream.str();
-                            context->error($1.intermNode->getLine(), " wrong operand type", "Internal Error", extraInfo.c_str());
-                            YYERROR;
-                        }
-                    } else {
-                        $$ = context->intermediate.setAggregateOperator($1.intermAggregate, op, @1);
-                    }
-                } else {
-                    // This is a real function call
-
-                    $$ = context->intermediate.setAggregateOperator($1.intermAggregate, EOpFunctionCall, @1);
-                    $$->setType(fnCandidate->getReturnType());
-
-                    // this is how we know whether the given function is a builtIn function or a user defined function
-                    // if builtIn == false, it's a userDefined -> could be an overloaded builtIn function also
-                    // if builtIn == true, it's definitely a builtIn function with EOpNull
-                    if (!builtIn)
-                        $$->getAsAggregate()->setUserDefined();
-                    $$->getAsAggregate()->setName(fnCandidate->getMangledName());
-
-                    TQualifier qual;
-                    for (size_t i = 0; i < fnCandidate->getParamCount(); ++i) {
-                        qual = fnCandidate->getParam(i).type->getQualifier();
-                        if (qual == EvqOut || qual == EvqInOut) {
-                            if (context->lValueErrorCheck($$->getLine(), "assign", $$->getAsAggregate()->getSequence()[i]->getAsTyped())) {
-                                context->error($1.intermNode->getLine(), "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error");
-                                context->recover();
-                            }
-                        }
-                    }
-                }
-                $$->setType(fnCandidate->getReturnType());
-            } else {
-                // error message was put out by PaFindFunction()
-                // Put on a dummy node for error recovery
-                ConstantUnion *unionArray = new ConstantUnion[1];
-                unionArray->setFConst(0.0f);
-                $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpUndefined, EvqConstExpr), @1);
-                context->recover();
-            }
+            YYERROR;

         }
-        delete fnCall;
     }
     ;
 
diff --git a/src/OpenGL/compiler/glslang_tab.cpp b/src/OpenGL/compiler/glslang_tab.cpp
index af91edd..75f8f59 100644
--- a/src/OpenGL/compiler/glslang_tab.cpp
+++ b/src/OpenGL/compiler/glslang_tab.cpp
@@ -766,33 +766,33 @@
 static const yytype_uint16 yyrline[] =
 {
        0,   202,   202,   237,   240,   245,   250,   255,   260,   266,
-     269,   272,   275,   278,   281,   287,   295,   395,   398,   406,
-     410,   417,   421,   428,   434,   443,   451,   457,   464,   474,
-     477,   487,   497,   519,   520,   521,   522,   530,   531,   535,
-     539,   547,   548,   551,   557,   558,   562,   569,   570,   573,
-     576,   579,   585,   586,   589,   595,   596,   603,   604,   611,
-     612,   619,   620,   626,   627,   633,   634,   640,   641,   658,
-     659,   667,   668,   669,   670,   672,   673,   674,   676,   678,
-     680,   682,   687,   690,   701,   709,   717,   744,   750,   757,
-     761,   765,   769,   776,   814,   817,   824,   832,   853,   874,
-     885,   914,   919,   929,   934,   944,   947,   950,   953,   959,
-     966,   969,   973,   977,   982,   987,   994,   998,  1002,  1006,
-    1011,  1016,  1020,  1096,  1106,  1112,  1115,  1121,  1127,  1134,
-    1143,  1152,  1155,  1158,  1165,  1169,  1176,  1180,  1185,  1190,
-    1200,  1210,  1219,  1229,  1236,  1239,  1242,  1248,  1255,  1258,
-    1264,  1267,  1270,  1276,  1279,  1284,  1299,  1303,  1307,  1311,
-    1315,  1319,  1324,  1329,  1334,  1339,  1344,  1349,  1354,  1359,
-    1364,  1369,  1374,  1379,  1385,  1391,  1397,  1403,  1409,  1415,
-    1421,  1427,  1433,  1438,  1443,  1452,  1457,  1462,  1467,  1472,
-    1477,  1482,  1487,  1492,  1497,  1502,  1507,  1512,  1517,  1522,
-    1535,  1535,  1538,  1538,  1544,  1547,  1563,  1566,  1575,  1579,
-    1585,  1592,  1607,  1611,  1615,  1616,  1622,  1623,  1624,  1625,
-    1626,  1627,  1628,  1632,  1633,  1633,  1633,  1643,  1644,  1648,
-    1648,  1649,  1649,  1654,  1657,  1667,  1670,  1676,  1677,  1681,
-    1689,  1693,  1700,  1700,  1707,  1710,  1719,  1724,  1741,  1741,
-    1746,  1746,  1753,  1753,  1761,  1764,  1770,  1773,  1779,  1783,
-    1790,  1793,  1796,  1799,  1802,  1811,  1815,  1822,  1825,  1831,
-    1831
+     269,   272,   275,   278,   281,   287,   295,   306,   309,   317,
+     321,   328,   332,   339,   345,   354,   362,   368,   375,   385,
+     388,   398,   408,   430,   431,   432,   433,   441,   442,   446,
+     450,   458,   459,   462,   468,   469,   473,   480,   481,   484,
+     487,   490,   496,   497,   500,   506,   507,   514,   515,   522,
+     523,   530,   531,   537,   538,   544,   545,   551,   552,   569,
+     570,   578,   579,   580,   581,   583,   584,   585,   587,   589,
+     591,   593,   598,   601,   612,   620,   628,   655,   661,   668,
+     672,   676,   680,   687,   725,   728,   735,   743,   764,   785,
+     796,   825,   830,   840,   845,   855,   858,   861,   864,   870,
+     877,   880,   884,   888,   893,   898,   905,   909,   913,   917,
+     922,   927,   931,  1007,  1017,  1023,  1026,  1032,  1038,  1045,
+    1054,  1063,  1066,  1069,  1076,  1080,  1087,  1091,  1096,  1101,
+    1111,  1121,  1130,  1140,  1147,  1150,  1153,  1159,  1166,  1169,
+    1175,  1178,  1181,  1187,  1190,  1195,  1210,  1214,  1218,  1222,
+    1226,  1230,  1235,  1240,  1245,  1250,  1255,  1260,  1265,  1270,
+    1275,  1280,  1285,  1290,  1296,  1302,  1308,  1314,  1320,  1326,
+    1332,  1338,  1344,  1349,  1354,  1363,  1368,  1373,  1378,  1383,
+    1388,  1393,  1398,  1403,  1408,  1413,  1418,  1423,  1428,  1433,
+    1446,  1446,  1449,  1449,  1455,  1458,  1474,  1477,  1486,  1490,
+    1496,  1503,  1518,  1522,  1526,  1527,  1533,  1534,  1535,  1536,
+    1537,  1538,  1539,  1543,  1544,  1544,  1544,  1554,  1555,  1559,
+    1559,  1560,  1560,  1565,  1568,  1578,  1581,  1587,  1588,  1592,
+    1600,  1604,  1611,  1611,  1618,  1621,  1630,  1635,  1652,  1652,
+    1657,  1657,  1664,  1664,  1672,  1675,  1681,  1684,  1690,  1694,
+    1701,  1704,  1707,  1710,  1713,  1722,  1726,  1733,  1736,  1742,
+    1742
 };
 #endif
 
@@ -2625,101 +2625,12 @@
   case 16:
 
     {
-        TFunction* fnCall = (yyvsp[(1) - (1)].interm).function;
-        TOperator op = fnCall->getBuiltInOp();
-
-        if (op != EOpNull)
+        bool fatalError = false;
+        (yyval.interm.intermTypedNode) = context->addFunctionCallOrMethod((yyvsp[(1) - (1)].interm).function, (yyvsp[(1) - (1)].interm).intermNode, nullptr, (yylsp[(1) - (1)]), &fatalError);
+        if (fatalError)
         {
-            //
-            // Then this should be a constructor.
-            // Don't go through the symbol table for constructors.
-            // Their parameters will be verified algorithmically.
-            //
-            TType type(EbtVoid, EbpUndefined);  // use this to get the type back
-            if (context->constructorErrorCheck((yylsp[(1) - (1)]), (yyvsp[(1) - (1)].interm).intermNode, *fnCall, op, &type)) {
-                (yyval.interm.intermTypedNode) = 0;
-            } else {
-                //
-                // It's a constructor, of type 'type'.
-                //
-                (yyval.interm.intermTypedNode) = context->addConstructor((yyvsp[(1) - (1)].interm).intermNode, &type, op, fnCall, (yylsp[(1) - (1)]));
-            }
-
-            if ((yyval.interm.intermTypedNode) == 0) {
-                context->recover();
-                (yyval.interm.intermTypedNode) = context->intermediate.setAggregateOperator(0, op, (yylsp[(1) - (1)]));
-            }
-            (yyval.interm.intermTypedNode)->setType(type);
-        } else {
-            //
-            // Not a constructor.  Find it in the symbol table.
-            //
-            const TFunction* fnCandidate;
-            bool builtIn;
-            fnCandidate = context->findFunction((yylsp[(1) - (1)]), fnCall, &builtIn);
-            if (fnCandidate) {
-                //
-                // A declared function.
-                //
-                if (builtIn && !fnCandidate->getExtension().empty() &&
-                    context->extensionErrorCheck((yylsp[(1) - (1)]), fnCandidate->getExtension())) {
-                    context->recover();
-                }
-                op = fnCandidate->getBuiltInOp();
-                if (builtIn && op != EOpNull) {
-                    //
-                    // A function call mapped to a built-in operation.
-                    //
-                    if (fnCandidate->getParamCount() == 1) {
-                        //
-                        // Treat it like a built-in unary operator.
-                        //
-                        (yyval.interm.intermTypedNode) = context->intermediate.addUnaryMath(op, (yyvsp[(1) - (1)].interm).intermNode, 0);
-                        if ((yyval.interm.intermTypedNode) == 0)  {
-                            std::stringstream extraInfoStream;
-                            extraInfoStream << "built in unary operator function.  Type: " << static_cast<TIntermTyped*>((yyvsp[(1) - (1)].interm).intermNode)->getCompleteString();
-                            std::string extraInfo = extraInfoStream.str();
-                            context->error((yyvsp[(1) - (1)].interm).intermNode->getLine(), " wrong operand type", "Internal Error", extraInfo.c_str());
-                            YYERROR;
-                        }
-                    } else {
-                        (yyval.interm.intermTypedNode) = context->intermediate.setAggregateOperator((yyvsp[(1) - (1)].interm).intermAggregate, op, (yylsp[(1) - (1)]));
-                    }
-                } else {
-                    // This is a real function call
-
-                    (yyval.interm.intermTypedNode) = context->intermediate.setAggregateOperator((yyvsp[(1) - (1)].interm).intermAggregate, EOpFunctionCall, (yylsp[(1) - (1)]));
-                    (yyval.interm.intermTypedNode)->setType(fnCandidate->getReturnType());
-
-                    // this is how we know whether the given function is a builtIn function or a user defined function
-                    // if builtIn == false, it's a userDefined -> could be an overloaded builtIn function also
-                    // if builtIn == true, it's definitely a builtIn function with EOpNull
-                    if (!builtIn)
-                        (yyval.interm.intermTypedNode)->getAsAggregate()->setUserDefined();
-                    (yyval.interm.intermTypedNode)->getAsAggregate()->setName(fnCandidate->getMangledName());
-
-                    TQualifier qual;
-                    for (size_t i = 0; i < fnCandidate->getParamCount(); ++i) {
-                        qual = fnCandidate->getParam(i).type->getQualifier();
-                        if (qual == EvqOut || qual == EvqInOut) {
-                            if (context->lValueErrorCheck((yyval.interm.intermTypedNode)->getLine(), "assign", (yyval.interm.intermTypedNode)->getAsAggregate()->getSequence()[i]->getAsTyped())) {
-                                context->error((yyvsp[(1) - (1)].interm).intermNode->getLine(), "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error");
-                                context->recover();
-                            }
-                        }
-                    }
-                }
-                (yyval.interm.intermTypedNode)->setType(fnCandidate->getReturnType());
-            } else {
-                // error message was put out by PaFindFunction()
-                // Put on a dummy node for error recovery
-                ConstantUnion *unionArray = new ConstantUnion[1];
-                unionArray->setFConst(0.0f);
-                (yyval.interm.intermTypedNode) = context->intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpUndefined, EvqConstExpr), (yylsp[(1) - (1)]));
-                context->recover();
-            }
+            YYERROR;
         }
-        delete fnCall;
     }
     break;