blob: 2431eb464722763f47903eecdcbf2ac9a58fd048 [file] [log] [blame]
/*
* 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/SkSLAnalysis.h"
#include "src/sksl/SkSLContext.h"
#include "src/sksl/SkSLProgramSettings.h"
#include "src/sksl/ir/SkSLFunctionCall.h"
namespace SkSL {
bool FunctionCall::hasProperty(Property property) const {
if (property == Property::kSideEffects &&
(this->function().modifiers().fFlags & Modifiers::kHasSideEffects_Flag)) {
return true;
}
for (const auto& arg : this->arguments()) {
if (arg->hasProperty(property)) {
return true;
}
}
return false;
}
std::unique_ptr<Expression> FunctionCall::clone() const {
ExpressionArray cloned;
cloned.reserve_back(this->arguments().size());
for (const std::unique_ptr<Expression>& arg : this->arguments()) {
cloned.push_back(arg->clone());
}
return std::make_unique<FunctionCall>(
fOffset, &this->type(), &this->function(), std::move(cloned));
}
String FunctionCall::description() const {
String result = String(this->function().name()) + "(";
String separator;
for (const std::unique_ptr<Expression>& arg : this->arguments()) {
result += separator;
result += arg->description();
separator = ", ";
}
result += ")";
return result;
}
std::unique_ptr<Expression> FunctionCall::Convert(const Context& context,
int offset,
const FunctionDeclaration& function,
ExpressionArray arguments) {
// Reject function calls with the wrong number of arguments.
if (function.parameters().size() != arguments.size()) {
String msg = "call to '" + function.name() + "' expected " +
to_string((int)function.parameters().size()) + " argument";
if (function.parameters().size() != 1) {
msg += "s";
}
msg += ", but found " + to_string(arguments.count());
context.fErrors.error(offset, msg);
return nullptr;
}
// GLSL ES 1.0 requires static recursion be rejected by the compiler. Also, our CPU back-end
// cannot handle recursion (and is tied to strictES2Mode front-ends). The safest way to reject
// all (potentially) recursive code is to disallow calls to functions before they're defined.
if (context.fConfig->strictES2Mode() && !function.definition() && !function.isBuiltin()) {
context.fErrors.error(offset, "call to undefined function '" + function.name() + "'");
return nullptr;
}
// Resolve generic types.
FunctionDeclaration::ParamTypes types;
const Type* returnType;
if (!function.determineFinalTypes(arguments, &types, &returnType)) {
String msg = "no match for " + function.name() + "(";
String separator;
for (const std::unique_ptr<Expression>& arg : arguments) {
msg += separator;
msg += arg->type().displayName();
separator = ", ";
}
msg += ")";
context.fErrors.error(offset, msg);
return nullptr;
}
for (size_t i = 0; i < arguments.size(); i++) {
// Coerce each argument to the proper type.
arguments[i] = types[i]->coerceExpression(std::move(arguments[i]), context);
if (!arguments[i]) {
return nullptr;
}
// Update the refKind on out-parameters, and ensure that they are actually assignable.
const Modifiers& paramModifiers = function.parameters()[i]->modifiers();
if (paramModifiers.fFlags & Modifiers::kOut_Flag) {
const VariableRefKind refKind = paramModifiers.fFlags & Modifiers::kIn_Flag
? VariableReference::RefKind::kReadWrite
: VariableReference::RefKind::kPointer;
if (!Analysis::MakeAssignmentExpr(arguments[i].get(), refKind, &context.fErrors)) {
return nullptr;
}
}
}
return Make(context, offset, returnType, function, std::move(arguments));
}
std::unique_ptr<Expression> FunctionCall::Make(const Context& context,
int offset,
const Type* returnType,
const FunctionDeclaration& function,
ExpressionArray arguments) {
SkASSERT(function.parameters().size() == arguments.size());
SkASSERT(function.definition() || function.isBuiltin() || !context.fConfig->strictES2Mode());
return std::make_unique<FunctionCall>(offset, returnType, &function, std::move(arguments));
}
} // namespace SkSL