SkSL::Program: Maintain a separate list of shared program elements

Change program iteration so that default iteration (over owned & shared
elements) only permits const access. Add a separate non-const iterator
that only visits owned elements.

Initially, nothing is being placed in the shared list. Follow-up CLs
will move builtin variable declarations, builtin functions, etc.

Bug: skia:10905
Change-Id: I9a5b11170117bad3ff6a43aab780c1189904417c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/330477
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
This commit is contained in:
Brian Osman 2020-10-28 14:14:39 -04:00 committed by Skia Commit-Bot
parent 1c49494d71
commit 133724cc02
16 changed files with 161 additions and 67 deletions

View File

@ -187,7 +187,7 @@ SkRuntimeEffect::EffectResult SkRuntimeEffect::Make(SkString sksl) {
const SkSL::Context& ctx(compiler->context());
// Go through program elements, pulling out information that we need
for (const auto& elem : program->elements()) {
for (const SkSL::ProgramElement* elem : program->elements()) {
// Variables (uniform, varying, etc.)
if (elem->is<SkSL::GlobalVarDeclaration>()) {
const SkSL::GlobalVarDeclaration& global = elem->as<SkSL::GlobalVarDeclaration>();

View File

@ -442,9 +442,8 @@ bool Analysis::IsAssignable(Expression& expr, VariableReference** assignableVar,
////////////////////////////////////////////////////////////////////////////////
// ProgramVisitor
template <typename PROG, typename EXPR, typename STMT, typename ELEM>
bool TProgramVisitor<PROG, EXPR, STMT, ELEM>::visit(PROG program) {
for (const auto& pe : program.elements()) {
bool ProgramVisitor::visit(const Program& program) {
for (const ProgramElement* pe : program.elements()) {
if (this->visitProgramElement(*pe)) {
return true;
}

View File

@ -66,8 +66,6 @@ class TProgramVisitor {
public:
virtual ~TProgramVisitor() = default;
bool visit(PROG program);
protected:
virtual bool visitExpression(EXPR expression);
virtual bool visitStatement(STMT statement);
@ -86,8 +84,14 @@ extern template class TProgramVisitor<Program&, Expression&, Statement&, Program
#pragma clang diagnostic pop
#endif
using ProgramVisitor = TProgramVisitor<const Program&, const Expression&,
const Statement&, const ProgramElement&>;
class ProgramVisitor : public TProgramVisitor<const Program&,
const Expression&,
const Statement&,
const ProgramElement&> {
public:
bool visit(const Program& program);
};
using ProgramWriter = TProgramVisitor<Program&, Expression&, Statement&, ProgramElement&>;
} // namespace SkSL

View File

@ -160,7 +160,7 @@ void ByteCodeGenerator::gatherUniforms(const Type& type, const String& name) {
}
bool ByteCodeGenerator::generateCode() {
for (const auto& e : fProgram.elements()) {
for (const ProgramElement* e : fProgram.elements()) {
switch (e->kind()) {
case ProgramElement::Kind::kFunction: {
std::unique_ptr<ByteCodeFunction> f =
@ -480,7 +480,7 @@ ByteCodeGenerator::Location ByteCodeGenerator::getLocation(const Variable& var)
case Variable::Storage::kGlobal: {
if (var.type() == *fContext.fFragmentProcessor_Type) {
int offset = 0;
for (const auto& e : fProgram.elements()) {
for (const ProgramElement* e : fProgram.elements()) {
if (e->is<GlobalVarDeclaration>()) {
const GlobalVarDeclaration& decl = e->as<GlobalVarDeclaration>();
const Variable& declVar = decl.declaration()->as<VarDeclaration>().var();
@ -508,7 +508,7 @@ ByteCodeGenerator::Location ByteCodeGenerator::getLocation(const Variable& var)
}
int offset = 0;
bool isUniform = is_uniform(var);
for (const auto& e : fProgram.elements()) {
for (const ProgramElement* e : fProgram.elements()) {
if (e->is<GlobalVarDeclaration>()) {
const GlobalVarDeclaration& decl = e->as<GlobalVarDeclaration>();
const Variable& declVar = decl.declaration()->as<VarDeclaration>().var();

View File

@ -413,7 +413,7 @@ void CPPCodeGenerator::writeFieldAccess(const FieldAccess& access) {
int CPPCodeGenerator::getChildFPIndex(const Variable& var) const {
int index = 0;
for (const auto& p : fProgram.elements()) {
for (const ProgramElement* p : fProgram.elements()) {
if (p->is<GlobalVarDeclaration>()) {
const VarDeclaration& decl =
p->as<GlobalVarDeclaration>().declaration()->as<VarDeclaration>();
@ -750,7 +750,7 @@ void CPPCodeGenerator::writeInputVars() {
}
void CPPCodeGenerator::writePrivateVars() {
for (const auto& p : fProgram.elements()) {
for (const ProgramElement* p : fProgram.elements()) {
if (p->is<GlobalVarDeclaration>()) {
const GlobalVarDeclaration& global = p->as<GlobalVarDeclaration>();
const Variable& var = global.declaration()->as<VarDeclaration>().var();
@ -788,7 +788,7 @@ void CPPCodeGenerator::writePrivateVars() {
}
void CPPCodeGenerator::writePrivateVarValues() {
for (const auto& p : fProgram.elements()) {
for (const ProgramElement* p : fProgram.elements()) {
if (p->is<GlobalVarDeclaration>()) {
const GlobalVarDeclaration& global = p->as<GlobalVarDeclaration>();
const VarDeclaration& decl = global.declaration()->as<VarDeclaration>();
@ -1016,7 +1016,7 @@ bool CPPCodeGenerator::writeEmitCode(std::vector<const Variable*>& uniforms) {
this->writef(" const %s& _outer = args.fFp.cast<%s>();\n"
" (void) _outer;\n",
fFullName.c_str(), fFullName.c_str());
for (const auto& p : fProgram.elements()) {
for (const ProgramElement* p : fProgram.elements()) {
if (p->is<GlobalVarDeclaration>()) {
const GlobalVarDeclaration& global = p->as<GlobalVarDeclaration>();
const VarDeclaration& decl = global.declaration()->as<VarDeclaration>();
@ -1045,7 +1045,7 @@ bool CPPCodeGenerator::writeEmitCode(std::vector<const Variable*>& uniforms) {
// Generate mangled names and argument lists for helper functions.
std::unordered_set<const FunctionDeclaration*> definedHelpers;
for (const auto& p : fProgram.elements()) {
for (const ProgramElement* p : fProgram.elements()) {
if (p->is<FunctionDefinition>()) {
const FunctionDeclaration* decl = &p->as<FunctionDefinition>().declaration();
definedHelpers.insert(decl);
@ -1055,7 +1055,7 @@ bool CPPCodeGenerator::writeEmitCode(std::vector<const Variable*>& uniforms) {
// Emit prototypes for defined helper functions that originally had prototypes in the FP file.
// (If a function was prototyped but never defined, we skip it, since it wasn't prepared above.)
for (const auto& p : fProgram.elements()) {
for (const ProgramElement* p : fProgram.elements()) {
if (p->is<FunctionPrototype>()) {
const FunctionDeclaration* decl = &p->as<FunctionPrototype>().declaration();
if (definedHelpers.find(decl) != definedHelpers.end()) {
@ -1152,7 +1152,7 @@ void CPPCodeGenerator::writeSetData(std::vector<const Variable*>& uniforms) {
}
if (section) {
int samplerIndex = 0;
for (const auto& p : fProgram.elements()) {
for (const ProgramElement* p : fProgram.elements()) {
if (p->is<GlobalVarDeclaration>()) {
const GlobalVarDeclaration& global = p->as<GlobalVarDeclaration>();
const VarDeclaration& decl = global.declaration()->as<VarDeclaration>();
@ -1326,7 +1326,7 @@ void CPPCodeGenerator::writeGetKey() {
this->writef("void %s::onGetGLSLProcessorKey(const GrShaderCaps& caps, "
"GrProcessorKeyBuilder* b) const {\n",
fFullName.c_str());
for (const auto& p : fProgram.elements()) {
for (const ProgramElement* p : fProgram.elements()) {
if (p->is<GlobalVarDeclaration>()) {
const GlobalVarDeclaration& global = p->as<GlobalVarDeclaration>();
const VarDeclaration& decl = global.declaration()->as<VarDeclaration>();
@ -1401,7 +1401,7 @@ void CPPCodeGenerator::writeGetKey() {
bool CPPCodeGenerator::generateCode() {
std::vector<const Variable*> uniforms;
for (const auto& p : fProgram.elements()) {
for (const ProgramElement* p : fProgram.elements()) {
if (p->is<GlobalVarDeclaration>()) {
const GlobalVarDeclaration& global = p->as<GlobalVarDeclaration>();
const VarDeclaration& decl = global.declaration()->as<VarDeclaration>();

View File

@ -292,6 +292,7 @@ LoadedModule Compiler::loadModule(Program::Kind kind,
fIRGenerator->convertProgram(kind, &settings, &standaloneCaps, baseModule,
/*isBuiltinCode=*/true, source->c_str(), source->length(),
/*externalValues=*/nullptr);
SkASSERT(ir.fSharedElements.empty());
LoadedModule module = {std::move(ir.fSymbolTable), std::move(ir.fElements)};
fIRGenerator->fCanInline = true;
if (this->fErrorCount) {
@ -1724,6 +1725,7 @@ std::unique_ptr<Program> Compiler::convertProgram(
fCaps,
fContext,
std::move(ir.fElements),
std::move(ir.fSharedElements),
std::move(ir.fModifiers),
std::move(ir.fSymbolTable),
std::move(pool),
@ -1752,7 +1754,7 @@ bool Compiler::optimize(Program& program) {
bool madeChanges = false;
// Scan and optimize based on the control-flow graph for each function.
for (const auto& element : program.elements()) {
for (const auto& element : program.ownedElements()) {
if (element->is<FunctionDefinition>()) {
madeChanges |= this->scanCFG(element->as<FunctionDefinition>(), usage);
}
@ -1764,41 +1766,54 @@ bool Compiler::optimize(Program& program) {
// Remove dead functions. We wait until after analysis so that we still report errors,
// even in unused code.
if (program.fSettings.fRemoveDeadFunctions) {
auto isDeadFunction = [&](const ProgramElement* element) {
if (!element->is<FunctionDefinition>()) {
return false;
}
const FunctionDefinition& fn = element->as<FunctionDefinition>();
if (fn.declaration().name() != "main" && usage->get(fn.declaration()) == 0) {
usage->remove(*element);
madeChanges = true;
return true;
}
return false;
};
program.fElements.erase(
std::remove_if(program.fElements.begin(),
program.fElements.end(),
std::remove_if(program.fElements.begin(), program.fElements.end(),
[&](const std::unique_ptr<ProgramElement>& element) {
if (!element->is<FunctionDefinition>()) {
return false;
}
const auto& fn = element->as<FunctionDefinition>();
bool dead = fn.declaration().name() != "main" &&
usage->get(fn.declaration()) == 0;
if (dead) {
madeChanges = true;
usage->remove(*element);
}
return dead;
return isDeadFunction(element.get());
}),
program.fElements.end());
program.fSharedElements.erase(
std::remove_if(program.fSharedElements.begin(), program.fSharedElements.end(),
isDeadFunction),
program.fSharedElements.end());
}
if (program.fKind != Program::kFragmentProcessor_Kind) {
// Remove declarations of dead global variables
auto isDeadVariable = [&](const ProgramElement* element) {
if (!element->is<GlobalVarDeclaration>()) {
return false;
}
const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
const VarDeclaration& varDecl = global.declaration()->as<VarDeclaration>();
if (usage->isDead(varDecl.var())) {
madeChanges = true;
return true;
}
return false;
};
program.fElements.erase(
std::remove_if(program.fElements.begin(), program.fElements.end(),
[&](const std::unique_ptr<ProgramElement>& element) {
if (!element->is<GlobalVarDeclaration>()) {
return false;
}
const auto& global = element->as<GlobalVarDeclaration>();
const auto& varDecl =
global.declaration()->as<VarDeclaration>();
bool dead = usage->isDead(varDecl.var());
madeChanges |= dead;
return dead;
return isDeadVariable(element.get());
}),
program.fElements.end());
program.fSharedElements.erase(
std::remove_if(program.fSharedElements.begin(), program.fSharedElements.end(),
isDeadVariable),
program.fSharedElements.end());
}
if (!madeChanges) {

View File

@ -1570,7 +1570,7 @@ bool GLSLCodeGenerator::generateCode() {
OutputStream* rawOut = fOut;
StringStream body;
fOut = &body;
for (const auto& e : fProgram.elements()) {
for (const ProgramElement* e : fProgram.elements()) {
this->writeProgramElement(*e);
}
fOut = rawOut;

View File

@ -355,7 +355,7 @@ bool HCodeGenerator::generateCode() {
"class %s : public GrFragmentProcessor {\n"
"public:\n",
fFullName.c_str());
for (const auto& p : fProgram.elements()) {
for (const ProgramElement* p : fProgram.elements()) {
if (p->is<Enum>() && !p->as<Enum>().isSharedWithCpp()) {
this->writef("%s\n", p->as<Enum>().code().c_str());
}

View File

@ -3011,7 +3011,10 @@ IRGenerator::IRBundle IRGenerator::convertProgram(
fIsBuiltinCode = isBuiltinCode;
std::vector<std::unique_ptr<ProgramElement>> elements;
std::vector<const ProgramElement*> sharedElements;
fProgramElements = &elements;
fSharedElements = &sharedElements;
fInputs.reset();
fInvocations = -1;
@ -3141,7 +3144,8 @@ IRGenerator::IRBundle IRGenerator::convertProgram(
fSettings = nullptr;
return IRBundle{std::move(elements), this->releaseModifiers(), fSymbolTable, fInputs};
return IRBundle{std::move(elements), std::move(sharedElements), this->releaseModifiers(),
fSymbolTable, fInputs};
}
} // namespace SkSL

View File

@ -100,6 +100,7 @@ public:
struct IRBundle {
std::vector<std::unique_ptr<ProgramElement>> fElements;
std::vector<const ProgramElement*> fSharedElements;
std::unique_ptr<ModifiersPool> fModifiers;
std::shared_ptr<SymbolTable> fSymbolTable;
Program::Inputs fInputs;
@ -250,6 +251,7 @@ private:
ErrorReporter& fErrors;
int fInvocations;
std::vector<std::unique_ptr<ProgramElement>>* fProgramElements = nullptr;
std::vector<const ProgramElement*>* fSharedElements = nullptr;
const Variable* fRTAdjust = nullptr;
const Variable* fRTAdjustInterfaceBlock = nullptr;
int fRTAdjustFieldIndex;

View File

@ -828,7 +828,7 @@ public:
fCandidateList = candidateList;
fSymbolTableStack.push_back(program.fSymbols.get());
for (const auto& pe : program.elements()) {
for (const std::unique_ptr<ProgramElement>& pe : program.ownedElements()) {
this->visitProgramElement(pe.get());
}

View File

@ -965,7 +965,7 @@ bool MetalCodeGenerator::writeFunctionDeclaration(const FunctionDeclaration& f)
this->write(", constant Uniforms& _uniforms [[buffer(" +
to_string(fUniformBuffer) + ")]]");
}
for (const auto& e : fProgram.elements()) {
for (const ProgramElement* e : fProgram.elements()) {
if (e->is<GlobalVarDeclaration>()) {
const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
const VarDeclaration& var = decls.declaration()->as<VarDeclaration>();
@ -1422,7 +1422,7 @@ void MetalCodeGenerator::writeHeader() {
}
void MetalCodeGenerator::writeUniformStruct() {
for (const auto& e : fProgram.elements()) {
for (const ProgramElement* e : fProgram.elements()) {
if (e->is<GlobalVarDeclaration>()) {
const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
const Variable& var = decls.declaration()->as<VarDeclaration>().var();
@ -1455,7 +1455,7 @@ void MetalCodeGenerator::writeUniformStruct() {
void MetalCodeGenerator::writeInputStruct() {
this->write("struct Inputs {\n");
for (const auto& e : fProgram.elements()) {
for (const ProgramElement* e : fProgram.elements()) {
if (e->is<GlobalVarDeclaration>()) {
const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
const Variable& var = decls.declaration()->as<VarDeclaration>().var();
@ -1488,7 +1488,7 @@ void MetalCodeGenerator::writeOutputStruct() {
} else if (fProgram.fKind == Program::kFragment_Kind) {
this->write(" float4 sk_FragColor [[color(0)]];\n");
}
for (const auto& e : fProgram.elements()) {
for (const ProgramElement* e : fProgram.elements()) {
if (e->is<GlobalVarDeclaration>()) {
const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
const Variable& var = decls.declaration()->as<VarDeclaration>().var();
@ -1522,7 +1522,7 @@ void MetalCodeGenerator::writeOutputStruct() {
void MetalCodeGenerator::writeInterfaceBlocks() {
bool wroteInterfaceBlock = false;
for (const auto& e : fProgram.elements()) {
for (const ProgramElement* e : fProgram.elements()) {
if (e->is<InterfaceBlock>()) {
this->writeInterfaceBlock(e->as<InterfaceBlock>());
wroteInterfaceBlock = true;
@ -1540,7 +1540,7 @@ void MetalCodeGenerator::visitGlobalStruct(GlobalStructVisitor* visitor) {
for (const auto& [interfaceType, interfaceName] : fInterfaceBlockNameMap) {
visitor->VisitInterfaceBlock(*interfaceType, interfaceName);
}
for (const auto& element : fProgram.elements()) {
for (const ProgramElement* element : fProgram.elements()) {
if (!element->is<GlobalVarDeclaration>()) {
continue;
}
@ -1843,7 +1843,7 @@ MetalCodeGenerator::Requirements MetalCodeGenerator::requirements(const Function
auto found = fRequirements.find(&f);
if (found == fRequirements.end()) {
fRequirements[&f] = kNo_Requirements;
for (const auto& e : fProgram.elements()) {
for (const ProgramElement* e : fProgram.elements()) {
if (e->is<FunctionDefinition>()) {
const FunctionDefinition& def = e->as<FunctionDefinition>();
if (&def.declaration() == &f) {
@ -1872,7 +1872,7 @@ bool MetalCodeGenerator::generateCode() {
this->writeGlobalStruct();
StringStream body;
fOut = &body;
for (const auto& e : fProgram.elements()) {
for (const ProgramElement* e : fProgram.elements()) {
this->writeProgramElement(*e);
}
fOut = rawOut;

View File

@ -44,7 +44,7 @@ void PipelineStageCodeGenerator::writeFunctionCall(const FunctionCall& c) {
SkASSERT(arguments[0]->is<VariableReference>());
int index = 0;
bool found = false;
for (const auto& p : fProgram.elements()) {
for (const ProgramElement* p : fProgram.elements()) {
if (p->is<GlobalVarDeclaration>()) {
const GlobalVarDeclaration& global = p->as<GlobalVarDeclaration>();
const VarDeclaration& decl = global.declaration()->as<VarDeclaration>();
@ -81,7 +81,7 @@ void PipelineStageCodeGenerator::writeFunctionCall(const FunctionCall& c) {
INHERITED::writeFunctionCall(c);
} else {
int index = 0;
for (const auto& e : fProgram.elements()) {
for (const ProgramElement* e : fProgram.elements()) {
if (e->is<FunctionDefinition>()) {
if (&e->as<FunctionDefinition>().declaration() == &function) {
break;
@ -117,7 +117,7 @@ void PipelineStageCodeGenerator::writeVariableReference(const VariableReference&
auto varIndexByFlag = [this, &ref](uint32_t flag) {
int index = 0;
bool found = false;
for (const auto& e : fProgram.elements()) {
for (const ProgramElement* e : fProgram.elements()) {
if (found) {
break;
}

View File

@ -2744,7 +2744,7 @@ SpvId SPIRVCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf, bool a
}
SpvId typeId;
if (intfModifiers.fLayout.fBuiltin == SK_IN_BUILTIN) {
for (const auto& e : fProgram.elements()) {
for (const ProgramElement* e : fProgram.elements()) {
if (e->is<ModifiersDeclaration>()) {
const Modifiers& m = e->as<ModifiersDeclaration>().modifiers();
update_sk_in_count(m, &fSkInCount);
@ -3122,7 +3122,7 @@ void SPIRVCodeGenerator::writeReturnStatement(const ReturnStatement& r, OutputSt
void SPIRVCodeGenerator::writeGeometryShaderExecutionMode(SpvId entryPoint, OutputStream& out) {
SkASSERT(fProgram.fKind == Program::kGeometry_Kind);
int invocations = 1;
for (const auto& e : fProgram.elements()) {
for (const ProgramElement* e : fProgram.elements()) {
if (e->is<ModifiersDeclaration>()) {
const Modifiers& m = e->as<ModifiersDeclaration>().modifiers();
if (m.fFlags & Modifiers::kIn_Flag) {
@ -3190,7 +3190,7 @@ void SPIRVCodeGenerator::writeInstructions(const Program& program, OutputStream&
StringStream body;
std::set<SpvId> interfaceVars;
// assign IDs to functions
for (const auto& e : program.elements()) {
for (const ProgramElement* e : program.elements()) {
switch (e->kind()) {
case ProgramElement::Kind::kFunction: {
const FunctionDefinition& f = e->as<FunctionDefinition>();
@ -3201,7 +3201,7 @@ void SPIRVCodeGenerator::writeInstructions(const Program& program, OutputStream&
break;
}
}
for (const auto& e : program.elements()) {
for (const ProgramElement* e : program.elements()) {
if (e->is<InterfaceBlock>()) {
const InterfaceBlock& intf = e->as<InterfaceBlock>();
SpvId id = this->writeInterfaceBlock(intf);
@ -3215,14 +3215,14 @@ void SPIRVCodeGenerator::writeInstructions(const Program& program, OutputStream&
}
}
}
for (const auto& e : program.elements()) {
for (const ProgramElement* e : program.elements()) {
if (e->is<GlobalVarDeclaration>()) {
this->writeGlobalVar(program.fKind,
e->as<GlobalVarDeclaration>().declaration()->as<VarDeclaration>(),
body);
}
}
for (const auto& e : program.elements()) {
for (const ProgramElement* e : program.elements()) {
if (e->is<FunctionDefinition>()) {
this->writeFunction(e->as<FunctionDefinition>(), body);
}
@ -3279,7 +3279,7 @@ void SPIRVCodeGenerator::writeInstructions(const Program& program, OutputStream&
SpvExecutionModeOriginUpperLeft,
out);
}
for (const auto& e : program.elements()) {
for (const ProgramElement* e : program.elements()) {
if (e->is<Extension>()) {
this->writeInstruction(SpvOpSourceExtension, e->as<Extension>().name().c_str(), out);
}

View File

@ -27,7 +27,7 @@ namespace SkSL {
SectionAndParameterHelper::SectionAndParameterHelper(const Program* program, ErrorReporter& errors)
: fProgram(*program) {
for (const auto& p : fProgram.elements()) {
for (const ProgramElement* p : fProgram.elements()) {
switch (p->kind()) {
case ProgramElement::Kind::kGlobalVar: {
const VarDeclaration& decl =

View File

@ -186,6 +186,7 @@ struct Program {
const ShaderCapsClass* caps,
std::shared_ptr<Context> context,
std::vector<std::unique_ptr<ProgramElement>> elements,
std::vector<const ProgramElement*> sharedElements,
std::unique_ptr<ModifiersPool> modifiers,
std::shared_ptr<SymbolTable> symbols,
std::unique_ptr<Pool> pool,
@ -199,6 +200,7 @@ struct Program {
, fPool(std::move(pool))
, fInputs(inputs)
, fElements(std::move(elements))
, fSharedElements(std::move(sharedElements))
, fModifiers(std::move(modifiers)) {
fUsage = Analysis::GetUsage(*this);
}
@ -215,7 +217,74 @@ struct Program {
fPool->detachFromThread();
}
const std::vector<std::unique_ptr<ProgramElement>>& elements() const { return fElements; }
class ElementsCollection {
public:
class iterator {
public:
const ProgramElement* operator*() {
if (fShared != fSharedEnd) {
return *fShared;
} else {
return fOwned->get();
}
}
iterator& operator++() {
if (fShared != fSharedEnd) {
++fShared;
} else {
++fOwned;
}
return *this;
}
bool operator==(const iterator& other) const {
return fOwned == other.fOwned && fShared == other.fShared;
}
bool operator!=(const iterator& other) const {
return !(*this == other);
}
private:
using Owned = std::vector<std::unique_ptr<ProgramElement>>::const_iterator;
using Shared = std::vector<const ProgramElement*>::const_iterator;
friend class ElementsCollection;
iterator(Owned owned, Owned ownedEnd, Shared shared, Shared sharedEnd)
: fOwned(owned), fOwnedEnd(ownedEnd), fShared(shared), fSharedEnd(sharedEnd) {}
Owned fOwned;
Owned fOwnedEnd;
Shared fShared;
Shared fSharedEnd;
};
iterator begin() const {
return iterator(fProgram.fElements.begin(), fProgram.fElements.end(),
fProgram.fSharedElements.begin(), fProgram.fSharedElements.end());
}
iterator end() const {
return iterator(fProgram.fElements.end(), fProgram.fElements.end(),
fProgram.fSharedElements.end(), fProgram.fSharedElements.end());
}
private:
friend struct Program;
ElementsCollection(const Program& program) : fProgram(program) {}
const Program& fProgram;
};
// Can be used to iterate over *all* elements in this Program, both owned and shared (builtin).
// The iterator's value type is 'const ProgramElement*', so it's clear that you *must not*
// modify anything (as you might be mutating shared data).
ElementsCollection elements() const { return ElementsCollection(*this); }
// Can be used to iterate over *just* the elements owned by the Program, not shared builtins.
// The iterator's value type is 'std::unique_ptr<ProgramElement>', and mutation is allowed.
const std::vector<std::unique_ptr<ProgramElement>>& ownedElements() { return fElements; }
Kind fKind;
std::unique_ptr<String> fSource;
@ -230,6 +299,7 @@ struct Program {
private:
std::vector<std::unique_ptr<ProgramElement>> fElements;
std::vector<const ProgramElement*> fSharedElements;
std::unique_ptr<ModifiersPool> fModifiers;
std::unique_ptr<ProgramUsage> fUsage;