SkSL now supports null child processors

Bug: skia:
Change-Id: Icb3f2d57b393e3d40247aaad98c17344b507f0d7
Reviewed-on: https://skia-review.googlesource.com/c/193379
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
diff --git a/src/sksl/SkSLCPPCodeGenerator.cpp b/src/sksl/SkSLCPPCodeGenerator.cpp
index 237148a..a322ebd 100644
--- a/src/sksl/SkSLCPPCodeGenerator.cpp
+++ b/src/sksl/SkSLCPPCodeGenerator.cpp
@@ -77,6 +77,32 @@
         if (precedence >= parentPrecedence) {
             this->write(")");
         }
+    } else if (b.fLeft->fKind == Expression::kNullLiteral_Kind ||
+               b.fRight->fKind == Expression::kNullLiteral_Kind) {
+        const Variable* var;
+        if (b.fLeft->fKind != Expression::kNullLiteral_Kind) {
+            SkASSERT(b.fLeft->fKind == Expression::kVariableReference_Kind);
+            var = &((VariableReference&) *b.fLeft).fVariable;
+        } else {
+            SkASSERT(b.fRight->fKind == Expression::kVariableReference_Kind);
+            var = &((VariableReference&) *b.fRight).fVariable;
+        }
+        SkASSERT(var->fType.kind() == Type::kNullable_Kind &&
+                 var->fType.componentType() == *fContext.fFragmentProcessor_Type);
+        this->write("%s");
+        const char* op;
+        switch (b.fOperator) {
+            case Token::EQEQ:
+                op = "<";
+                break;
+            case Token::NEQ:
+                op = ">=";
+                break;
+            default:
+                SkASSERT(false);
+        }
+        fFormatArgs.push_back("_outer." + String(var->fName) + "_index() " + op + " 0 ? \"true\" "
+                              ": \"false\"");
     } else {
         INHERITED::writeBinaryExpression(b, parentPrecedence);
     }
@@ -333,9 +359,10 @@
         }
 
         const Type::Field& field = fContext.fFragmentProcessor_Type->fields()[access.fFieldIndex];
-        int index = getChildFPIndex((const VariableReference&) *access.fBase);
-        String cppAccess = String::printf("_outer.childProcessor(%s).%s()",
-                                          to_string(index).c_str(), String(field.fName).c_str());
+        const Variable& var = ((const VariableReference&) *access.fBase).fVariable;
+        String cppAccess = String::printf("_outer.childProcessor(_outer.%s_index()).%s()",
+                                          String(var.fName).c_str(),
+                                          String(field.fName).c_str());
 
         if (fCPPMode) {
             this->write(cppAccess.c_str());
@@ -347,7 +374,7 @@
     INHERITED::writeFieldAccess(access);
 }
 
