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 "include/sksl/DSLFunction.h"
9
10 #include "include/sksl/DSLVar.h"
11 #include "src/sksl/SkSLAnalysis.h"
12 #include "src/sksl/SkSLCompiler.h"
13 #include "src/sksl/SkSLThreadContext.h"
14 #include "src/sksl/dsl/priv/DSLWriter.h"
15 #include "src/sksl/ir/SkSLFunctionCall.h"
16 #include "src/sksl/ir/SkSLFunctionDefinition.h"
17 #include "src/sksl/ir/SkSLFunctionPrototype.h"
18 #include "src/sksl/ir/SkSLReturnStatement.h"
19
20 namespace SkSL {
21
22 namespace dsl {
23
init(DSLModifiers modifiers,const DSLType & returnType,skstd::string_view name,SkTArray<DSLParameter * > params,PositionInfo pos)24 void DSLFunction::init(DSLModifiers modifiers, const DSLType& returnType, skstd::string_view name,
25 SkTArray<DSLParameter*> params, PositionInfo pos) {
26 fPosition = pos;
27 // Conservatively assume all user-defined functions have side effects.
28 if (!ThreadContext::IsModule()) {
29 modifiers.fModifiers.fFlags |= Modifiers::kHasSideEffects_Flag;
30 }
31
32 if (ThreadContext::Settings().fForceNoInline) {
33 // Apply the `noinline` modifier to every function. This allows us to test Runtime
34 // Effects without any inlining, even when the code is later added to a paint.
35 modifiers.fModifiers.fFlags &= ~Modifiers::kInline_Flag;
36 modifiers.fModifiers.fFlags |= Modifiers::kNoInline_Flag;
37 }
38
39 std::vector<std::unique_ptr<Variable>> paramVars;
40 paramVars.reserve(params.size());
41 for (DSLParameter* param : params) {
42 if (param->fDeclared) {
43 ThreadContext::ReportError("parameter has already been used in another function");
44 }
45 SkASSERT(!param->fInitialValue.hasValue());
46 SkASSERT(!param->fDeclaration);
47 param->fDeclared = true;
48 std::unique_ptr<SkSL::Variable> paramVar = DSLWriter::CreateParameterVar(*param);
49 if (!paramVar) {
50 return;
51 }
52 paramVars.push_back(std::move(paramVar));
53 }
54 SkASSERT(paramVars.size() == params.size());
55 fDecl = SkSL::FunctionDeclaration::Convert(ThreadContext::Context(),
56 *ThreadContext::SymbolTable(),
57 pos.line(),
58 ThreadContext::Modifiers(modifiers.fModifiers),
59 name == "main" ? name : DSLWriter::Name(name),
60 std::move(paramVars), &returnType.skslType());
61 ThreadContext::ReportErrors(pos);
62 if (fDecl) {
63 for (size_t i = 0; i < params.size(); ++i) {
64 params[i]->fVar = fDecl->parameters()[i];
65 params[i]->fInitialized = true;
66 }
67 // We don't know when this function is going to be defined; go ahead and add a prototype in
68 // case the definition is delayed. If we end up defining the function immediately, we'll
69 // remove the prototype in define().
70 ThreadContext::ProgramElements().push_back(std::make_unique<SkSL::FunctionPrototype>(
71 pos.line(), fDecl, ThreadContext::IsModule()));
72 }
73 }
74
define(DSLBlock block,PositionInfo pos)75 void DSLFunction::define(DSLBlock block, PositionInfo pos) {
76 std::unique_ptr<SkSL::Block> body = block.release();
77 if (!fDecl) {
78 // Evidently we failed to create the declaration; error should already have been reported.
79 // Release the block so we don't fail its destructor assert.
80 return;
81 }
82 if (!ThreadContext::ProgramElements().empty()) {
83 // If the last ProgramElement was the prototype for this function, it was unnecessary and we
84 // can remove it.
85 const SkSL::ProgramElement& last = *ThreadContext::ProgramElements().back();
86 if (last.is<SkSL::FunctionPrototype>()) {
87 const SkSL::FunctionPrototype& prototype = last.as<SkSL::FunctionPrototype>();
88 if (&prototype.declaration() == fDecl) {
89 ThreadContext::ProgramElements().pop_back();
90 }
91 }
92 }
93 if (fDecl->definition()) {
94 ThreadContext::ReportError(String::printf("function '%s' was already defined",
95 fDecl->description().c_str()), pos);
96 block.release();
97 return;
98 }
99 std::unique_ptr<FunctionDefinition> function = FunctionDefinition::Convert(
100 ThreadContext::Context(),
101 pos.line(),
102 *fDecl,
103 std::move(body),
104 /*builtin=*/false);
105 ThreadContext::ReportErrors(fPosition);
106 fDecl->setDefinition(function.get());
107 ThreadContext::ProgramElements().push_back(std::move(function));
108 }
109
call(SkTArray<DSLWrapper<DSLExpression>> args,PositionInfo pos)110 DSLExpression DSLFunction::call(SkTArray<DSLWrapper<DSLExpression>> args, PositionInfo pos) {
111 ExpressionArray released;
112 released.reserve_back(args.size());
113 for (DSLWrapper<DSLExpression>& arg : args) {
114 released.push_back(arg->release());
115 }
116 return this->call(std::move(released));
117 }
118
call(ExpressionArray args,PositionInfo pos)119 DSLExpression DSLFunction::call(ExpressionArray args, PositionInfo pos) {
120 std::unique_ptr<SkSL::Expression> result = SkSL::FunctionCall::Convert(ThreadContext::Context(),
121 pos.line(), *fDecl, std::move(args));
122 return DSLExpression(std::move(result), pos);
123 }
124
125 } // namespace dsl
126
127 } // namespace SkSL
128