source: src/AST/Pass.hpp@ 994030ce

Last change on this file since 994030ce was b9fe89b, checked in by Michael Brooks <mlbrooks@…>, 2 years ago

Make the symbol table's error-checking times explicit.

Previously, error checking happened on all WithSymbolTable uses. Error checking means having a symbol-table add operation potentially cause a user-visible error report. Now, this only happens on the resolver pass's symbol table, while other passes' run in an "assert no errors can happen" mode.

An "ignore errors for now" mode is implemented too, which will be used in upcoming commits, for pre-resolver passes that use the symbol table.

  • Property mode set to 100644
File size: 21.6 KB
RevLine 
[04124c4]1//
2// Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// Pass.hpp --
8//
9// Author : Thierry Delisle
[a86b2ca6]10// Created On : Thu May 09 15:37:05 2019
[04124c4]11// Last Modified By :
12// Last Modified On :
13// Update Count :
14//
15
[f47f887]16#pragma once
[04124c4]17// IWYU pragma: private, include "AST/Pass.hpp"
[f47f887]18
19#include <functional>
20#include <list>
21#include <stack>
22
[04124c4]23#include "AST/Fwd.hpp"
24#include "AST/Node.hpp"
[6d51bd7]25
26#include "AST/Attribute.hpp"
[04124c4]27#include "AST/Decl.hpp"
[6d51bd7]28#include "AST/Expr.hpp"
29#include "AST/Init.hpp"
30#include "AST/Stmt.hpp"
[8a5530c]31#include "AST/Type.hpp"
[6d51bd7]32
[04124c4]33#include "AST/Visitor.hpp"
34
[0e42794]35#include "AST/SymbolTable.hpp"
[f47f887]36
37// Private prelude header, needed for some of the magic tricks this class pulls off
[04124c4]38#include "AST/Pass.proto.hpp"
[f47f887]39
40namespace ast {
41//-------------------------------------------------------------------------------------------------
42// Templated visitor type
43// To use declare a Pass< YOUR VISITOR TYPE >
44// The visitor type should specify the previsit/postvisit for types that are desired.
45// Note: previsit/postvisit must be **public** members
46//
47// Several additional features are available through inheritance
[73f1b1c]48// | PureVisitor - makes the visitor pure, it never modifies nodes in place and always
49// clones nodes it needs to make changes to
[b2a11ba]50// | WithConstTypeSubstitution - provides polymorphic const TypeSubstitution * typeSubs for the
[e0e9a0b]51// current expression
52// | WithStmtsToAdd - provides the ability to insert statements before or after the current
53// statement by adding new statements into stmtsToAddBefore or
54// stmtsToAddAfter respectively.
55// | WithDeclsToAdd - provides the ability to insert declarations before or after the
56// current declarations by adding new DeclStmt into declsToAddBefore or
57// declsToAddAfter respectively.
58// | WithShortCircuiting - provides the ability to skip visiting child nodes; set visit_children
59// to false in pre{visit,visit} to skip visiting children
60// | WithGuards - provides the ability to save/restore data like a LIFO stack; to save,
61// call GuardValue with the variable to save, the variable will
62// automatically be restored to its previous value after the
63// corresponding postvisit/postmutate teminates.
64// | WithVisitorRef - provides an pointer to the templated visitor wrapper
65// | WithSymbolTable - provides symbol table functionality
[e6b42e7]66//
67// Other Special Members:
[d0bdb18]68// | beginScope - A method with no parameters or return value, called each time the
69// visitor enters a block.
70// | endScope - A method with no parameters or return value, called each time the
71// visitor leaves a block.
[e6b42e7]72// | result - Either a method that takes no parameters or a field. If a method (or
73// callable field) get_result calls it, otherwise the value is returned.
[f47f887]74//-------------------------------------------------------------------------------------------------
[7ff3e522]75template< typename core_t >
[f47f887]76class Pass final : public ast::Visitor {
77public:
[7ff3e522]78 using core_type = core_t;
79 using type = Pass<core_t>;
80
[04124c4]81 /// Forward any arguments to the pass constructor
82 /// Propagate 'this' if necessary
[f47f887]83 template< typename... Args >
84 Pass( Args &&... args)
[7ff3e522]85 : core( std::forward<Args>( args )... )
[f47f887]86 {
87 // After the pass is constructed, check if it wants the have a pointer to the wrapping visitor
[66a89e7]88 type * const * visitor = __pass::visitor( core, 0 );
89 if ( visitor ) {
[7ff3e522]90 *const_cast<type **>( visitor ) = this;
[f47f887]91 }
92 }
93
94 virtual ~Pass() = default;
95
[e6b42e7]96 /// Storage for the actual pass.
97 core_t core;
98
99 /// If the core defines a result, call it if possible, otherwise return it.
[66a89e7]100 inline auto get_result() -> decltype( __pass::result::get( core, '0' ) ) {
101 return __pass::result::get( core, '0' );
[e6b42e7]102 }
103
[a86b2ca6]104 /// Construct and run a pass on a translation unit.
105 template< typename... Args >
[293dc1c]106 static void run( TranslationUnit & decls, Args &&... args ) {
[7ff3e522]107 Pass<core_t> visitor( std::forward<Args>( args )... );
[a86b2ca6]108 accept_all( decls, visitor );
109 }
110
[e6b42e7]111 /// Contruct and run a pass on a pointer to extract a value.
112 template< typename node_type, typename... Args >
113 static auto read( node_type const * node, Args&&... args ) {
114 Pass<core_t> visitor( std::forward<Args>( args )... );
[ce36b55]115 auto const * temp = node->accept( visitor );
[e6b42e7]116 assert( temp == node );
117 return visitor.get_result();
118 }
119
120 // Versions of the above for older compilers.
[b9fa85b]121 template< typename... Args >
[293dc1c]122 static void run( TranslationUnit & decls ) {
[0240cd69]123 Pass<core_t> visitor;
124 accept_all( decls, visitor );
125 }
126
[b9fa85b]127 template< typename node_type, typename... Args >
128 static auto read( node_type const * node ) {
[e6b42e7]129 Pass<core_t> visitor;
[ce36b55]130 auto const * temp = node->accept( visitor );
[e6b42e7]131 assert( temp == node );
132 return visitor.get_result();
133 }
[f47f887]134
[04124c4]135 /// Visit function declarations
[23f99e1]136 const ast::DeclWithType * visit( const ast::ObjectDecl * ) override final;
137 const ast::DeclWithType * visit( const ast::FunctionDecl * ) override final;
138 const ast::Decl * visit( const ast::StructDecl * ) override final;
139 const ast::Decl * visit( const ast::UnionDecl * ) override final;
140 const ast::Decl * visit( const ast::EnumDecl * ) override final;
141 const ast::Decl * visit( const ast::TraitDecl * ) override final;
142 const ast::Decl * visit( const ast::TypeDecl * ) override final;
143 const ast::Decl * visit( const ast::TypedefDecl * ) override final;
144 const ast::AsmDecl * visit( const ast::AsmDecl * ) override final;
[2d019af]145 const ast::DirectiveDecl * visit( const ast::DirectiveDecl * ) override final;
[23f99e1]146 const ast::StaticAssertDecl * visit( const ast::StaticAssertDecl * ) override final;
[19a8c40]147 const ast::DeclWithType * visit( const ast::InlineMemberDecl * ) override final;
[23f99e1]148 const ast::CompoundStmt * visit( const ast::CompoundStmt * ) override final;
149 const ast::Stmt * visit( const ast::ExprStmt * ) override final;
150 const ast::Stmt * visit( const ast::AsmStmt * ) override final;
151 const ast::Stmt * visit( const ast::DirectiveStmt * ) override final;
152 const ast::Stmt * visit( const ast::IfStmt * ) override final;
[3b0bc16]153 const ast::Stmt * visit( const ast::WhileDoStmt * ) override final;
[23f99e1]154 const ast::Stmt * visit( const ast::ForStmt * ) override final;
155 const ast::Stmt * visit( const ast::SwitchStmt * ) override final;
[400b8be]156 const ast::CaseClause * visit( const ast::CaseClause * ) override final;
[23f99e1]157 const ast::Stmt * visit( const ast::BranchStmt * ) override final;
158 const ast::Stmt * visit( const ast::ReturnStmt * ) override final;
159 const ast::Stmt * visit( const ast::ThrowStmt * ) override final;
160 const ast::Stmt * visit( const ast::TryStmt * ) override final;
[400b8be]161 const ast::CatchClause * visit( const ast::CatchClause * ) override final;
162 const ast::FinallyClause * visit( const ast::FinallyClause * ) override final;
[37cdd97]163 const ast::Stmt * visit( const ast::SuspendStmt * ) override final;
[c86b08d]164 const ast::WhenClause * visit( const ast::WhenClause * ) override final;
[23f99e1]165 const ast::Stmt * visit( const ast::WaitForStmt * ) override final;
[f6e6a55]166 const ast::WaitForClause * visit( const ast::WaitForClause * ) override final;
[c86b08d]167 const ast::Stmt * visit( const ast::WaitUntilStmt * ) override final;
[e67991f]168 const ast::Decl * visit( const ast::WithStmt * ) override final;
[23f99e1]169 const ast::NullStmt * visit( const ast::NullStmt * ) override final;
170 const ast::Stmt * visit( const ast::DeclStmt * ) override final;
171 const ast::Stmt * visit( const ast::ImplicitCtorDtorStmt * ) override final;
[6cebfef]172 const ast::Stmt * visit( const ast::MutexStmt * ) override final;
[23f99e1]173 const ast::Expr * visit( const ast::ApplicationExpr * ) override final;
174 const ast::Expr * visit( const ast::UntypedExpr * ) override final;
175 const ast::Expr * visit( const ast::NameExpr * ) override final;
[fad1f14]176 const ast::Expr * visit( const ast::QualifiedNameExpr * ) override final;
[23f99e1]177 const ast::Expr * visit( const ast::AddressExpr * ) override final;
178 const ast::Expr * visit( const ast::LabelAddressExpr * ) override final;
179 const ast::Expr * visit( const ast::CastExpr * ) override final;
180 const ast::Expr * visit( const ast::KeywordCastExpr * ) override final;
181 const ast::Expr * visit( const ast::VirtualCastExpr * ) override final;
182 const ast::Expr * visit( const ast::UntypedMemberExpr * ) override final;
183 const ast::Expr * visit( const ast::MemberExpr * ) override final;
184 const ast::Expr * visit( const ast::VariableExpr * ) override final;
185 const ast::Expr * visit( const ast::ConstantExpr * ) override final;
186 const ast::Expr * visit( const ast::SizeofExpr * ) override final;
187 const ast::Expr * visit( const ast::AlignofExpr * ) override final;
188 const ast::Expr * visit( const ast::UntypedOffsetofExpr * ) override final;
189 const ast::Expr * visit( const ast::OffsetofExpr * ) override final;
190 const ast::Expr * visit( const ast::OffsetPackExpr * ) override final;
191 const ast::Expr * visit( const ast::LogicalExpr * ) override final;
192 const ast::Expr * visit( const ast::ConditionalExpr * ) override final;
193 const ast::Expr * visit( const ast::CommaExpr * ) override final;
194 const ast::Expr * visit( const ast::TypeExpr * ) override final;
[4ec9513]195 const ast::Expr * visit( const ast::DimensionExpr * ) override final;
[23f99e1]196 const ast::Expr * visit( const ast::AsmExpr * ) override final;
197 const ast::Expr * visit( const ast::ImplicitCopyCtorExpr * ) override final;
198 const ast::Expr * visit( const ast::ConstructorExpr * ) override final;
199 const ast::Expr * visit( const ast::CompoundLiteralExpr * ) override final;
200 const ast::Expr * visit( const ast::RangeExpr * ) override final;
201 const ast::Expr * visit( const ast::UntypedTupleExpr * ) override final;
202 const ast::Expr * visit( const ast::TupleExpr * ) override final;
203 const ast::Expr * visit( const ast::TupleIndexExpr * ) override final;
204 const ast::Expr * visit( const ast::TupleAssignExpr * ) override final;
205 const ast::Expr * visit( const ast::StmtExpr * ) override final;
206 const ast::Expr * visit( const ast::UniqueExpr * ) override final;
207 const ast::Expr * visit( const ast::UntypedInitExpr * ) override final;
208 const ast::Expr * visit( const ast::InitExpr * ) override final;
209 const ast::Expr * visit( const ast::DeletedExpr * ) override final;
210 const ast::Expr * visit( const ast::DefaultArgExpr * ) override final;
211 const ast::Expr * visit( const ast::GenericExpr * ) override final;
212 const ast::Type * visit( const ast::VoidType * ) override final;
213 const ast::Type * visit( const ast::BasicType * ) override final;
214 const ast::Type * visit( const ast::PointerType * ) override final;
215 const ast::Type * visit( const ast::ArrayType * ) override final;
216 const ast::Type * visit( const ast::ReferenceType * ) override final;
217 const ast::Type * visit( const ast::QualifiedType * ) override final;
218 const ast::Type * visit( const ast::FunctionType * ) override final;
219 const ast::Type * visit( const ast::StructInstType * ) override final;
220 const ast::Type * visit( const ast::UnionInstType * ) override final;
221 const ast::Type * visit( const ast::EnumInstType * ) override final;
222 const ast::Type * visit( const ast::TraitInstType * ) override final;
223 const ast::Type * visit( const ast::TypeInstType * ) override final;
224 const ast::Type * visit( const ast::TupleType * ) override final;
225 const ast::Type * visit( const ast::TypeofType * ) override final;
[3945abe]226 const ast::Type * visit( const ast::VTableType * ) override final;
[23f99e1]227 const ast::Type * visit( const ast::VarArgsType * ) override final;
228 const ast::Type * visit( const ast::ZeroType * ) override final;
229 const ast::Type * visit( const ast::OneType * ) override final;
230 const ast::Type * visit( const ast::GlobalScopeType * ) override final;
231 const ast::Designation * visit( const ast::Designation * ) override final;
232 const ast::Init * visit( const ast::SingleInit * ) override final;
233 const ast::Init * visit( const ast::ListInit * ) override final;
234 const ast::Init * visit( const ast::ConstructorInit * ) override final;
235 const ast::Attribute * visit( const ast::Attribute * ) override final;
236 const ast::TypeSubstitution * visit( const ast::TypeSubstitution * ) override final;
[f47f887]237
[7ff3e522]238 template<typename core_type>
239 friend void accept_all( std::list< ptr<Decl> > & decls, Pass<core_type>& visitor );
[53d55b6]240
241 bool isInFunction() const {
242 return inFunction;
243 }
244
[f47f887]245private:
246
[7ff3e522]247 bool __visit_children() { __pass::bool_ref * ptr = __pass::visit_children(core, 0); return ptr ? *ptr : true; }
[f47f887]248
249private:
250
[eb211bf]251 __pass::result1<ast::Stmt> call_accept( const ast::Stmt * );
252 __pass::result1<ast::Expr> call_accept( const ast::Expr * );
[490fb92e]253
[eb211bf]254 /// This has a `type` member that is the return type for the
255 /// generic call_accept if the generic call_accept is defined.
[6d51bd7]256 template< typename node_t >
[eb211bf]257 using generic_call_accept_result =
258 std::enable_if<
[8a5530c]259 !std::is_base_of<ast::Expr, node_t>::value &&
260 !std::is_base_of<ast::Stmt, node_t>::value
[eb211bf]261 , __pass::result1<
262 typename std::remove_pointer< typename std::result_of<
263 decltype(&node_t::accept)(node_t*, type&) >::type >::type
[f8143a6]264 >
[eb211bf]265 >;
[f8143a6]266
[eb211bf]267 template< typename node_t >
268 auto call_accept( const node_t * node )
269 -> typename generic_call_accept_result<node_t>::type;
[f8143a6]270
[eb211bf]271 // requests WithStmtsToAdd directly add to this statement, as if it is a compound.
272 __pass::result1<ast::Stmt> call_accept_as_compound(const ast::Stmt *);
[f8143a6]273
[9e23b446]274 // requests type environment to be updated (why is it implemented like this?)
275 __pass::result1<ast::Expr> call_accept_top(const ast::Expr *);
276
[f8143a6]277 template< template <class...> class container_t >
[eb211bf]278 __pass::resultNstmt<container_t> call_accept( const container_t< ptr<Stmt> > & );
[f47f887]279
[6d51bd7]280 template< template <class...> class container_t, typename node_t >
[eb211bf]281 __pass::resultN< container_t, node_t > call_accept( const container_t< ptr<node_t> > & container );
[f47f887]282
[2b59f55]283public:
[6d51bd7]284 /// Logic to call the accept and mutate the parent if needed, delegates call to accept
[f8143a6]285 template<typename node_t, typename parent_t, typename field_t>
286 void maybe_accept(const node_t * &, field_t parent_t::* field);
[6d51bd7]287
[f8143a6]288 template<typename node_t, typename parent_t, typename field_t>
289 void maybe_accept_as_compound(const node_t * &, field_t parent_t::* field);
[490fb92e]290
[9e23b446]291 template<typename node_t, typename parent_t, typename field_t>
292 void maybe_accept_top(const node_t * &, field_t parent_t::* field);
293
[f47f887]294private:
[0e42794]295 /// Internal RAII guard for symbol table features
296 struct guard_symtab {
[7ff3e522]297 guard_symtab( Pass<core_t> & pass ): pass( pass ) { __pass::symtab::enter(pass.core, 0); }
298 ~guard_symtab() { __pass::symtab::leave(pass.core, 0); }
299 Pass<core_t> & pass;
[f47f887]300 };
301
[04124c4]302 /// Internal RAII guard for scope features
303 struct guard_scope {
[7ff3e522]304 guard_scope( Pass<core_t> & pass ): pass( pass ) { __pass::scope::enter(pass.core, 0); }
305 ~guard_scope() { __pass::scope::leave(pass.core, 0); }
306 Pass<core_t> & pass;
[f47f887]307 };
[23f99e1]308
[e0e9a0b]309 /// Internal RAII guard for forall substitutions
310 struct guard_forall_subs {
[361bf01]311 guard_forall_subs( Pass<core_t> & pass, const FunctionType * type )
[7ff3e522]312 : pass( pass ), type( type ) { __pass::forall::enter(pass.core, 0, type ); }
313 ~guard_forall_subs() { __pass::forall::leave(pass.core, 0, type ); }
314 Pass<core_t> & pass;
[361bf01]315 const FunctionType * type;
[e0e9a0b]316 };
317
[23f99e1]318private:
319 bool inFunction = false;
[c6c682cf]320 bool atFunctionTop = false;
[f47f887]321};
322
[d8938622]323/// Apply a pass to an entire translation unit
[7ff3e522]324template<typename core_t>
325void accept_all( std::list< ast::ptr<ast::Decl> > &, ast::Pass<core_t> & visitor );
[f47f887]326
[293dc1c]327template<typename core_t>
328void accept_all( ast::TranslationUnit &, ast::Pass<core_t> & visitor );
329
[04124c4]330//-------------------------------------------------------------------------------------------------
[f47f887]331// PASS ACCESSORIES
[04124c4]332//-------------------------------------------------------------------------------------------------
[f47f887]333
[73f1b1c]334/// If used the visitor will always clone nodes.
[d3aa64f1]335struct PureVisitor {};
336
[e9e9f56]337/// Keep track of the nearest parent node's location field.
[a9762dc]338struct WithCodeLocation {
339 const CodeLocation * location = nullptr;
340};
341
[b2a11ba]342/// Keep track of the polymorphic const TypeSubstitution * typeSubs for the current expression.
[04124c4]343struct WithConstTypeSubstitution {
[b2a11ba]344 const TypeSubstitution * typeSubs = nullptr;
[04124c4]345};
346
347/// Used if visitor requires added statements before or after the current node.
348/// The Pass template handles what *before* and *after* means automatically
[6d51bd7]349template< template<class...> class container_t = std::list >
[f47f887]350struct WithStmtsToAdd {
[04124c4]351 container_t< ptr<Stmt> > stmtsToAddBefore;
352 container_t< ptr<Stmt> > stmtsToAddAfter;
[f47f887]353};
354
[04124c4]355/// Used if visitor requires added declarations before or after the current node.
356/// The Pass template handles what *before* and *after* means automatically
[6d51bd7]357template< template<class...> class container_t = std::list >
[f47f887]358struct WithDeclsToAdd {
[04124c4]359 container_t< ptr<Decl> > declsToAddBefore;
360 container_t< ptr<Decl> > declsToAddAfter;
[f47f887]361};
362
[04124c4]363/// Use if visitation should stop at certain levels
364/// set visit_children false of all child nodes should be ignored
[f47f887]365struct WithShortCircuiting {
366 __pass::bool_ref visit_children;
367};
368
[04124c4]369/// Used to restore values/functions/etc. when the Pass finishes visiting this node
370class WithGuards {
[e16e27e1]371 __pass::at_cleanup_t at_cleanup = [](__pass::cleanup_func_t, void*) {
372 std::cerr << "No cleanup function was set" << std::endl;
373 abort();
374 };
[f47f887]375
[7ff3e522]376 template< typename core_t>
377 friend auto __pass::at_cleanup( core_t & core, int ) -> decltype( &core.at_cleanup );
[04124c4]378public:
[e16e27e1]379
[04124c4]380 /// When this node is finished being visited, restore the value of a variable
[148ba7d]381 /// You may assign to the return value to set the new value in the same statement.
[04124c4]382 template< typename T >
[148ba7d]383 T& GuardValue( T& val ) {
[04124c4]384 at_cleanup( [ val ]( void * newVal ) {
385 * static_cast< T * >( newVal ) = val;
386 }, static_cast< void * >( & val ) );
[148ba7d]387 return val;
[04124c4]388 }
[f47f887]389
[04124c4]390 /// On the object, all beginScope now and endScope when the current node is finished being visited
391 template< typename T >
392 void GuardScope( T& val ) {
393 val.beginScope();
394 at_cleanup( []( void * val ) {
395 static_cast< T * >( val )->endScope();
396 }, static_cast< void * >( & val ) );
397 }
[f47f887]398
[04124c4]399 /// When this node is finished being visited, call a function
400 template< typename Func >
401 void GuardAction( Func func ) {
402 at_cleanup( [func](void *) { func(); }, nullptr );
403 }
404};
[f47f887]405
[04124c4]406/// Used to get a pointer to the pass with its wrapped type
[7ff3e522]407template<typename core_t>
[04124c4]408struct WithVisitorRef {
[7ff3e522]409 Pass<core_t> * const visitor = nullptr;
[f47f887]410
[53d55b6]411 bool isInFunction() const {
412 return visitor->isInFunction();
[cb25fc9]413 }
414};
415
[b9fe89b]416/// Use when the templated visitor should update the symbol table,
417/// that is, when your pass core needs to query the symbol table.
418/// Expected setups:
419/// - For master passes that kick off at the compilation unit
420/// - before resolver: extend WithSymbolTableX<IgnoreErrors>
421/// - after resolver: extend WithSymbolTable and use defaults
422/// - (FYI, for completeness, the resolver's main pass uses ValidateOnAdd when it kicks off)
423/// - For helper passes that kick off at arbitrary points in the AST:
424/// - take an existing symbol table as a parameter, extend WithSymbolTable,
425/// and construct with WithSymbolTable(const SymbolTable &)
[0e42794]426struct WithSymbolTable {
[b9fe89b]427 WithSymbolTable(const ast::SymbolTable & from) : symtab(from) {}
428 WithSymbolTable(ast::SymbolTable::ErrorDetection errorMode = ast::SymbolTable::ErrorDetection::AssertClean) : symtab(errorMode) {}
429 ast::SymbolTable symtab;
430};
431template <ast::SymbolTable::ErrorDetection errorMode>
432struct WithSymbolTableX : WithSymbolTable {
433 WithSymbolTableX() : WithSymbolTable(errorMode) {}
[f47f887]434};
[e0e9a0b]435
[c600df1]436/// Used to get a pointer to the wrapping TranslationUnit.
437struct WithConstTranslationUnit {
438 const TranslationUnit * translationUnit = nullptr;
439
440 const TranslationUnit & transUnit() const {
441 assertf( translationUnit, "WithConstTranslationUnit not set-up." );
442 return *translationUnit;
443 }
444};
445
[6d51bd7]446}
447
448#include "Common/Stats.h"
449
[172d9342]450namespace ast {
[6d51bd7]451extern struct PassVisitorStats {
452 size_t depth = 0;
[c408483]453 Stats::Counters::MaxCounter<double> * max;
454 Stats::Counters::AverageCounter<double> * avg;
[6d51bd7]455} pass_visitor_stats;
[172d9342]456}
[6d51bd7]457
[be8518f]458#include "AST/Pass.impl.hpp"
Note: See TracBrowser for help on using the repository browser.