-int CPPCodeGenerator::getChildFPIndex(const VariableReference& reference) const {
+int CPPCodeGenerator::getChildFPIndex(const Variable& var) const {
     int index = 0;
     bool found = false;
     for (const auto& p : fProgram) {
@@ -355,9 +382,9 @@
             const VarDeclarations& decls = (const VarDeclarations&) p;
             for (const auto& raw : decls.fVars) {
                 const VarDeclaration& decl = (VarDeclaration&) *raw;
-                if (decl.fVar == &reference.fVariable) {
+                if (decl.fVar == &var) {
                     found = true;
-                } else if (decl.fVar->fType == *fContext.fFragmentProcessor_Type) {
+                } else if (decl.fVar->fType.nonnullable() == *fContext.fFragmentProcessor_Type) {
                     ++index;
                 }
             }
@@ -388,7 +415,8 @@
             // Second argument must also be a half4 expression
             SkASSERT("half4" == c.fArguments[1]->fType.name());
         }
-        int index = getChildFPIndex((const VariableReference&) *c.fArguments[0]);
+        const Variable& child = ((const VariableReference&) *c.fArguments[0]).fVariable;
+        int index = getChildFPIndex(child);
 
         // Start a new extra emit code section so that the emitted child processor can depend on
         // sksl variables defined in earlier sksl code.
@@ -412,9 +440,14 @@
         // Write the output handling after the possible input handling
         String childName = "_child" + to_string(index);
         addExtraEmitCodeLine("SkString " + childName + "(\"" + childName + "\");");
-        addExtraEmitCodeLine("this->emitChild(" + to_string(index) + inputArg +
-                             ", &" + childName + ", args);");
-
+        if (c.fArguments[0]->fType.kind() == Type::kNullable_Kind) {
+            addExtraEmitCodeLine("if (_outer." + String(child.fName) + "_index() >= 0) {\n    ");
+        }
+        addExtraEmitCodeLine("this->emitChild(_outer." + String(child.fName) + "_index()" +
+                             inputArg + ", &" + childName + ", args);");
+        if (c.fArguments[0]->fType.kind() == Type::kNullable_Kind) {
+            addExtraEmitCodeLine("}");
+        }
         this->write("%s");
         fFormatArgs.push_back(childName + ".c_str()");
         return;
@@ -597,8 +630,9 @@
 }
 
 static bool is_accessible(const Variable& var) {
-    return Type::kSampler_Kind != var.fType.kind() &&
-           Type::kOther_Kind != var.fType.kind();
+    const Type& type = var.fType.nonnullable();
+    return Type::kSampler_Kind != type.kind() &&
+           Type::kOther_Kind != type.kind();
 }
 
 void CPPCodeGenerator::newExtraEmitCodeBlock() {
@@ -991,13 +1025,16 @@
                      ": INHERITED(k%s_ClassID, src.optimizationFlags())", fFullName.c_str(),
                      fFullName.c_str(), fFullName.c_str(), fFullName.c_str());
         for (const auto& param : fSectionAndParameterHelper.getParameters()) {
-            if (param->fType == *fContext.fFragmentProcessor_Type) {
-                continue;
-            }
             String fieldName = HCodeGenerator::FieldName(String(param->fName).c_str());
-            this->writef("\n, %s(src.%s)",
-                         fieldName.c_str(),
-                         fieldName.c_str());
+            if (param->fType.nonnullable() == *fContext.fFragmentProcessor_Type) {
+                this->writef("\n, %s_index(src.%s_index)",
+                             fieldName.c_str(),
+                             fieldName.c_str());
+            } else {
+                this->writef("\n, %s(src.%s)",
+                             fieldName.c_str(),
+                             fieldName.c_str());
+            }
         }
         const auto transforms = fSectionAndParameterHelper.getSections(COORD_TRANSFORM_SECTION);
         for (size_t i = 0; i < transforms.size(); ++i) {
@@ -1006,14 +1043,20 @@
             this->writef("\n, %s(src.%s)", fieldName.c_str(), fieldName.c_str());
         }
         this->writef(" {\n");
-        int childCount = 0;
         int samplerCount = 0;
         for (const auto& param : fSectionAndParameterHelper.getParameters()) {
             if (param->fType.kind() == Type::kSampler_Kind) {
                 ++samplerCount;
-            } else if (param->fType == *fContext.fFragmentProcessor_Type) {
-                this->writef("    this->registerChildProcessor(src.childProcessor(%d).clone());"
-                             "\n", childCount++);
+            } else if (param->fType.nonnullable() == *fContext.fFragmentProcessor_Type) {
+                String fieldName = HCodeGenerator::FieldName(String(param->fName).c_str());
+                if (param->fType.kind() == Type::kNullable_Kind) {
+                    this->writef("    if (%s_index >= 0) {\n    ", fieldName.c_str());
+                }
+                this->writef("    this->registerChildProcessor(src.childProcessor(%s_index)."
+                             "clone());\n", fieldName.c_str());
+                if (param->fType.kind() == Type::kNullable_Kind) {
+                    this->writef("    }\n");
+                }
             }
         }
         if (samplerCount) {
@@ -1182,7 +1225,7 @@
                  "    (void) that;\n",
                  fullName, fullName, fullName);
     for (const auto& param : fSectionAndParameterHelper.getParameters()) {
-        if (param->fType == *fContext.fFragmentProcessor_Type) {
+        if (param->fType.nonnullable() == *fContext.fFragmentProcessor_Type) {
             continue;
         }
         String nameString(param->fName);