xref: /aosp_15_r20/external/skia/src/sksl/ir/SkSLFieldAccess.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2021 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/sksl/ir/SkSLFieldAccess.h"
9 
10 #include "include/core/SkSpan.h"
11 #include "include/core/SkTypes.h"
12 #include "include/private/base/SkTArray.h"
13 #include "src/sksl/SkSLAnalysis.h"
14 #include "src/sksl/SkSLBuiltinTypes.h"
15 #include "src/sksl/SkSLConstantFolder.h"
16 #include "src/sksl/SkSLContext.h"
17 #include "src/sksl/SkSLDefines.h"
18 #include "src/sksl/SkSLErrorReporter.h"
19 #include "src/sksl/SkSLOperator.h"
20 #include "src/sksl/ir/SkSLConstructorStruct.h"
21 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
22 #include "src/sksl/ir/SkSLMethodReference.h"
23 #include "src/sksl/ir/SkSLSetting.h"
24 #include "src/sksl/ir/SkSLSymbol.h"
25 #include "src/sksl/ir/SkSLSymbolTable.h"  // IWYU pragma: keep
26 
27 #include <cstddef>
28 
29 namespace SkSL {
30 
Convert(const Context & context,Position pos,std::unique_ptr<Expression> base,std::string_view field)31 std::unique_ptr<Expression> FieldAccess::Convert(const Context& context,
32                                                  Position pos,
33                                                  std::unique_ptr<Expression> base,
34                                                  std::string_view field) {
35     const Type& baseType = base->type();
36     if (baseType.isEffectChild()) {
37         // Turn the field name into a free function name, prefixed with '$':
38         std::string methodName = "$" + std::string(field);
39         const Symbol* result = context.fSymbolTable->find(methodName);
40         if (result && result->is<FunctionDeclaration>()) {
41             return std::make_unique<MethodReference>(context, pos, std::move(base),
42                                                      &result->as<FunctionDeclaration>());
43         }
44         context.fErrors->error(pos, "type '" + baseType.displayName() + "' has no method named '" +
45                                     std::string(field) + "'");
46         return nullptr;
47     }
48     if (baseType.isStruct()) {
49         SkSpan<const Field> fields = baseType.fields();
50         for (size_t i = 0; i < fields.size(); i++) {
51             if (fields[i].fName == field) {
52                 return FieldAccess::Make(context, pos, std::move(base), (int)i);
53             }
54         }
55     }
56     if (baseType.matches(*context.fTypes.fSkCaps)) {
57         return Setting::Convert(context, pos, field);
58     }
59 
60     context.fErrors->error(pos, "type '" + baseType.displayName() +
61                                 "' does not have a field named '" + std::string(field) + "'");
62     return nullptr;
63 }
64 
extract_field(Position pos,const ConstructorStruct & ctor,int fieldIndex)65 static std::unique_ptr<Expression> extract_field(Position pos,
66                                                  const ConstructorStruct& ctor,
67                                                  int fieldIndex) {
68     // Confirm that the fields that are being removed are side-effect free.
69     const ExpressionArray& args = ctor.arguments();
70     int numFields = args.size();
71     for (int index = 0; index < numFields; ++index) {
72         if (fieldIndex == index) {
73             continue;
74         }
75         if (Analysis::HasSideEffects(*args[index])) {
76             return nullptr;
77         }
78     }
79 
80     // Return the desired field.
81     return args[fieldIndex]->clone(pos);
82 }
83 
Make(const Context & context,Position pos,std::unique_ptr<Expression> base,int fieldIndex,OwnerKind ownerKind)84 std::unique_ptr<Expression> FieldAccess::Make(const Context& context,
85                                               Position pos,
86                                               std::unique_ptr<Expression> base,
87                                               int fieldIndex,
88                                               OwnerKind ownerKind) {
89     SkASSERT(base->type().isStruct());
90     SkASSERT(fieldIndex >= 0);
91     SkASSERT(fieldIndex < (int)base->type().fields().size());
92 
93     // Replace `knownStruct.field` with the field's value if there are no side-effects involved.
94     const Expression* expr = ConstantFolder::GetConstantValueForVariable(*base);
95     if (expr->is<ConstructorStruct>()) {
96         if (std::unique_ptr<Expression> field = extract_field(pos, expr->as<ConstructorStruct>(),
97                                                               fieldIndex)) {
98             return field;
99         }
100     }
101 
102     return std::make_unique<FieldAccess>(pos, std::move(base), fieldIndex, ownerKind);
103 }
104 
initialSlot() const105 size_t FieldAccess::initialSlot() const {
106     SkSpan<const Field> fields = this->base()->type().fields();
107     const int fieldIndex = this->fieldIndex();
108 
109     size_t slot = 0;
110     for (int index = 0; index < fieldIndex; ++index) {
111         slot += fields[index].fType->slotCount();
112     }
113     return slot;
114 }
115 
description(OperatorPrecedence) const116 std::string FieldAccess::description(OperatorPrecedence) const {
117     std::string f = this->base()->description(OperatorPrecedence::kPostfix);
118     if (!f.empty()) {
119         f.push_back('.');
120     }
121     return f + std::string(this->base()->type().fields()[this->fieldIndex()].fName);
122 }
123 
124 }  // namespace SkSL
125