SkSL: Add support for method calls on effect objects

Includes .eval() as a new alternative syntax for sample/shade.

Bug: skia:12302
Change-Id: Ia921c141b3eeeba7e5309d921967b941f9cd055e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/444756
Reviewed-by: John Stiles <johnstiles@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index f60ca6a..4f234ad 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -45,6 +45,7 @@
 #include "src/sksl/ir/SkSLIndexExpression.h"
 #include "src/sksl/ir/SkSLIntLiteral.h"
 #include "src/sksl/ir/SkSLInterfaceBlock.h"
+#include "src/sksl/ir/SkSLMethodReference.h"
 #include "src/sksl/ir/SkSLNop.h"
 #include "src/sksl/ir/SkSLPostfixExpression.h"
 #include "src/sksl/ir/SkSLPrefixExpression.h"
@@ -1050,7 +1051,7 @@
  * the call is not valid.
  */
 CoercionCost IRGenerator::callCost(const FunctionDeclaration& function,
-                                   const ExpressionArray& arguments) {
+                                   const ExpressionArray& arguments) const {
     if (this->strictES2Mode() && (function.modifiers().fFlags & Modifiers::kES3_Flag)) {
         return CoercionCost::Impossible();
     }
@@ -1069,6 +1070,24 @@
     return total;
 }
 
+const FunctionDeclaration* IRGenerator::findBestFunctionForCall(
+        const std::vector<const FunctionDeclaration*>& functions,
+        const ExpressionArray& arguments) const {
+    if (functions.size() == 1) {
+        return functions.front();
+    }
+    CoercionCost bestCost = CoercionCost::Impossible();
+    const FunctionDeclaration* best = nullptr;
+    for (const auto& f : functions) {
+        CoercionCost cost = this->callCost(*f, arguments);
+        if (cost < bestCost) {
+            bestCost = cost;
+            best = f;
+        }
+    }
+    return best;
+}
+
 std::unique_ptr<Expression> IRGenerator::call(int offset,
                                               std::unique_ptr<Expression> functionValue,
                                               ExpressionArray arguments) {
@@ -1102,31 +1121,41 @@
         case Expression::Kind::kFunctionReference: {
             const FunctionReference& ref = functionValue->as<FunctionReference>();
             const std::vector<const FunctionDeclaration*>& functions = ref.functions();
-            CoercionCost bestCost = CoercionCost::Impossible();
-            const FunctionDeclaration* best = nullptr;
-            if (functions.size() > 1) {
-                for (const auto& f : functions) {
-                    CoercionCost cost = this->callCost(*f, arguments);
-                    if (cost < bestCost) {
-                        bestCost = cost;
-                        best = f;
-                    }
-                }
-                if (best) {
-                    return this->call(offset, *best, std::move(arguments));
-                }
-                String msg = "no match for " + functions[0]->name() + "(";
-                String separator;
-                for (size_t i = 0; i < arguments.size(); i++) {
-                    msg += separator;
-                    separator = ", ";
-                    msg += arguments[i]->type().displayName();
-                }
-                msg += ")";
-                this->errorReporter().error(offset, msg);
-                return nullptr;
+            const FunctionDeclaration* best = this->findBestFunctionForCall(functions, arguments);
+            if (best) {
+                return this->call(offset, *best, std::move(arguments));
             }
-            return this->call(offset, *functions[0], std::move(arguments));
+            String msg = "no match for " + functions[0]->name() + "(";
+            String separator;
+            for (size_t i = 0; i < arguments.size(); i++) {
+                msg += separator;
+                separator = ", ";
+                msg += arguments[i]->type().displayName();
+            }
+            msg += ")";
+            this->errorReporter().error(offset, msg);
+            return nullptr;
+        }
+        case Expression::Kind::kMethodReference: {
+            MethodReference& ref = functionValue->as<MethodReference>();
+            arguments.push_back(std::move(ref.self()));
+
+            const std::vector<const FunctionDeclaration*>& functions = ref.functions();
+            const FunctionDeclaration* best = this->findBestFunctionForCall(functions, arguments);
+            if (best) {
+                return this->call(offset, *best, std::move(arguments));
+            }
+            String msg = "no match for " + arguments.back()->type().displayName() +
+                         "::" + functions[0]->name().substr(1) + "(";
+            String separator;
+            for (size_t i = 0; i < arguments.size() - 1; i++) {
+                msg += separator;
+                separator = ", ";
+                msg += arguments[i]->type().displayName();
+            }
+            msg += ")";
+            this->errorReporter().error(offset, msg);
+            return nullptr;
         }
         case Expression::Kind::kPoison:
             return functionValue;
@@ -1197,8 +1226,8 @@
     }
     const skstd::string_view& field = fieldNode.getStringView();
     const Type& baseType = base->type();
-    if (baseType == *fContext.fTypes.fSkCaps || baseType.isStruct()) {
-        return FieldAccess::Convert(fContext, std::move(base), field);
+    if (baseType == *fContext.fTypes.fSkCaps || baseType.isStruct() || baseType.isEffectChild()) {
+        return FieldAccess::Convert(fContext, *fSymbolTable, std::move(base), field);
     }
     return this->convertSwizzle(std::move(base), field);
 }