1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2021 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/tracing/SkSLDebugTracePlayer.h"
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/tracing/SkSLDebugTracePriv.h"
11*c8dee2aaSAndroid Build Coastguard Worker
12*c8dee2aaSAndroid Build Coastguard Worker #include <limits.h>
13*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
14*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
15*c8dee2aaSAndroid Build Coastguard Worker
16*c8dee2aaSAndroid Build Coastguard Worker namespace SkSL {
17*c8dee2aaSAndroid Build Coastguard Worker
reset(sk_sp<DebugTracePriv> debugTrace)18*c8dee2aaSAndroid Build Coastguard Worker void SkSLDebugTracePlayer::reset(sk_sp<DebugTracePriv> debugTrace) {
19*c8dee2aaSAndroid Build Coastguard Worker size_t nslots = debugTrace ? debugTrace->fSlotInfo.size() : 0;
20*c8dee2aaSAndroid Build Coastguard Worker fDebugTrace = debugTrace;
21*c8dee2aaSAndroid Build Coastguard Worker fCursor = 0;
22*c8dee2aaSAndroid Build Coastguard Worker fScope = 0;
23*c8dee2aaSAndroid Build Coastguard Worker fSlots.clear();
24*c8dee2aaSAndroid Build Coastguard Worker fSlots.resize(nslots, {/*fValue=*/0,
25*c8dee2aaSAndroid Build Coastguard Worker /*fScope=*/INT_MAX,
26*c8dee2aaSAndroid Build Coastguard Worker /*fWriteTime=*/0});
27*c8dee2aaSAndroid Build Coastguard Worker fStack.clear();
28*c8dee2aaSAndroid Build Coastguard Worker fStack.push_back({/*fFunction=*/-1,
29*c8dee2aaSAndroid Build Coastguard Worker /*fLine=*/-1,
30*c8dee2aaSAndroid Build Coastguard Worker /*fDisplayMask=*/SkBitSet(nslots)});
31*c8dee2aaSAndroid Build Coastguard Worker fDirtyMask.emplace(nslots);
32*c8dee2aaSAndroid Build Coastguard Worker fReturnValues.emplace(nslots);
33*c8dee2aaSAndroid Build Coastguard Worker
34*c8dee2aaSAndroid Build Coastguard Worker if (fDebugTrace) {
35*c8dee2aaSAndroid Build Coastguard Worker for (size_t slotIdx = 0; slotIdx < nslots; ++slotIdx) {
36*c8dee2aaSAndroid Build Coastguard Worker if (fDebugTrace->fSlotInfo[slotIdx].fnReturnValue >= 0) {
37*c8dee2aaSAndroid Build Coastguard Worker fReturnValues->set(slotIdx);
38*c8dee2aaSAndroid Build Coastguard Worker }
39*c8dee2aaSAndroid Build Coastguard Worker }
40*c8dee2aaSAndroid Build Coastguard Worker
41*c8dee2aaSAndroid Build Coastguard Worker for (const TraceInfo& trace : fDebugTrace->fTraceInfo) {
42*c8dee2aaSAndroid Build Coastguard Worker if (trace.op == TraceInfo::Op::kLine) {
43*c8dee2aaSAndroid Build Coastguard Worker fLineNumbers[trace.data[0]] += 1;
44*c8dee2aaSAndroid Build Coastguard Worker }
45*c8dee2aaSAndroid Build Coastguard Worker }
46*c8dee2aaSAndroid Build Coastguard Worker }
47*c8dee2aaSAndroid Build Coastguard Worker }
48*c8dee2aaSAndroid Build Coastguard Worker
step()49*c8dee2aaSAndroid Build Coastguard Worker void SkSLDebugTracePlayer::step() {
50*c8dee2aaSAndroid Build Coastguard Worker this->tidyState();
51*c8dee2aaSAndroid Build Coastguard Worker while (!this->traceHasCompleted()) {
52*c8dee2aaSAndroid Build Coastguard Worker if (this->execute(fCursor++)) {
53*c8dee2aaSAndroid Build Coastguard Worker break;
54*c8dee2aaSAndroid Build Coastguard Worker }
55*c8dee2aaSAndroid Build Coastguard Worker }
56*c8dee2aaSAndroid Build Coastguard Worker }
57*c8dee2aaSAndroid Build Coastguard Worker
stepOver()58*c8dee2aaSAndroid Build Coastguard Worker void SkSLDebugTracePlayer::stepOver() {
59*c8dee2aaSAndroid Build Coastguard Worker this->tidyState();
60*c8dee2aaSAndroid Build Coastguard Worker size_t initialStackDepth = fStack.size();
61*c8dee2aaSAndroid Build Coastguard Worker while (!this->traceHasCompleted()) {
62*c8dee2aaSAndroid Build Coastguard Worker bool canEscapeFromThisStackDepth = (fStack.size() <= initialStackDepth);
63*c8dee2aaSAndroid Build Coastguard Worker if (this->execute(fCursor++)) {
64*c8dee2aaSAndroid Build Coastguard Worker if (canEscapeFromThisStackDepth || this->atBreakpoint()) {
65*c8dee2aaSAndroid Build Coastguard Worker break;
66*c8dee2aaSAndroid Build Coastguard Worker }
67*c8dee2aaSAndroid Build Coastguard Worker }
68*c8dee2aaSAndroid Build Coastguard Worker }
69*c8dee2aaSAndroid Build Coastguard Worker }
70*c8dee2aaSAndroid Build Coastguard Worker
stepOut()71*c8dee2aaSAndroid Build Coastguard Worker void SkSLDebugTracePlayer::stepOut() {
72*c8dee2aaSAndroid Build Coastguard Worker this->tidyState();
73*c8dee2aaSAndroid Build Coastguard Worker size_t initialStackDepth = fStack.size();
74*c8dee2aaSAndroid Build Coastguard Worker while (!this->traceHasCompleted()) {
75*c8dee2aaSAndroid Build Coastguard Worker if (this->execute(fCursor++)) {
76*c8dee2aaSAndroid Build Coastguard Worker bool hasEscapedFromInitialStackDepth = (fStack.size() < initialStackDepth);
77*c8dee2aaSAndroid Build Coastguard Worker if (hasEscapedFromInitialStackDepth || this->atBreakpoint()) {
78*c8dee2aaSAndroid Build Coastguard Worker break;
79*c8dee2aaSAndroid Build Coastguard Worker }
80*c8dee2aaSAndroid Build Coastguard Worker }
81*c8dee2aaSAndroid Build Coastguard Worker }
82*c8dee2aaSAndroid Build Coastguard Worker }
83*c8dee2aaSAndroid Build Coastguard Worker
run()84*c8dee2aaSAndroid Build Coastguard Worker void SkSLDebugTracePlayer::run() {
85*c8dee2aaSAndroid Build Coastguard Worker this->tidyState();
86*c8dee2aaSAndroid Build Coastguard Worker while (!this->traceHasCompleted()) {
87*c8dee2aaSAndroid Build Coastguard Worker if (this->execute(fCursor++)) {
88*c8dee2aaSAndroid Build Coastguard Worker if (this->atBreakpoint()) {
89*c8dee2aaSAndroid Build Coastguard Worker break;
90*c8dee2aaSAndroid Build Coastguard Worker }
91*c8dee2aaSAndroid Build Coastguard Worker }
92*c8dee2aaSAndroid Build Coastguard Worker }
93*c8dee2aaSAndroid Build Coastguard Worker }
94*c8dee2aaSAndroid Build Coastguard Worker
tidyState()95*c8dee2aaSAndroid Build Coastguard Worker void SkSLDebugTracePlayer::tidyState() {
96*c8dee2aaSAndroid Build Coastguard Worker fDirtyMask->reset();
97*c8dee2aaSAndroid Build Coastguard Worker
98*c8dee2aaSAndroid Build Coastguard Worker // Conceptually this is `fStack.back().fDisplayMask &= ~fReturnValues`, but SkBitSet doesn't
99*c8dee2aaSAndroid Build Coastguard Worker // support masking one set of bits against another.
100*c8dee2aaSAndroid Build Coastguard Worker fReturnValues->forEachSetIndex([&](int slot) {
101*c8dee2aaSAndroid Build Coastguard Worker fStack.back().fDisplayMask.reset(slot);
102*c8dee2aaSAndroid Build Coastguard Worker });
103*c8dee2aaSAndroid Build Coastguard Worker }
104*c8dee2aaSAndroid Build Coastguard Worker
traceHasCompleted() const105*c8dee2aaSAndroid Build Coastguard Worker bool SkSLDebugTracePlayer::traceHasCompleted() const {
106*c8dee2aaSAndroid Build Coastguard Worker return !fDebugTrace || fCursor >= fDebugTrace->fTraceInfo.size();
107*c8dee2aaSAndroid Build Coastguard Worker }
108*c8dee2aaSAndroid Build Coastguard Worker
getCurrentLine() const109*c8dee2aaSAndroid Build Coastguard Worker int32_t SkSLDebugTracePlayer::getCurrentLine() const {
110*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fStack.empty());
111*c8dee2aaSAndroid Build Coastguard Worker return fStack.back().fLine;
112*c8dee2aaSAndroid Build Coastguard Worker }
113*c8dee2aaSAndroid Build Coastguard Worker
getCurrentLineInStackFrame(int stackFrameIndex) const114*c8dee2aaSAndroid Build Coastguard Worker int32_t SkSLDebugTracePlayer::getCurrentLineInStackFrame(int stackFrameIndex) const {
115*c8dee2aaSAndroid Build Coastguard Worker // The first entry on the stack is the "global" frame before we enter main, so offset our index
116*c8dee2aaSAndroid Build Coastguard Worker // by one to account for it.
117*c8dee2aaSAndroid Build Coastguard Worker ++stackFrameIndex;
118*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(stackFrameIndex > 0);
119*c8dee2aaSAndroid Build Coastguard Worker SkASSERT((size_t)stackFrameIndex < fStack.size());
120*c8dee2aaSAndroid Build Coastguard Worker return fStack[stackFrameIndex].fLine;
121*c8dee2aaSAndroid Build Coastguard Worker }
122*c8dee2aaSAndroid Build Coastguard Worker
atBreakpoint() const123*c8dee2aaSAndroid Build Coastguard Worker bool SkSLDebugTracePlayer::atBreakpoint() const {
124*c8dee2aaSAndroid Build Coastguard Worker return fBreakpointLines.count(this->getCurrentLine());
125*c8dee2aaSAndroid Build Coastguard Worker }
126*c8dee2aaSAndroid Build Coastguard Worker
setBreakpoints(std::unordered_set<int> breakpointLines)127*c8dee2aaSAndroid Build Coastguard Worker void SkSLDebugTracePlayer::setBreakpoints(std::unordered_set<int> breakpointLines) {
128*c8dee2aaSAndroid Build Coastguard Worker fBreakpointLines = std::move(breakpointLines);
129*c8dee2aaSAndroid Build Coastguard Worker }
130*c8dee2aaSAndroid Build Coastguard Worker
addBreakpoint(int line)131*c8dee2aaSAndroid Build Coastguard Worker void SkSLDebugTracePlayer::addBreakpoint(int line) {
132*c8dee2aaSAndroid Build Coastguard Worker fBreakpointLines.insert(line);
133*c8dee2aaSAndroid Build Coastguard Worker }
134*c8dee2aaSAndroid Build Coastguard Worker
removeBreakpoint(int line)135*c8dee2aaSAndroid Build Coastguard Worker void SkSLDebugTracePlayer::removeBreakpoint(int line) {
136*c8dee2aaSAndroid Build Coastguard Worker fBreakpointLines.erase(line);
137*c8dee2aaSAndroid Build Coastguard Worker }
138*c8dee2aaSAndroid Build Coastguard Worker
getCallStack() const139*c8dee2aaSAndroid Build Coastguard Worker std::vector<int> SkSLDebugTracePlayer::getCallStack() const {
140*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fStack.empty());
141*c8dee2aaSAndroid Build Coastguard Worker std::vector<int> funcs;
142*c8dee2aaSAndroid Build Coastguard Worker funcs.reserve(fStack.size() - 1);
143*c8dee2aaSAndroid Build Coastguard Worker for (size_t index = 1; index < fStack.size(); ++index) {
144*c8dee2aaSAndroid Build Coastguard Worker funcs.push_back(fStack[index].fFunction);
145*c8dee2aaSAndroid Build Coastguard Worker }
146*c8dee2aaSAndroid Build Coastguard Worker return funcs;
147*c8dee2aaSAndroid Build Coastguard Worker }
148*c8dee2aaSAndroid Build Coastguard Worker
getStackDepth() const149*c8dee2aaSAndroid Build Coastguard Worker int SkSLDebugTracePlayer::getStackDepth() const {
150*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fStack.empty());
151*c8dee2aaSAndroid Build Coastguard Worker return fStack.size() - 1;
152*c8dee2aaSAndroid Build Coastguard Worker }
153*c8dee2aaSAndroid Build Coastguard Worker
getVariablesForDisplayMask(const SkBitSet & displayMask) const154*c8dee2aaSAndroid Build Coastguard Worker std::vector<SkSLDebugTracePlayer::VariableData> SkSLDebugTracePlayer::getVariablesForDisplayMask(
155*c8dee2aaSAndroid Build Coastguard Worker const SkBitSet& displayMask) const {
156*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(displayMask.size() == fSlots.size());
157*c8dee2aaSAndroid Build Coastguard Worker
158*c8dee2aaSAndroid Build Coastguard Worker std::vector<VariableData> vars;
159*c8dee2aaSAndroid Build Coastguard Worker displayMask.forEachSetIndex([&](int slot) {
160*c8dee2aaSAndroid Build Coastguard Worker double typedValue = fDebugTrace->interpretValueBits(slot, fSlots[slot].fValue);
161*c8dee2aaSAndroid Build Coastguard Worker vars.push_back({slot, fDirtyMask->test(slot), typedValue});
162*c8dee2aaSAndroid Build Coastguard Worker });
163*c8dee2aaSAndroid Build Coastguard Worker // Order the variable list so that the most recently-written variables are shown at the top.
164*c8dee2aaSAndroid Build Coastguard Worker std::stable_sort(vars.begin(), vars.end(), [&](const VariableData& a, const VariableData& b) {
165*c8dee2aaSAndroid Build Coastguard Worker return fSlots[a.fSlotIndex].fWriteTime > fSlots[b.fSlotIndex].fWriteTime;
166*c8dee2aaSAndroid Build Coastguard Worker });
167*c8dee2aaSAndroid Build Coastguard Worker return vars;
168*c8dee2aaSAndroid Build Coastguard Worker }
169*c8dee2aaSAndroid Build Coastguard Worker
getLocalVariables(int stackFrameIndex) const170*c8dee2aaSAndroid Build Coastguard Worker std::vector<SkSLDebugTracePlayer::VariableData> SkSLDebugTracePlayer::getLocalVariables(
171*c8dee2aaSAndroid Build Coastguard Worker int stackFrameIndex) const {
172*c8dee2aaSAndroid Build Coastguard Worker // The first entry on the stack is the "global" frame before we enter main, so offset our index
173*c8dee2aaSAndroid Build Coastguard Worker // by one to account for it.
174*c8dee2aaSAndroid Build Coastguard Worker ++stackFrameIndex;
175*c8dee2aaSAndroid Build Coastguard Worker if (stackFrameIndex <= 0 || (size_t)stackFrameIndex >= fStack.size()) {
176*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGFAILF("stack frame %d doesn't exist", stackFrameIndex - 1);
177*c8dee2aaSAndroid Build Coastguard Worker return {};
178*c8dee2aaSAndroid Build Coastguard Worker }
179*c8dee2aaSAndroid Build Coastguard Worker return this->getVariablesForDisplayMask(fStack[stackFrameIndex].fDisplayMask);
180*c8dee2aaSAndroid Build Coastguard Worker }
181*c8dee2aaSAndroid Build Coastguard Worker
getGlobalVariables() const182*c8dee2aaSAndroid Build Coastguard Worker std::vector<SkSLDebugTracePlayer::VariableData> SkSLDebugTracePlayer::getGlobalVariables() const {
183*c8dee2aaSAndroid Build Coastguard Worker if (fStack.empty()) {
184*c8dee2aaSAndroid Build Coastguard Worker return {};
185*c8dee2aaSAndroid Build Coastguard Worker }
186*c8dee2aaSAndroid Build Coastguard Worker return this->getVariablesForDisplayMask(fStack.front().fDisplayMask);
187*c8dee2aaSAndroid Build Coastguard Worker }
188*c8dee2aaSAndroid Build Coastguard Worker
updateVariableWriteTime(int slotIdx,size_t cursor)189*c8dee2aaSAndroid Build Coastguard Worker void SkSLDebugTracePlayer::updateVariableWriteTime(int slotIdx, size_t cursor) {
190*c8dee2aaSAndroid Build Coastguard Worker // The slotIdx could point to any slot within a variable.
191*c8dee2aaSAndroid Build Coastguard Worker // We want to update the write time on EVERY slot associated with this variable.
192*c8dee2aaSAndroid Build Coastguard Worker // The SlotInfo's groupIndex gives us enough information to find the affected range.
193*c8dee2aaSAndroid Build Coastguard Worker const SkSL::SlotDebugInfo& changedSlot = fDebugTrace->fSlotInfo[slotIdx];
194*c8dee2aaSAndroid Build Coastguard Worker slotIdx -= changedSlot.groupIndex;
195*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(slotIdx >= 0);
196*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(slotIdx < (int)fDebugTrace->fSlotInfo.size());
197*c8dee2aaSAndroid Build Coastguard Worker
198*c8dee2aaSAndroid Build Coastguard Worker for (;;) {
199*c8dee2aaSAndroid Build Coastguard Worker fSlots[slotIdx++].fWriteTime = cursor;
200*c8dee2aaSAndroid Build Coastguard Worker
201*c8dee2aaSAndroid Build Coastguard Worker // Stop if we've reached the final slot.
202*c8dee2aaSAndroid Build Coastguard Worker if (slotIdx >= (int)fDebugTrace->fSlotInfo.size()) {
203*c8dee2aaSAndroid Build Coastguard Worker break;
204*c8dee2aaSAndroid Build Coastguard Worker }
205*c8dee2aaSAndroid Build Coastguard Worker // Each separate variable-group starts with a groupIndex of 0; stop when we detect this.
206*c8dee2aaSAndroid Build Coastguard Worker if (fDebugTrace->fSlotInfo[slotIdx].groupIndex == 0) {
207*c8dee2aaSAndroid Build Coastguard Worker break;
208*c8dee2aaSAndroid Build Coastguard Worker }
209*c8dee2aaSAndroid Build Coastguard Worker }
210*c8dee2aaSAndroid Build Coastguard Worker }
211*c8dee2aaSAndroid Build Coastguard Worker
execute(size_t position)212*c8dee2aaSAndroid Build Coastguard Worker bool SkSLDebugTracePlayer::execute(size_t position) {
213*c8dee2aaSAndroid Build Coastguard Worker if (position >= fDebugTrace->fTraceInfo.size()) {
214*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGFAILF("position %zu out of range", position);
215*c8dee2aaSAndroid Build Coastguard Worker return true;
216*c8dee2aaSAndroid Build Coastguard Worker }
217*c8dee2aaSAndroid Build Coastguard Worker
218*c8dee2aaSAndroid Build Coastguard Worker const TraceInfo& trace = fDebugTrace->fTraceInfo[position];
219*c8dee2aaSAndroid Build Coastguard Worker switch (trace.op) {
220*c8dee2aaSAndroid Build Coastguard Worker case TraceInfo::Op::kLine: { // data: line number, (unused)
221*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fStack.empty());
222*c8dee2aaSAndroid Build Coastguard Worker int lineNumber = trace.data[0];
223*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(lineNumber >= 0);
224*c8dee2aaSAndroid Build Coastguard Worker SkASSERT((size_t)lineNumber < fDebugTrace->fSource.size());
225*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fLineNumbers[lineNumber] > 0);
226*c8dee2aaSAndroid Build Coastguard Worker fStack.back().fLine = lineNumber;
227*c8dee2aaSAndroid Build Coastguard Worker fLineNumbers[lineNumber] -= 1;
228*c8dee2aaSAndroid Build Coastguard Worker return true;
229*c8dee2aaSAndroid Build Coastguard Worker }
230*c8dee2aaSAndroid Build Coastguard Worker case TraceInfo::Op::kVar: { // data: slot, value
231*c8dee2aaSAndroid Build Coastguard Worker int slotIdx = trace.data[0];
232*c8dee2aaSAndroid Build Coastguard Worker int value = trace.data[1];
233*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(slotIdx >= 0);
234*c8dee2aaSAndroid Build Coastguard Worker SkASSERT((size_t)slotIdx < fDebugTrace->fSlotInfo.size());
235*c8dee2aaSAndroid Build Coastguard Worker fSlots[slotIdx].fValue = value;
236*c8dee2aaSAndroid Build Coastguard Worker fSlots[slotIdx].fScope = std::min<>(fSlots[slotIdx].fScope, fScope);
237*c8dee2aaSAndroid Build Coastguard Worker this->updateVariableWriteTime(slotIdx, position);
238*c8dee2aaSAndroid Build Coastguard Worker if (fDebugTrace->fSlotInfo[slotIdx].fnReturnValue < 0) {
239*c8dee2aaSAndroid Build Coastguard Worker // Normal variables are associated with the current function.
240*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fStack.empty());
241*c8dee2aaSAndroid Build Coastguard Worker fStack.rbegin()[0].fDisplayMask.set(slotIdx);
242*c8dee2aaSAndroid Build Coastguard Worker } else {
243*c8dee2aaSAndroid Build Coastguard Worker // Return values are associated with the parent function (since the current function
244*c8dee2aaSAndroid Build Coastguard Worker // is exiting and we won't see them there).
245*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fStack.size() > 1);
246*c8dee2aaSAndroid Build Coastguard Worker fStack.rbegin()[1].fDisplayMask.set(slotIdx);
247*c8dee2aaSAndroid Build Coastguard Worker }
248*c8dee2aaSAndroid Build Coastguard Worker fDirtyMask->set(slotIdx);
249*c8dee2aaSAndroid Build Coastguard Worker break;
250*c8dee2aaSAndroid Build Coastguard Worker }
251*c8dee2aaSAndroid Build Coastguard Worker case TraceInfo::Op::kEnter: { // data: function index, (unused)
252*c8dee2aaSAndroid Build Coastguard Worker int fnIdx = trace.data[0];
253*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fnIdx >= 0);
254*c8dee2aaSAndroid Build Coastguard Worker SkASSERT((size_t)fnIdx < fDebugTrace->fFuncInfo.size());
255*c8dee2aaSAndroid Build Coastguard Worker fStack.push_back({/*fFunction=*/fnIdx,
256*c8dee2aaSAndroid Build Coastguard Worker /*fLine=*/-1,
257*c8dee2aaSAndroid Build Coastguard Worker /*fDisplayMask=*/SkBitSet(fDebugTrace->fSlotInfo.size())});
258*c8dee2aaSAndroid Build Coastguard Worker break;
259*c8dee2aaSAndroid Build Coastguard Worker }
260*c8dee2aaSAndroid Build Coastguard Worker case TraceInfo::Op::kExit: { // data: function index, (unused)
261*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fStack.empty());
262*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fStack.back().fFunction == trace.data[0]);
263*c8dee2aaSAndroid Build Coastguard Worker fStack.pop_back();
264*c8dee2aaSAndroid Build Coastguard Worker return true;
265*c8dee2aaSAndroid Build Coastguard Worker }
266*c8dee2aaSAndroid Build Coastguard Worker case TraceInfo::Op::kScope: { // data: scope delta, (unused)
267*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fStack.empty());
268*c8dee2aaSAndroid Build Coastguard Worker fScope += trace.data[0];
269*c8dee2aaSAndroid Build Coastguard Worker if (trace.data[0] < 0) {
270*c8dee2aaSAndroid Build Coastguard Worker // If the scope is being reduced, discard variables that are now out of scope.
271*c8dee2aaSAndroid Build Coastguard Worker for (size_t slotIdx = 0; slotIdx < fSlots.size(); ++slotIdx) {
272*c8dee2aaSAndroid Build Coastguard Worker if (fScope < fSlots[slotIdx].fScope) {
273*c8dee2aaSAndroid Build Coastguard Worker fSlots[slotIdx].fScope = INT_MAX;
274*c8dee2aaSAndroid Build Coastguard Worker fStack.back().fDisplayMask.reset(slotIdx);
275*c8dee2aaSAndroid Build Coastguard Worker }
276*c8dee2aaSAndroid Build Coastguard Worker }
277*c8dee2aaSAndroid Build Coastguard Worker }
278*c8dee2aaSAndroid Build Coastguard Worker return false;
279*c8dee2aaSAndroid Build Coastguard Worker }
280*c8dee2aaSAndroid Build Coastguard Worker }
281*c8dee2aaSAndroid Build Coastguard Worker
282*c8dee2aaSAndroid Build Coastguard Worker return false;
283*c8dee2aaSAndroid Build Coastguard Worker }
284*c8dee2aaSAndroid Build Coastguard Worker
285*c8dee2aaSAndroid Build Coastguard Worker } // namespace SkSL
286