| /* |
| * Copyright 2021 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "src/sksl/ir/SkSLFieldAccess.h" |
| |
| #include "include/core/SkTypes.h" |
| #include "include/private/SkSLDefines.h" |
| #include "include/private/SkSLSymbol.h" |
| #include "include/private/SkTArray.h" |
| #include "include/sksl/SkSLErrorReporter.h" |
| #include "include/sksl/SkSLOperator.h" |
| #include "src/sksl/SkSLAnalysis.h" |
| #include "src/sksl/SkSLBuiltinTypes.h" |
| #include "src/sksl/SkSLConstantFolder.h" |
| #include "src/sksl/SkSLContext.h" |
| #include "src/sksl/ir/SkSLConstructorStruct.h" |
| #include "src/sksl/ir/SkSLFunctionDeclaration.h" |
| #include "src/sksl/ir/SkSLMethodReference.h" |
| #include "src/sksl/ir/SkSLSetting.h" |
| #include "src/sksl/ir/SkSLSymbolTable.h" |
| |
| #include <cstddef> |
| |
| namespace SkSL { |
| |
| std::unique_ptr<Expression> FieldAccess::Convert(const Context& context, |
| Position pos, |
| SymbolTable& symbolTable, |
| std::unique_ptr<Expression> base, |
| std::string_view field) { |
| const Type& baseType = base->type(); |
| if (baseType.isEffectChild()) { |
| // Turn the field name into a free function name, prefixed with '$': |
| std::string methodName = "$" + std::string(field); |
| const Symbol* result = symbolTable.find(methodName); |
| if (result && result->is<FunctionDeclaration>()) { |
| return std::make_unique<MethodReference>(context, pos, std::move(base), |
| &result->as<FunctionDeclaration>()); |
| } |
| context.fErrors->error(pos, "type '" + baseType.displayName() + "' has no method named '" + |
| std::string(field) + "'"); |
| return nullptr; |
| } |
| if (baseType.isStruct()) { |
| const std::vector<Type::Field>& fields = baseType.fields(); |
| for (size_t i = 0; i < fields.size(); i++) { |
| if (fields[i].fName == field) { |
| return FieldAccess::Make(context, pos, std::move(base), (int) i); |
| } |
| } |
| } |
| if (baseType.matches(*context.fTypes.fSkCaps)) { |
| return Setting::Convert(context, pos, field); |
| } |
| |
| context.fErrors->error(pos, "type '" + baseType.displayName() + |
| "' does not have a field named '" + std::string(field) + "'"); |
| return nullptr; |
| } |
| |
| static std::unique_ptr<Expression> extract_field(Position pos, |
| const ConstructorStruct& ctor, |
| int fieldIndex) { |
| // Confirm that the fields that are being removed are side-effect free. |
| const ExpressionArray& args = ctor.arguments(); |
| int numFields = args.size(); |
| for (int index = 0; index < numFields; ++index) { |
| if (fieldIndex == index) { |
| continue; |
| } |
| if (Analysis::HasSideEffects(*args[index])) { |
| return nullptr; |
| } |
| } |
| |
| // Return the desired field. |
| return args[fieldIndex]->clone(pos); |
| } |
| |
| std::unique_ptr<Expression> FieldAccess::Make(const Context& context, |
| Position pos, |
| std::unique_ptr<Expression> base, |
| int fieldIndex, |
| OwnerKind ownerKind) { |
| SkASSERT(base->type().isStruct()); |
| SkASSERT(fieldIndex >= 0); |
| SkASSERT(fieldIndex < (int) base->type().fields().size()); |
| |
| // Replace `knownStruct.field` with the field's value if there are no side-effects involved. |
| const Expression* expr = ConstantFolder::GetConstantValueForVariable(*base); |
| if (expr->is<ConstructorStruct>()) { |
| if (std::unique_ptr<Expression> field = extract_field(pos, expr->as<ConstructorStruct>(), |
| fieldIndex)) { |
| return field; |
| } |
| } |
| |
| return std::make_unique<FieldAccess>(pos, std::move(base), fieldIndex, ownerKind); |
| } |
| |
| std::string FieldAccess::description(OperatorPrecedence) const { |
| std::string f = this->base()->description(OperatorPrecedence::kPostfix); |
| if (!f.empty()) { |
| f.push_back('.'); |
| } |
| return f + std::string(this->base()->type().fields()[this->fieldIndex()].fName); |
| } |
| |
| } // namespace SkSL |