• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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