<commit_before>/*
    Copyright 2011 Joe Hermaszewski. All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:

    1. Redistributions of source code must retain the above copyright notice,
    this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.

    THIS SOFTWARE IS PROVIDED BY JOE HERMASZEWSKI "AS IS" AND ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
    EVENT SHALL JOE HERMASZEWSKI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    The views and conclusions contained in the software and documentation are
    those of the authors and should not be interpreted as representing official
    policies, either expressed or implied, of Joe Hermaszewski.
*/

#include "declaration.hpp"

#include <algorithm>
#include <cassert>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include <compiler/casting.hpp>
#include <compiler/parser.hpp>
#include <compiler/sema_analyzer.hpp>
#include <compiler/terminal_types.hpp>
#include <compiler/tokens/declaration_specifier.hpp>
#include <compiler/tokens/declarator.hpp>
#include <compiler/tokens/definition.hpp>
#include <compiler/tokens/token.hpp>
#include <compiler/tokens/statements/compound_statement.hpp>
#include <compiler/type_properties.hpp>
#include <joelang/state_assignment.hpp>
#include <joelang/technique.hpp>

namespace JoeLang
{
namespace Compiler
{

//------------------------------------------------------------------------------
// DeclarationBase
//------------------------------------------------------------------------------

DeclarationBase::DeclarationBase( TokenTy sub_class_id )
    :Token( sub_class_id )
{
}

DeclarationBase::~DeclarationBase()
{
}

bool DeclarationBase::Parse( Parser& parser,
                             std::unique_ptr<DeclarationBase>& token )
{
    // Try and parse any of the top level declarations
    std::unique_ptr<Token> t;
    if( !parser.ExpectAnyOf< TechniqueDeclaration,
                             PassDeclaration,
                             VariableListOrFunctionDefinition,
                             EmptyDeclaration>( t ) )
        return false;

    assert( isa<DeclarationBase>( t ) &&
            "DeclarationBase parsed a non-declaration" );
    // Cast it back to a DeclarationBase because ExpectAnyOf only returns Token*
    token.reset( static_cast<DeclarationBase*>( t.release() ) );
    return true;
}

bool DeclarationBase::classof( const Token* d )
{
    return d->GetSubClassID() >= TokenTy::Declaration_Start &&
           d->GetSubClassID() <= TokenTy::Declaration_End;
}

bool DeclarationBase::classof( const DeclarationBase* d )
{
    // A DeclarationBase is always a DeclarationBase
    return true;
}

//------------------------------------------------------------------------------
// EmptyDeclaration
//------------------------------------------------------------------------------

EmptyDeclaration::EmptyDeclaration()
    :DeclarationBase( TokenTy::EmptyDeclaration )
{
}

EmptyDeclaration::~EmptyDeclaration()
{
}

bool EmptyDeclaration::PerformSema( SemaAnalyzer& sema )
{
    return true;
}

bool EmptyDeclaration::Parse( Parser& parser,
                              std::unique_ptr<EmptyDeclaration>& token )
{
    // Try to parse just a semicolon
    if( !parser.ExpectTerminal( TerminalType::SEMICOLON ) )
        return false;

    token.reset( new EmptyDeclaration );
    return true;
}

bool EmptyDeclaration::classof( const DeclarationBase* d )
{
    return d->GetSubClassID() == TokenTy::EmptyDeclaration;
}

bool EmptyDeclaration::classof( const EmptyDeclaration* d )
{
    return true;
}

//------------------------------------------------------------------------------
// PassDeclaration
//------------------------------------------------------------------------------

PassDeclaration::PassDeclaration( std::string name,
                                  std::unique_ptr<PassDefinition> definition )
    :DeclarationBase( TokenTy::PassDeclaration )
    ,m_Name         ( std::move(name) )
    ,m_Definition   ( std::move(definition) )
{
}

PassDeclaration::~PassDeclaration()
{
}

bool PassDeclaration::PerformSema( SemaAnalyzer& sema )
{
    // Only declare it if it's not an anonymous pass
    if( !m_Name.empty() )
        sema.DeclarePass( m_Name, std::move(m_Definition) );
    else
    {
        if( sema.InGlobalScope() )
            sema.Error( "Declaring an anonymous pass in global scope" );
        // We haven't given the pass to sema, so it must have sema performed on
        // it now
        return m_Definition->PerformSema( sema );
    }
    return true;
}

const std::string& PassDeclaration::GetName() const
{
    return m_Name;
}

bool PassDeclaration::HasDefinition() const
{
    return bool(m_Definition);
}

const std::unique_ptr<PassDefinition>& PassDeclaration::GetDefinition() const
{
    return m_Definition;
}

std::unique_ptr<PassDefinition> PassDeclaration::TakeDefinition()
{
    return std::move(m_Definition);
}

bool PassDeclaration::Parse( Parser& parser,
                             std::unique_ptr<PassDeclaration>& token )
{
    // Needs to start with 'pass'
    if( !parser.ExpectTerminal( TerminalType::PASS ) )
        return false;

    // Allow anonymous passes
    std::string name;
    parser.ExpectTerminal( TerminalType::IDENTIFIER, name );

    // If we have a semicolon here, this pass doesn't have a definition
    if( parser.ExpectTerminal( TerminalType::SEMICOLON ) )
    {
        token.reset( new PassDeclaration( name, nullptr ) );
        return true;
    }
    // We may have moved the lexer on trying to parse the semicolon
    CHECK_PARSER;

    // This declaration must have a definition
    std::unique_ptr< PassDefinition > definition;
    if( !parser.Expect<PassDefinition>( definition ) )
        return false;

    token.reset( new PassDeclaration( std::move(name),
                                      std::move(definition) ) );
    return true;
}

bool PassDeclaration::classof( const DeclarationBase* d )
{
    return d->GetSubClassID() == TokenTy::PassDeclaration;
}

bool PassDeclaration::classof( const PassDeclaration* d )
{
    return true;
}

//------------------------------------------------------------------------------
// TechniqueDeclaration
//------------------------------------------------------------------------------

TechniqueDeclaration::TechniqueDeclaration( std::string name,
                                            PassDeclarationVector passes )

    :DeclarationBase( TokenTy::TechniqueDeclaration )
    ,m_Name( std::move(name) )
    ,m_Passes( std::move(passes) )
{
#ifndef NDEBUG
    for( const auto& p : m_Passes )
        assert( p && "TechniqueDeclaration given a null pass" );
#endif
}

TechniqueDeclaration::~TechniqueDeclaration()
{
}

bool TechniqueDeclaration::PerformSema( SemaAnalyzer& sema )
{
    sema.DeclareTechnique( m_Name );
    SemaAnalyzer::ScopeHolder scope( sema );
    scope.Enter();
    bool good = true;
    for( auto& p : m_Passes )
        good &= p->PerformSema( sema );
    scope.Leave();
    return good;
}

const std::string& TechniqueDeclaration::GetName() const
{
    return m_Name;
}

Technique TechniqueDeclaration::GenerateTechnique(
                                                 CodeGenerator& code_gen ) const
{
    std::vector<Pass> passes;
    for( const auto& p : m_Passes )
        passes.push_back( p->GeneratePass( code_gen ) );
    return Technique( m_Name, std::move(passes) );
}

bool TechniqueDeclaration::Parse( Parser& parser,
                                  std::unique_ptr<TechniqueDeclaration>& token )
{
    // Has to start with 'technique'
    if( !parser.ExpectTerminal( TerminalType::TECHNIQUE ) )
        return false;

    // Parse the technique's name into name
    // Don't worry if there isn't a name, techniques can be anonymous
    std::string name;
    parser.ExpectTerminal( TerminalType::IDENTIFIER, name );

    // Technique Declarations always have a definition

    // Start with an open brace
    if( !parser.ExpectTerminal( TerminalType::OPEN_BRACE ) )
        return false;

    // Try and parse the pass declarations
    PassDeclarationVector pass_declarations;
    parser.ExpectSequenceOf<PassDeclarationOrIdentifier>( pass_declarations );
    CHECK_PARSER;

    // End with a close brace
    if( !parser.ExpectTerminal( TerminalType::CLOSE_BRACE ) )
        return false;

    token.reset( new TechniqueDeclaration( std::move(name),
                                           std::move(pass_declarations) ) );
    return true;
}

bool TechniqueDeclaration::classof( const DeclarationBase* d )
{
    return d->GetSubClassID() == TokenTy::TechniqueDeclaration;
}

bool TechniqueDeclaration::classof( const TechniqueDeclaration* d )
{
    return true;
}

//------------------------------------------------------------------------------
// VariableListOrFunctionDefinition
//------------------------------------------------------------------------------

VariableListOrFunctionDefinition::VariableListOrFunctionDefinition(
                                                    TokenTy sub_class_id )
    :DeclarationBase( sub_class_id )
{
}

VariableListOrFunctionDefinition::~VariableListOrFunctionDefinition()
{
}

bool VariableListOrFunctionDefinition::Parse(
                      Parser& parser,
                      std::unique_ptr<VariableListOrFunctionDefinition>& token )
{
    DeclSpecsVector decl_specs;

    // We need at least one declaration specifier
    if( !parser.ExpectSequenceOf<DeclarationSpecifier>( decl_specs ) )
        return false;

    // Try and parse some declarators
    DeclaratorVector declarators;
    if( !parser.ExpectListOf<InitDeclarator, TerminalType::COMMA>(
                                                                 declarators ) )
        return false;

    if( declarators.empty() )
    {
        parser.Error( "Declaration without a declarator" );
        return false;
    }

    CompoundStatement_up function_body;

    if( declarators.size() == 1 &&
        declarators[0]->IsFunctionDeclarator() &&
        parser.Expect<CompoundStatement>( function_body ) )
    {
        // If there was only one declarator and it's a function declarator and
        // there's a compound statement then we have a function definiton;
        token.reset( new FunctionDefinition( std::move(decl_specs),
                                             declarators[0]->TakeDeclarator(),
                                             std::move(function_body) ) );
        return true;
    }
    CHECK_PARSER;

    // variable declarations must end in a semicolon
    if( !parser.ExpectTerminal( TerminalType::SEMICOLON ) )
        return false;

    token.reset( new VariableDeclarationList( std::move(decl_specs),
                                              std::move(declarators) ) );
    return true;
}

//------------------------------------------------------------------------------
// VariableDeclarationList
//------------------------------------------------------------------------------

VariableDeclarationList::VariableDeclarationList( DeclSpecsVector decl_specs,
                                                  DeclaratorVector declarators)
    :VariableListOrFunctionDefinition( TokenTy::VariableDeclarationList )
    ,m_DeclSpecs( std::move(decl_specs) )
    ,m_Declarators( std::move(declarators) )
{
    assert( !m_DeclSpecs.empty() &&
            "VariableDeclarationList given no declaration specifiers" );
}

VariableDeclarationList::~VariableDeclarationList()
{
}

bool VariableDeclarationList::PerformSema( SemaAnalyzer& sema )
{
    DeclSpecs decl_specs;

    //
    // If the decl specs were no good we can't continue
    //
    if( !decl_specs.AnalyzeDeclSpecs( m_DeclSpecs, sema ) )
        return false;

    if( sema.InGlobalScope() &&
        !decl_specs.IsStatic() &&
        !decl_specs.IsVarying() )
        // Globals are uniform by default
        decl_specs.SetIsUniform( true );

    //
    // Check that
    //

    for( const InitDeclarator_up& declarator : m_Declarators )
        declarator->PerformSema( sema, decl_specs );
    return true;
}

//------------------------------------------------------------------------------
// FunctionDefinition
//------------------------------------------------------------------------------

FunctionDefinition::FunctionDefinition( DeclSpecsVector decl_specs,
                                        Declarator_up declarator,
                                        CompoundStatement_up body )
    :VariableListOrFunctionDefinition( TokenTy::FunctionDefinition )
    ,m_DeclarationSpecifiers( std::move(decl_specs) )
    ,m_Declarator( std::move(declarator) )
    ,m_Body( std::move(body) )
{
#if !defined(NDEBUG)
    assert( !m_DeclarationSpecifiers.empty() &&
            "FunctionDefinition given no decl specs" );
    for( const auto& d : m_DeclarationSpecifiers )
        assert( d && "FunctionDefinition given a null declaration specifier" );
    assert( m_Declarator && "FunctionDefinition given a null declarator" );
    assert( m_Declarator->IsFunctionDeclarator() &&
            "FunctionDefinition given a non-function declarator" );
    assert( m_Body && "FunctionDefinition given a null body" );
#endif
}

FunctionDefinition::~FunctionDefinition()
{
}

bool FunctionDefinition::PerformSema( SemaAnalyzer& sema )
{
    DeclSpecs decl_specs;
    decl_specs.AnalyzeDeclSpecs( m_DeclarationSpecifiers, sema );

    // This will register the function with sema and verify that it's all ok
    m_Declarator->PerformSema( sema, decl_specs );

    SemaAnalyzer::ScopeHolder scope( sema );
    scope.Enter();

    // Register the variables with sema
    m_Declarator->DeclareFunctionParameters( sema );

    // Pass the return type to sema for generating the return statements
    m_Body->PerformSemaAsFunction( sema,
                                   CompleteType(
                                            decl_specs.GetType(),
                                            m_Declarator->GetArrayExtents() ) );
    scope.Leave();

    sema.DefineFunction( m_Declarator->GetIdentifier(),
                         m_Declarator->GetFunctionParameters(),
                         std::move(m_Body) );
    return true;
}

} // namespace Compiler
} // namespace JoeLang
<commit_msg>[+] Const variables are no longer uniform by default<commit_after>/*
    Copyright 2011 Joe Hermaszewski. All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:

    1. Redistributions of source code must retain the above copyright notice,
    this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.

    THIS SOFTWARE IS PROVIDED BY JOE HERMASZEWSKI "AS IS" AND ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
    EVENT SHALL JOE HERMASZEWSKI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    The views and conclusions contained in the software and documentation are
    those of the authors and should not be interpreted as representing official
    policies, either expressed or implied, of Joe Hermaszewski.
*/

#include "declaration.hpp"

#include <algorithm>
#include <cassert>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include <compiler/casting.hpp>
#include <compiler/parser.hpp>
#include <compiler/sema_analyzer.hpp>
#include <compiler/terminal_types.hpp>
#include <compiler/tokens/declaration_specifier.hpp>
#include <compiler/tokens/declarator.hpp>
#include <compiler/tokens/definition.hpp>
#include <compiler/tokens/token.hpp>
#include <compiler/tokens/statements/compound_statement.hpp>
#include <compiler/type_properties.hpp>
#include <joelang/state_assignment.hpp>
#include <joelang/technique.hpp>

namespace JoeLang
{
namespace Compiler
{

//------------------------------------------------------------------------------
// DeclarationBase
//------------------------------------------------------------------------------

DeclarationBase::DeclarationBase( TokenTy sub_class_id )
    :Token( sub_class_id )
{
}

DeclarationBase::~DeclarationBase()
{
}

bool DeclarationBase::Parse( Parser& parser,
                             std::unique_ptr<DeclarationBase>& token )
{
    // Try and parse any of the top level declarations
    std::unique_ptr<Token> t;
    if( !parser.ExpectAnyOf< TechniqueDeclaration,
                             PassDeclaration,
                             VariableListOrFunctionDefinition,
                             EmptyDeclaration>( t ) )
        return false;

    assert( isa<DeclarationBase>( t ) &&
            "DeclarationBase parsed a non-declaration" );
    // Cast it back to a DeclarationBase because ExpectAnyOf only returns Token*
    token.reset( static_cast<DeclarationBase*>( t.release() ) );
    return true;
}

bool DeclarationBase::classof( const Token* d )
{
    return d->GetSubClassID() >= TokenTy::Declaration_Start &&
           d->GetSubClassID() <= TokenTy::Declaration_End;
}

bool DeclarationBase::classof( const DeclarationBase* d )
{
    // A DeclarationBase is always a DeclarationBase
    return true;
}

//------------------------------------------------------------------------------
// EmptyDeclaration
//------------------------------------------------------------------------------

EmptyDeclaration::EmptyDeclaration()
    :DeclarationBase( TokenTy::EmptyDeclaration )
{
}

EmptyDeclaration::~EmptyDeclaration()
{
}

bool EmptyDeclaration::PerformSema( SemaAnalyzer& sema )
{
    return true;
}

bool EmptyDeclaration::Parse( Parser& parser,
                              std::unique_ptr<EmptyDeclaration>& token )
{
    // Try to parse just a semicolon
    if( !parser.ExpectTerminal( TerminalType::SEMICOLON ) )
        return false;

    token.reset( new EmptyDeclaration );
    return true;
}

bool EmptyDeclaration::classof( const DeclarationBase* d )
{
    return d->GetSubClassID() == TokenTy::EmptyDeclaration;
}

bool EmptyDeclaration::classof( const EmptyDeclaration* d )
{
    return true;
}

//------------------------------------------------------------------------------
// PassDeclaration
//------------------------------------------------------------------------------

PassDeclaration::PassDeclaration( std::string name,
                                  std::unique_ptr<PassDefinition> definition )
    :DeclarationBase( TokenTy::PassDeclaration )
    ,m_Name         ( std::move(name) )
    ,m_Definition   ( std::move(definition) )
{
}

PassDeclaration::~PassDeclaration()
{
}

bool PassDeclaration::PerformSema( SemaAnalyzer& sema )
{
    // Only declare it if it's not an anonymous pass
    if( !m_Name.empty() )
        sema.DeclarePass( m_Name, std::move(m_Definition) );
    else
    {
        if( sema.InGlobalScope() )
            sema.Error( "Declaring an anonymous pass in global scope" );
        // We haven't given the pass to sema, so it must have sema performed on
        // it now
        return m_Definition->PerformSema( sema );
    }
    return true;
}

const std::string& PassDeclaration::GetName() const
{
    return m_Name;
}

bool PassDeclaration::HasDefinition() const
{
    return bool(m_Definition);
}

const std::unique_ptr<PassDefinition>& PassDeclaration::GetDefinition() const
{
    return m_Definition;
}

std::unique_ptr<PassDefinition> PassDeclaration::TakeDefinition()
{
    return std::move(m_Definition);
}

bool PassDeclaration::Parse( Parser& parser,
                             std::unique_ptr<PassDeclaration>& token )
{
    // Needs to start with 'pass'
    if( !parser.ExpectTerminal( TerminalType::PASS ) )
        return false;

    // Allow anonymous passes
    std::string name;
    parser.ExpectTerminal( TerminalType::IDENTIFIER, name );

    // If we have a semicolon here, this pass doesn't have a definition
    if( parser.ExpectTerminal( TerminalType::SEMICOLON ) )
    {
        token.reset( new PassDeclaration( name, nullptr ) );
        return true;
    }
    // We may have moved the lexer on trying to parse the semicolon
    CHECK_PARSER;

    // This declaration must have a definition
    std::unique_ptr< PassDefinition > definition;
    if( !parser.Expect<PassDefinition>( definition ) )
        return false;

    token.reset( new PassDeclaration( std::move(name),
                                      std::move(definition) ) );
    return true;
}

bool PassDeclaration::classof( const DeclarationBase* d )
{
    return d->GetSubClassID() == TokenTy::PassDeclaration;
}

bool PassDeclaration::classof( const PassDeclaration* d )
{
    return true;
}

//------------------------------------------------------------------------------
// TechniqueDeclaration
//------------------------------------------------------------------------------

TechniqueDeclaration::TechniqueDeclaration( std::string name,
                                            PassDeclarationVector passes )

    :DeclarationBase( TokenTy::TechniqueDeclaration )
    ,m_Name( std::move(name) )
    ,m_Passes( std::move(passes) )
{
#ifndef NDEBUG
    for( const auto& p : m_Passes )
        assert( p && "TechniqueDeclaration given a null pass" );
#endif
}

TechniqueDeclaration::~TechniqueDeclaration()
{
}

bool TechniqueDeclaration::PerformSema( SemaAnalyzer& sema )
{
    sema.DeclareTechnique( m_Name );
    SemaAnalyzer::ScopeHolder scope( sema );
    scope.Enter();
    bool good = true;
    for( auto& p : m_Passes )
        good &= p->PerformSema( sema );
    scope.Leave();
    return good;
}

const std::string& TechniqueDeclaration::GetName() const
{
    return m_Name;
}

Technique TechniqueDeclaration::GenerateTechnique(
                                                 CodeGenerator& code_gen ) const
{
    std::vector<Pass> passes;
    for( const auto& p : m_Passes )
        passes.push_back( p->GeneratePass( code_gen ) );
    return Technique( m_Name, std::move(passes) );
}

bool TechniqueDeclaration::Parse( Parser& parser,
                                  std::unique_ptr<TechniqueDeclaration>& token )
{
    // Has to start with 'technique'
    if( !parser.ExpectTerminal( TerminalType::TECHNIQUE ) )
        return false;

    // Parse the technique's name into name
    // Don't worry if there isn't a name, techniques can be anonymous
    std::string name;
    parser.ExpectTerminal( TerminalType::IDENTIFIER, name );

    // Technique Declarations always have a definition

    // Start with an open brace
    if( !parser.ExpectTerminal( TerminalType::OPEN_BRACE ) )
        return false;

    // Try and parse the pass declarations
    PassDeclarationVector pass_declarations;
    parser.ExpectSequenceOf<PassDeclarationOrIdentifier>( pass_declarations );
    CHECK_PARSER;

    // End with a close brace
    if( !parser.ExpectTerminal( TerminalType::CLOSE_BRACE ) )
        return false;

    token.reset( new TechniqueDeclaration( std::move(name),
                                           std::move(pass_declarations) ) );
    return true;
}

bool TechniqueDeclaration::classof( const DeclarationBase* d )
{
    return d->GetSubClassID() == TokenTy::TechniqueDeclaration;
}

bool TechniqueDeclaration::classof( const TechniqueDeclaration* d )
{
    return true;
}

//------------------------------------------------------------------------------
// VariableListOrFunctionDefinition
//------------------------------------------------------------------------------

VariableListOrFunctionDefinition::VariableListOrFunctionDefinition(
                                                    TokenTy sub_class_id )
    :DeclarationBase( sub_class_id )
{
}

VariableListOrFunctionDefinition::~VariableListOrFunctionDefinition()
{
}

bool VariableListOrFunctionDefinition::Parse(
                      Parser& parser,
                      std::unique_ptr<VariableListOrFunctionDefinition>& token )
{
    DeclSpecsVector decl_specs;

    // We need at least one declaration specifier
    if( !parser.ExpectSequenceOf<DeclarationSpecifier>( decl_specs ) )
        return false;

    // Try and parse some declarators
    DeclaratorVector declarators;
    if( !parser.ExpectListOf<InitDeclarator, TerminalType::COMMA>(
                                                                 declarators ) )
        return false;

    if( declarators.empty() )
    {
        parser.Error( "Declaration without a declarator" );
        return false;
    }

    CompoundStatement_up function_body;

    if( declarators.size() == 1 &&
        declarators[0]->IsFunctionDeclarator() &&
        parser.Expect<CompoundStatement>( function_body ) )
    {
        // If there was only one declarator and it's a function declarator and
        // there's a compound statement then we have a function definiton;
        token.reset( new FunctionDefinition( std::move(decl_specs),
                                             declarators[0]->TakeDeclarator(),
                                             std::move(function_body) ) );
        return true;
    }
    CHECK_PARSER;

    // variable declarations must end in a semicolon
    if( !parser.ExpectTerminal( TerminalType::SEMICOLON ) )
        return false;

    token.reset( new VariableDeclarationList( std::move(decl_specs),
                                              std::move(declarators) ) );
    return true;
}

//------------------------------------------------------------------------------
// VariableDeclarationList
//------------------------------------------------------------------------------

VariableDeclarationList::VariableDeclarationList( DeclSpecsVector decl_specs,
                                                  DeclaratorVector declarators)
    :VariableListOrFunctionDefinition( TokenTy::VariableDeclarationList )
    ,m_DeclSpecs( std::move(decl_specs) )
    ,m_Declarators( std::move(declarators) )
{
    assert( !m_DeclSpecs.empty() &&
            "VariableDeclarationList given no declaration specifiers" );
}

VariableDeclarationList::~VariableDeclarationList()
{
}

bool VariableDeclarationList::PerformSema( SemaAnalyzer& sema )
{
    DeclSpecs decl_specs;

    //
    // If the decl specs were no good we can't continue
    //
    if( !decl_specs.AnalyzeDeclSpecs( m_DeclSpecs, sema ) )
        return false;

    if( sema.InGlobalScope() &&
        !decl_specs.IsStatic() &&
        !decl_specs.IsConst() &&
        !decl_specs.IsVarying() )
        // Globals are uniform by default
        decl_specs.SetIsUniform( true );

    //
    // Check that
    //

    for( const InitDeclarator_up& declarator : m_Declarators )
        declarator->PerformSema( sema, decl_specs );
    return true;
}

//------------------------------------------------------------------------------
// FunctionDefinition
//------------------------------------------------------------------------------

FunctionDefinition::FunctionDefinition( DeclSpecsVector decl_specs,
                                        Declarator_up declarator,
                                        CompoundStatement_up body )
    :VariableListOrFunctionDefinition( TokenTy::FunctionDefinition )
    ,m_DeclarationSpecifiers( std::move(decl_specs) )
    ,m_Declarator( std::move(declarator) )
    ,m_Body( std::move(body) )
{
#if !defined(NDEBUG)
    assert( !m_DeclarationSpecifiers.empty() &&
            "FunctionDefinition given no decl specs" );
    for( const auto& d : m_DeclarationSpecifiers )
        assert( d && "FunctionDefinition given a null declaration specifier" );
    assert( m_Declarator && "FunctionDefinition given a null declarator" );
    assert( m_Declarator->IsFunctionDeclarator() &&
            "FunctionDefinition given a non-function declarator" );
    assert( m_Body && "FunctionDefinition given a null body" );
#endif
}

FunctionDefinition::~FunctionDefinition()
{
}

bool FunctionDefinition::PerformSema( SemaAnalyzer& sema )
{
    DeclSpecs decl_specs;
    decl_specs.AnalyzeDeclSpecs( m_DeclarationSpecifiers, sema );

    // This will register the function with sema and verify that it's all ok
    m_Declarator->PerformSema( sema, decl_specs );

    SemaAnalyzer::ScopeHolder scope( sema );
    scope.Enter();

    // Register the variables with sema
    m_Declarator->DeclareFunctionParameters( sema );

    // Pass the return type to sema for generating the return statements
    m_Body->PerformSemaAsFunction( sema,
                                   CompleteType(
                                            decl_specs.GetType(),
                                            m_Declarator->GetArrayExtents() ) );
    scope.Leave();

    sema.DefineFunction( m_Declarator->GetIdentifier(),
                         m_Declarator->GetFunctionParameters(),
                         std::move(m_Body) );
    return true;
}

} // namespace Compiler
} // namespace JoeLang
<|endoftext|>


<commit_before>/*
Copyright (C) 2001-2011 by Serge Lamikhov-Center

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#ifndef ELFIO_DYNAMIC_HPP
#define ELFIO_DYNAMIC_HPP

namespace ELFIO {

//------------------------------------------------------------------------------
class dynamic_section_accessor
{
  public:
//------------------------------------------------------------------------------
    dynamic_section_accessor( const elfio& elf_file_, section* section_ ) :
                              elf_file( elf_file_ ),
                              dynamic_section( section_ )
    {
    }

//------------------------------------------------------------------------------
    Elf_Xword
    get_entries_num() const
    {
        Elf_Xword nRet = 0;

        if ( 0 != dynamic_section->get_entry_size() ) {
            nRet = dynamic_section->get_size() / dynamic_section->get_entry_size();
        }

        return nRet;
    }

//------------------------------------------------------------------------------
    bool
    get_entry( Elf_Xword    index,
               Elf_Xword&   tag,
               Elf_Xword&   value,
               std::string& str ) const
    {
        if ( index >= get_entries_num() ) {    // Is index valid
            return false;
        }

        if ( elf_file.get_class() == ELFCLASS32 ) {
            generic_get_entry_dyn< Elf32_Dyn >( index, tag, value );
        }
        else {
            generic_get_entry_dyn< Elf64_Dyn >( index, tag, value );
        }

        // If the tag may have a string table reference, prepare the string
        if ( tag == DT_NEEDED ||
             tag == DT_SONAME ||
             tag == DT_RPATH  ||
             tag == DT_RUNPATH ) {
            string_section_accessor strsec =
                elf_file.sections[ get_string_table_index() ];
            str = strsec.get_string( value );
        }
        else {
            str.clear();
        }

        return true;
    }

//------------------------------------------------------------------------------
    void
    add_entry( Elf_Xword& tag,
               Elf_Xword& value )
    {
        if ( elf_file.get_class() == ELFCLASS32 ) {
            generic_add_entry< Elf32_Dyn >( tag, value );
        }
        else {
            generic_add_entry< Elf64_Dyn >( tag, value );
        }
    }

//------------------------------------------------------------------------------
    void
    add_entry( Elf_Xword&   tag,
               std::string& str )
    {
        string_section_accessor strsec =
            elf_file.sections[ get_string_table_index() ];
        Elf_Xword value = strsec.add_string( str );
        add_entry( tag, value );
    }

//------------------------------------------------------------------------------
  private:
//------------------------------------------------------------------------------
    Elf_Half
    get_string_table_index() const
    {
        return (Elf_Half)dynamic_section->get_link();
    }

//------------------------------------------------------------------------------
    template< class T >
    void
    generic_get_entry_dyn( Elf_Xword  index,
                           Elf_Xword& tag,
                           Elf_Xword& value ) const
    {
        const endianess_convertor& convertor = elf_file.get_convertor();

        const T* pEntry = reinterpret_cast<const T*>(
                dynamic_section->get_data() +
                index * dynamic_section->get_entry_size() );
        tag = convertor( pEntry->d_tag );
        switch ( tag ) {
        case DT_NULL:
        case DT_SYMBOLIC:
        case DT_TEXTREL:
        case DT_BIND_NOW:
            value = 0;
            break;
        case DT_NEEDED:
        case DT_PLTRELSZ:
        case DT_RELASZ:
        case DT_RELAENT:
        case DT_STRSZ:
        case DT_SYMENT:
        case DT_SONAME:
        case DT_RPATH:
        case DT_RELSZ:
        case DT_RELENT:
        case DT_PLTREL:
        case DT_INIT_ARRAYSZ:
        case DT_FINI_ARRAYSZ:
        case DT_RUNPATH:
        case DT_FLAGS:
        case DT_PREINIT_ARRAYSZ:
            value = convertor( pEntry->d_un.d_val );
            break;
        case DT_PLTGOT:
        case DT_HASH:
        case DT_STRTAB:
        case DT_SYMTAB:
        case DT_RELA:
        case DT_INIT:
        case DT_FINI:
        case DT_REL:
        case DT_DEBUG:
        case DT_JMPREL:
        case DT_INIT_ARRAY:
        case DT_FINI_ARRAY:
        case DT_PREINIT_ARRAY:
        default:
            value = convertor( pEntry->d_un.d_ptr );
            break;
        }
    }

//------------------------------------------------------------------------------
    template< class T >
    void
    generic_add_entry( Elf_Xword tag, Elf_Xword value )
    {
        const endianess_convertor& convertor = elf_file.get_convertor();

        T entry;

        switch ( tag ) {
        case DT_NULL:
        case DT_SYMBOLIC:
        case DT_TEXTREL:
        case DT_BIND_NOW:
            value = 0;
        case DT_NEEDED:
        case DT_PLTRELSZ:
        case DT_RELASZ:
        case DT_RELAENT:
        case DT_STRSZ:
        case DT_SYMENT:
        case DT_SONAME:
        case DT_RPATH:
        case DT_RELSZ:
        case DT_RELENT:
        case DT_PLTREL:
        case DT_INIT_ARRAYSZ:
        case DT_FINI_ARRAYSZ:
        case DT_RUNPATH:
        case DT_FLAGS:
        case DT_PREINIT_ARRAYSZ:
            entry.d_un.d_val = convertor( value );
            break;
        case DT_PLTGOT:
        case DT_HASH:
        case DT_STRTAB:
        case DT_SYMTAB:
        case DT_RELA:
        case DT_INIT:
        case DT_FINI:
        case DT_REL:
        case DT_DEBUG:
        case DT_JMPREL:
        case DT_INIT_ARRAY:
        case DT_FINI_ARRAY:
        case DT_PREINIT_ARRAY:
        default:
            entry.d_un.d_ptr = convertor( value );
            break;
        }

        entry.d_tag = convertor( tag );

        dynamic_section->append_data( reinterpret_cast<char*>( &entry ), sizeof( entry ) );
    }

//------------------------------------------------------------------------------
  private:
    const elfio& elf_file;
    section*     dynamic_section;
};

} // namespace ELFIO

#endif // ELFIO_DYNAMIC_HPP
<commit_msg>Add a validation for the case when dynamic section has no data<commit_after>/*
Copyright (C) 2001-2011 by Serge Lamikhov-Center

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#ifndef ELFIO_DYNAMIC_HPP
#define ELFIO_DYNAMIC_HPP

namespace ELFIO {

//------------------------------------------------------------------------------
class dynamic_section_accessor
{
  public:
//------------------------------------------------------------------------------
    dynamic_section_accessor( const elfio& elf_file_, section* section_ ) :
                              elf_file( elf_file_ ),
                              dynamic_section( section_ )
    {
    }

//------------------------------------------------------------------------------
    Elf_Xword
    get_entries_num() const
    {
        Elf_Xword nRet = 0;

        if ( 0 != dynamic_section->get_entry_size() ) {
            nRet = dynamic_section->get_size() / dynamic_section->get_entry_size();
        }

        return nRet;
    }

//------------------------------------------------------------------------------
    bool
    get_entry( Elf_Xword    index,
               Elf_Xword&   tag,
               Elf_Xword&   value,
               std::string& str ) const
    {
        if ( index >= get_entries_num() ) {    // Is index valid
            return false;
        }

        if ( elf_file.get_class() == ELFCLASS32 ) {
            generic_get_entry_dyn< Elf32_Dyn >( index, tag, value );
        }
        else {
            generic_get_entry_dyn< Elf64_Dyn >( index, tag, value );
        }

        // If the tag may have a string table reference, prepare the string
        if ( tag == DT_NEEDED ||
             tag == DT_SONAME ||
             tag == DT_RPATH  ||
             tag == DT_RUNPATH ) {
            string_section_accessor strsec =
                elf_file.sections[ get_string_table_index() ];
            str = strsec.get_string( value );
        }
        else {
            str.clear();
        }

        return true;
    }

//------------------------------------------------------------------------------
    void
    add_entry( Elf_Xword& tag,
               Elf_Xword& value )
    {
        if ( elf_file.get_class() == ELFCLASS32 ) {
            generic_add_entry< Elf32_Dyn >( tag, value );
        }
        else {
            generic_add_entry< Elf64_Dyn >( tag, value );
        }
    }

//------------------------------------------------------------------------------
    void
    add_entry( Elf_Xword&   tag,
               std::string& str )
    {
        string_section_accessor strsec =
            elf_file.sections[ get_string_table_index() ];
        Elf_Xword value = strsec.add_string( str );
        add_entry( tag, value );
    }

//------------------------------------------------------------------------------
  private:
//------------------------------------------------------------------------------
    Elf_Half
    get_string_table_index() const
    {
        return (Elf_Half)dynamic_section->get_link();
    }

//------------------------------------------------------------------------------
    template< class T >
    void
    generic_get_entry_dyn( Elf_Xword  index,
                           Elf_Xword& tag,
                           Elf_Xword& value ) const
    {
        const endianess_convertor& convertor = elf_file.get_convertor();

        // Check unusual case when dynamic section has no data
        if( dynamic_section->get_data() == 0 ) {
            tag   = DT_NULL;
            value = 0;
            return;
        }

        const T* pEntry = reinterpret_cast<const T*>(
                dynamic_section->get_data() +
                index * dynamic_section->get_entry_size() );
        tag = convertor( pEntry->d_tag );
        switch ( tag ) {
        case DT_NULL:
        case DT_SYMBOLIC:
        case DT_TEXTREL:
        case DT_BIND_NOW:
            value = 0;
            break;
        case DT_NEEDED:
        case DT_PLTRELSZ:
        case DT_RELASZ:
        case DT_RELAENT:
        case DT_STRSZ:
        case DT_SYMENT:
        case DT_SONAME:
        case DT_RPATH:
        case DT_RELSZ:
        case DT_RELENT:
        case DT_PLTREL:
        case DT_INIT_ARRAYSZ:
        case DT_FINI_ARRAYSZ:
        case DT_RUNPATH:
        case DT_FLAGS:
        case DT_PREINIT_ARRAYSZ:
            value = convertor( pEntry->d_un.d_val );
            break;
        case DT_PLTGOT:
        case DT_HASH:
        case DT_STRTAB:
        case DT_SYMTAB:
        case DT_RELA:
        case DT_INIT:
        case DT_FINI:
        case DT_REL:
        case DT_DEBUG:
        case DT_JMPREL:
        case DT_INIT_ARRAY:
        case DT_FINI_ARRAY:
        case DT_PREINIT_ARRAY:
        default:
            value = convertor( pEntry->d_un.d_ptr );
            break;
        }
    }

//------------------------------------------------------------------------------
    template< class T >
    void
    generic_add_entry( Elf_Xword tag, Elf_Xword value )
    {
        const endianess_convertor& convertor = elf_file.get_convertor();

        T entry;

        switch ( tag ) {
        case DT_NULL:
        case DT_SYMBOLIC:
        case DT_TEXTREL:
        case DT_BIND_NOW:
            value = 0;
        case DT_NEEDED:
        case DT_PLTRELSZ:
        case DT_RELASZ:
        case DT_RELAENT:
        case DT_STRSZ:
        case DT_SYMENT:
        case DT_SONAME:
        case DT_RPATH:
        case DT_RELSZ:
        case DT_RELENT:
        case DT_PLTREL:
        case DT_INIT_ARRAYSZ:
        case DT_FINI_ARRAYSZ:
        case DT_RUNPATH:
        case DT_FLAGS:
        case DT_PREINIT_ARRAYSZ:
            entry.d_un.d_val = convertor( value );
            break;
        case DT_PLTGOT:
        case DT_HASH:
        case DT_STRTAB:
        case DT_SYMTAB:
        case DT_RELA:
        case DT_INIT:
        case DT_FINI:
        case DT_REL:
        case DT_DEBUG:
        case DT_JMPREL:
        case DT_INIT_ARRAY:
        case DT_FINI_ARRAY:
        case DT_PREINIT_ARRAY:
        default:
            entry.d_un.d_ptr = convertor( value );
            break;
        }

        entry.d_tag = convertor( tag );

        dynamic_section->append_data( reinterpret_cast<char*>( &entry ), sizeof( entry ) );
    }

//------------------------------------------------------------------------------
  private:
    const elfio& elf_file;
    section*     dynamic_section;
};

} // namespace ELFIO

#endif // ELFIO_DYNAMIC_HPP
<|endoftext|>


<commit_before>//-----------------------------------
// Copyright Pierric Gimmig 2013-2017
//-----------------------------------

#include "TimerManager.h"

#include "Message.h"
#include "OrbitBase/Logging.h"
#include "OrbitLib.h"
#include "Params.h"
#include "TcpClient.h"
#include "TcpForward.h"
#include "Threading.h"

#ifdef _WIN32
#include <direct.h>
#endif

std::unique_ptr<TimerManager> GTimerManager;

//-----------------------------------------------------------------------------
TimerManager::TimerManager(bool a_IsClient)
    : m_LockFreeQueue(65534), m_IsClient(a_IsClient) {
  m_Paused = false;
  m_IsFull = false;
  m_IsRecording = false;
  m_ExitRequested = false;
  m_FlushRequested = false;
  m_NumQueuedEntries = 0;
  m_NumQueuedTimers = 0;
  m_NumQueuedMessages = 0;
  m_TimerIndex = 0;
  m_NumTimersFromPreviousSession = 0;
  m_NumFlushedTimers = 0;

  if (m_IsClient) {
    GTcpClient->Start();
    consumerThread_ = std::thread{[this]() { SendTimers(); }};
  }
}

//-----------------------------------------------------------------------------
TimerManager::~TimerManager() {}

//-----------------------------------------------------------------------------
void TimerManager::StartRecording() {
  CHECK(!m_IsClient);

  if (m_IsRecording) {
    return;
  }

  if (!consumerThread_.joinable()) {
    consumerThread_ = std::thread{[this]() { ConsumeTimers(); }};
  }

  m_IsRecording = true;
}

//-----------------------------------------------------------------------------
void TimerManager::StopRecording() {
  CHECK(!m_IsClient);
  m_IsRecording = false;
  FlushQueue();
}

//-----------------------------------------------------------------------------
void TimerManager::StartClient() {
  CHECK(m_IsClient);
  m_IsRecording = true;
}

//-----------------------------------------------------------------------------
void TimerManager::StopClient() {
  CHECK(m_IsClient);
  m_IsRecording = false;
  GTimerManager->FlushQueue();

  if (GTcpClient) {
    GTcpClient->FlushSendQueue();
  }
}

//-----------------------------------------------------------------------------
void TimerManager::FlushQueue() {
  m_FlushRequested = true;

  const size_t numTimers = 4096;
  Timer Timers[numTimers];
  m_NumFlushedTimers = 0;

  while (!m_ExitRequested) {
    size_t numDequeued = m_LockFreeQueue.try_dequeue_bulk(Timers, numTimers);

    if (numDequeued == 0) break;

    m_NumQueuedEntries -= (int)numDequeued;
    m_NumFlushedTimers += (int)numDequeued;

    if (m_IsClient) {
      int numEntries = m_NumFlushedTimers;
      GTcpClient->Send(Msg_NumFlushedEntries, numEntries);
    }
  }

  m_FlushRequested = false;
  m_ConditionVariable.signal();
}

//-----------------------------------------------------------------------------
void TimerManager::Stop() {
  m_IsRecording = false;
  m_ExitRequested = true;
  m_ConditionVariable.signal();

  if (consumerThread_.joinable()) {
    consumerThread_.join();
  }
}

//-----------------------------------------------------------------------------
void TimerManager::ConsumeTimers() {
  SetCurrentThreadName(L"OrbitConsumeTimers");
#ifdef _WIN32
  SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
#endif

  Timer Timer;

  while (!m_ExitRequested) {
    m_ConditionVariable.wait();

    while (!m_ExitRequested && !m_FlushRequested &&
           m_LockFreeQueue.try_dequeue(Timer)) {
      --m_NumQueuedEntries;
      --m_NumQueuedTimers;

      // if( Timer.m_SessionID == Message::GSessionID ) // TODO: re-enable
      // check.
      {
        for (TimerAddedCallback& Callback : m_TimerAddedCallbacks) {
          Callback(Timer);
        }
      }
      /*else
      {
          ++m_NumTimersFromPreviousSession;
      }*/
    }
  }
}

//-----------------------------------------------------------------------------
void TimerManager::SendTimers() {
  SetCurrentThreadName(L"OrbitSendTimers");

  constexpr size_t numTimers = 4096;

  Timer timers[numTimers];

  while (!m_ExitRequested) {
    Message Msg(Msg_Timer);

    // Wait for non-empty queue
    while (m_NumQueuedEntries <= 0 && !m_ExitRequested) {
      m_ConditionVariable.wait();
    }

    size_t numDequeued = m_LockFreeQueue.try_dequeue_bulk(timers, numTimers);
    m_NumQueuedEntries -= (int)numDequeued;
    m_NumQueuedTimers -= (int)numDequeued;

    GTcpClient->Send(Msg, timers, numDequeued * sizeof(Timer));

    int numEntries = m_NumQueuedEntries;
    GTcpClient->Send(Msg_NumQueuedEntries, numEntries);

    while (m_LockFreeMessageQueue.try_dequeue(Msg) && !m_ExitRequested) {
      --m_NumQueuedEntries;
      --m_NumQueuedMessages;
      GTcpClient->Send(Msg);
    }
  }
}

//-----------------------------------------------------------------------------
void TimerManager::Add(const Timer& a_Timer) {
  if (m_IsRecording) {
    m_LockFreeQueue.enqueue(a_Timer);
    m_ConditionVariable.signal();
    ++m_NumQueuedEntries;
    ++m_NumQueuedTimers;
  }
}

//-----------------------------------------------------------------------------
void TimerManager::Add(const Message& a_Message) {
  if (m_IsRecording || m_IsClient) {
    m_LockFreeMessageQueue.enqueue(a_Message);
    m_ConditionVariable.signal();
    ++m_NumQueuedEntries;
    ++m_NumQueuedMessages;
  }
}

//-----------------------------------------------------------------------------
void TimerManager::Add(const ContextSwitch& a_CS) {
  if (m_ContextSwitchAddedCallback) m_ContextSwitchAddedCallback(a_CS);
}
<commit_msg>Fix crash when capture ends on Windows.<commit_after>//-----------------------------------
// Copyright Pierric Gimmig 2013-2017
//-----------------------------------

#include "TimerManager.h"

#include "Message.h"
#include "OrbitBase/Logging.h"
#include "OrbitLib.h"
#include "Params.h"
#include "TcpClient.h"
#include "TcpForward.h"
#include "Threading.h"

#ifdef _WIN32
#include <direct.h>
#endif

std::unique_ptr<TimerManager> GTimerManager;

//-----------------------------------------------------------------------------
TimerManager::TimerManager(bool a_IsClient)
    : m_LockFreeQueue(65534), m_IsClient(a_IsClient) {
  m_Paused = false;
  m_IsFull = false;
  m_IsRecording = false;
  m_ExitRequested = false;
  m_FlushRequested = false;
  m_NumQueuedEntries = 0;
  m_NumQueuedTimers = 0;
  m_NumQueuedMessages = 0;
  m_TimerIndex = 0;
  m_NumTimersFromPreviousSession = 0;
  m_NumFlushedTimers = 0;

  if (m_IsClient) {
    GTcpClient->Start();
    consumerThread_ = std::thread{[this]() { SendTimers(); }};
  }
}

//-----------------------------------------------------------------------------
TimerManager::~TimerManager() {}

//-----------------------------------------------------------------------------
void TimerManager::StartRecording() {
  CHECK(!m_IsClient);

  if (m_IsRecording) {
    return;
  }

  if (!consumerThread_.joinable()) {
    consumerThread_ = std::thread{[this]() { ConsumeTimers(); }};
  }

  m_IsRecording = true;
}

//-----------------------------------------------------------------------------
void TimerManager::StopRecording() {
  CHECK(!m_IsClient);
  m_IsRecording = false;
  FlushQueue();
}

//-----------------------------------------------------------------------------
void TimerManager::StartClient() {
  CHECK(m_IsClient);
  m_IsRecording = true;
}

//-----------------------------------------------------------------------------
void TimerManager::StopClient() {
  if (!m_IsClient) return;
  m_IsRecording = false;
  GTimerManager->FlushQueue();

  if (GTcpClient) {
    GTcpClient->FlushSendQueue();
  }
}

//-----------------------------------------------------------------------------
void TimerManager::FlushQueue() {
  m_FlushRequested = true;

  const size_t numTimers = 4096;
  Timer Timers[numTimers];
  m_NumFlushedTimers = 0;

  while (!m_ExitRequested) {
    size_t numDequeued = m_LockFreeQueue.try_dequeue_bulk(Timers, numTimers);

    if (numDequeued == 0) break;

    m_NumQueuedEntries -= (int)numDequeued;
    m_NumFlushedTimers += (int)numDequeued;

    if (m_IsClient) {
      int numEntries = m_NumFlushedTimers;
      GTcpClient->Send(Msg_NumFlushedEntries, numEntries);
    }
  }

  m_FlushRequested = false;
  m_ConditionVariable.signal();
}

//-----------------------------------------------------------------------------
void TimerManager::Stop() {
  m_IsRecording = false;
  m_ExitRequested = true;
  m_ConditionVariable.signal();

  if (consumerThread_.joinable()) {
    consumerThread_.join();
  }
}

//-----------------------------------------------------------------------------
void TimerManager::ConsumeTimers() {
  SetCurrentThreadName(L"OrbitConsumeTimers");
#ifdef _WIN32
  SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
#endif

  Timer Timer;

  while (!m_ExitRequested) {
    m_ConditionVariable.wait();

    while (!m_ExitRequested && !m_FlushRequested &&
           m_LockFreeQueue.try_dequeue(Timer)) {
      --m_NumQueuedEntries;
      --m_NumQueuedTimers;

      // if( Timer.m_SessionID == Message::GSessionID ) // TODO: re-enable
      // check.
      {
        for (TimerAddedCallback& Callback : m_TimerAddedCallbacks) {
          Callback(Timer);
        }
      }
      /*else
      {
          ++m_NumTimersFromPreviousSession;
      }*/
    }
  }
}

//-----------------------------------------------------------------------------
void TimerManager::SendTimers() {
  SetCurrentThreadName(L"OrbitSendTimers");

  constexpr size_t numTimers = 4096;

  Timer timers[numTimers];

  while (!m_ExitRequested) {
    Message Msg(Msg_Timer);

    // Wait for non-empty queue
    while (m_NumQueuedEntries <= 0 && !m_ExitRequested) {
      m_ConditionVariable.wait();
    }

    size_t numDequeued = m_LockFreeQueue.try_dequeue_bulk(timers, numTimers);
    m_NumQueuedEntries -= (int)numDequeued;
    m_NumQueuedTimers -= (int)numDequeued;

    GTcpClient->Send(Msg, timers, numDequeued * sizeof(Timer));

    int numEntries = m_NumQueuedEntries;
    GTcpClient->Send(Msg_NumQueuedEntries, numEntries);

    while (m_LockFreeMessageQueue.try_dequeue(Msg) && !m_ExitRequested) {
      --m_NumQueuedEntries;
      --m_NumQueuedMessages;
      GTcpClient->Send(Msg);
    }
  }
}

//-----------------------------------------------------------------------------
void TimerManager::Add(const Timer& a_Timer) {
  if (m_IsRecording) {
    m_LockFreeQueue.enqueue(a_Timer);
    m_ConditionVariable.signal();
    ++m_NumQueuedEntries;
    ++m_NumQueuedTimers;
  }
}

//-----------------------------------------------------------------------------
void TimerManager::Add(const Message& a_Message) {
  if (m_IsRecording || m_IsClient) {
    m_LockFreeMessageQueue.enqueue(a_Message);
    m_ConditionVariable.signal();
    ++m_NumQueuedEntries;
    ++m_NumQueuedMessages;
  }
}

//-----------------------------------------------------------------------------
void TimerManager::Add(const ContextSwitch& a_CS) {
  if (m_ContextSwitchAddedCallback) m_ContextSwitchAddedCallback(a_CS);
}
<|endoftext|>


<commit_before>#include <platform_config.hpp>

#include <hal_config.hpp>

namespace platform {

MPU6000 imu(&SPID1, &mpu6000_spicfg);
DefaultMultirotorVehicleSystem system(&imu, &imu);

void init() {
  // Initialize platform HAL
  i2cPlatformInit();
  pwmPlatformInit();
  spiPlatformInit();
  usartPlatformInit();

  // Initialize platform devices
  imu.init();
  system.init();

  palSetPadMode(GPIOA, 6, PAL_MODE_OUTPUT_PUSHPULL);
}

msg_t HeartbeatThread::main(void) {
  while(true) {
    palSetPad(GPIOA, 6);
    sleep(MS2ST(50));
    palClearPad(GPIOA, 6);
    sleep(MS2ST(950));
  }

  return 0;
}

}
<commit_msg>Make heartbeat LED pattern for aerial_v3 fancier.<commit_after>#include <platform_config.hpp>

#include <hal_config.hpp>

namespace platform {

MPU6000 imu(&SPID1, &mpu6000_spicfg);
DefaultMultirotorVehicleSystem system(&imu, &imu);

void init() {
  // Initialize platform HAL
  i2cPlatformInit();
  pwmPlatformInit();
  spiPlatformInit();
  usartPlatformInit();

  // Initialize platform devices
  imu.init();
  system.init();

  palSetPadMode(GPIOA, 6, PAL_MODE_OUTPUT_PUSHPULL);
  palSetPadMode(GPIOA, 7, PAL_MODE_OUTPUT_PUSHPULL);
}

msg_t HeartbeatThread::main(void) {
  while(true) {
    palSetPad(GPIOA, 6);
    sleep(MS2ST(50));
    palClearPad(GPIOA, 6);
    palSetPad(GPIOA, 7);
    sleep(MS2ST(50));
    palClearPad(GPIOA, 7);
    sleep(MS2ST(900));
  }

  return 0;
}

}
<|endoftext|>


<commit_before>/* Copyright 2019 Google LLC. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "context.h"

#include "check_macros.h"
#include "detect_arm.h"
#include "detect_x86.h"
#include "have_built_path_for.h"
#include "platform.h"

namespace ruy {

void Context::SetRuntimeEnabledPaths(Path paths) {
  runtime_enabled_paths_ = paths;
}

Path Context::GetRuntimeEnabledPaths() {
  // This function should always return the same value on a given machine.
  // When runtime_enabled_paths_ has its initial value kNone, it performs
  // some platform detection to resolve it to specific Path values.

  // Fast path: already resolved.
  if (runtime_enabled_paths_ != Path::kNone) {
    return runtime_enabled_paths_;
  }

  // Need to resolve now. Start by considering all paths enabled.
  runtime_enabled_paths_ = kAllPaths;

#if RUY_PLATFORM(ARM)
  // Now selectively disable paths that aren't supported on this machine.
  if ((runtime_enabled_paths_ & Path::kNeonDotprod) != Path::kNone) {
    if (!DetectDotprod()) {
      runtime_enabled_paths_ = runtime_enabled_paths_ & ~Path::kNeonDotprod;
      // Sanity check.
      RUY_DCHECK((runtime_enabled_paths_ & Path::kNeonDotprod) == Path::kNone);
    }
  }
#endif  // RUY_PLATFORM(ARM)

#if RUY_PLATFORM(X86)
  if ((runtime_enabled_paths_ & Path::kAvx2) != Path::kNone) {
    if (!(HaveBuiltPathForAvx2() && DetectCpuAvx2())) {
      runtime_enabled_paths_ = runtime_enabled_paths_ & ~Path::kAvx2;
      // Sanity check.
      RUY_DCHECK((runtime_enabled_paths_ & Path::kAvx2) == Path::kNone);
    }
  }

  if ((runtime_enabled_paths_ & Path::kAvx512) != Path::kNone) {
    if (!(HaveBuiltPathForAvx512() && DetectCpuAvx512())) {
      runtime_enabled_paths_ = runtime_enabled_paths_ & ~Path::kAvx512;
      // Sanity check.
      RUY_DCHECK((runtime_enabled_paths_ & Path::kAvx512) == Path::kNone);
    }
  }
#endif  // RUY_PLATFORM(X86)

  // Sanity check. We can't possibly have disabled all paths, as some paths
  // are universally available (kReference, kStandardCpp).
  RUY_DCHECK(runtime_enabled_paths_ != Path::kNone);
  return runtime_enabled_paths_;
}

}  // namespace ruy
<commit_msg>Ruy: Add mechanism to mask out paths.<commit_after>/* Copyright 2019 Google LLC. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "context.h"

#include "check_macros.h"
#include "detect_arm.h"
#include "detect_x86.h"
#include "have_built_path_for.h"
#include "platform.h"

namespace ruy {

void Context::SetRuntimeEnabledPaths(Path paths) {
  runtime_enabled_paths_ = paths;
}

Path Context::GetRuntimeEnabledPaths() {
  // This function should always return the same value on a given machine.
  // When runtime_enabled_paths_ has its initial value kNone, it performs
  // some platform detection to resolve it to specific Path values.

  // Fast path: already resolved.
  if (runtime_enabled_paths_ != Path::kNone) {
    return runtime_enabled_paths_;
  }

  // Need to resolve now. Start by considering all paths enabled.
  runtime_enabled_paths_ = kAllPaths;

  // This mechanism is intended to be used for testing and benchmarking. For
  // example, one can set RUY_FORCE_DISABLE_PATHS to Path::kAvx512 in order to
  // evaluate AVX2 performance on an AVX-512 machine.
#ifdef RUY_FORCE_DISABLE_PATHS
  runtime_enabled_paths_ = runtime_enabled_paths_ & ~(RUY_FORCE_DISABLE_PATHS);
#endif

#if RUY_PLATFORM(ARM)
  // Now selectively disable paths that aren't supported on this machine.
  if ((runtime_enabled_paths_ & Path::kNeonDotprod) != Path::kNone) {
    if (!DetectDotprod()) {
      runtime_enabled_paths_ = runtime_enabled_paths_ & ~Path::kNeonDotprod;
      // Sanity check.
      RUY_DCHECK((runtime_enabled_paths_ & Path::kNeonDotprod) == Path::kNone);
    }
  }
#endif  // RUY_PLATFORM(ARM)

#if RUY_PLATFORM(X86)
  if ((runtime_enabled_paths_ & Path::kAvx2) != Path::kNone) {
    if (!(HaveBuiltPathForAvx2() && DetectCpuAvx2())) {
      runtime_enabled_paths_ = runtime_enabled_paths_ & ~Path::kAvx2;
      // Sanity check.
      RUY_DCHECK((runtime_enabled_paths_ & Path::kAvx2) == Path::kNone);
    }
  }

  if ((runtime_enabled_paths_ & Path::kAvx512) != Path::kNone) {
    if (!(HaveBuiltPathForAvx512() && DetectCpuAvx512())) {
      runtime_enabled_paths_ = runtime_enabled_paths_ & ~Path::kAvx512;
      // Sanity check.
      RUY_DCHECK((runtime_enabled_paths_ & Path::kAvx512) == Path::kNone);
    }
  }
#endif  // RUY_PLATFORM(X86)

  // Sanity check. We can't possibly have disabled all paths, as some paths
  // are universally available (kReference, kStandardCpp).
  RUY_DCHECK(runtime_enabled_paths_ != Path::kNone);
  return runtime_enabled_paths_;
}

}  // namespace ruy
<|endoftext|>


<commit_before>/*
 * Distributed under the OSI-approved Apache License, Version 2.0.  See
 * accompanying file Copyright.txt for details.
 *
 * ParaViewFidesEngine.cpp
 *
 *  Created on: Sep 21, 2022
 *      Author: Caitlin Ross <caitlin.ross@kitware.com>
 */

#include "ParaViewFidesEngine.h"

#include <adios2/engine/inline/InlineWriter.h>

#include <catalyst.hpp>

#include <iostream>
#include <sstream>

namespace fides_plugin
{

struct ParaViewFidesEngine::EngineImpl
{
    adios2::core::IO *Io;
    adios2::core::Engine *Writer;

    std::string ScriptFileName;
    std::string JSONFileName;

    int Rank;

    EngineImpl(adios2::core::ADIOS &adios)
    {
        this->Io = &adios.DeclareIO("InlinePluginIO");
        this->Io->SetEngine("inline");
        this->Writer = &Io->Open("write", adios2::Mode::Write);
    }

    void CatalystConfig()
    {
        std::cout << "\tCatalyst Library Version: " << CATALYST_VERSION << "\n";
        std::cout << "\tCatalyst ABI Version: " << CATALYST_ABI_VERSION << "\n";

        conduit_cpp::Node node;
        catalyst_about(conduit_cpp::c_node(&node));
        auto implementation = node.has_path("catalyst/implementation")
                                  ? node["catalyst/implementation"].as_string()
                                  : std::string("stub");
        std::cout << "\tImplementation: " << implementation << "\n\n";
    }

    void CatalystInit()
    {
        conduit_cpp::Node node;
        node["catalyst/scripts/script/filename"].set(this->ScriptFileName);

        // options to set up the fides reader in paraview
        std::ostringstream address;
        address << &Io;

        node["catalyst/fides/json_file"].set(this->JSONFileName);
        node["catalyst/fides/data_source_io/source"].set(std::string("source"));
        node["catalyst/fides/data_source_io/address"].set(address.str());
        node["catalyst/fides/data_source_path/source"].set(
            std::string("source"));
        node["catalyst/fides/data_source_path/path"].set(
            std::string("DataReader"));
        catalyst_initialize(conduit_cpp::c_node(&node));

        if (this->Rank == 0)
        {
            this->CatalystConfig();
        }
    }

    void CatalystExecute()
    {
        auto timestep = this->Writer->CurrentStep();
        conduit_cpp::Node node;
        node["catalyst/state/timestep"].set(timestep);
        // catalyst requires the next one, but when using Fides as the reader
        // for Catalyst, it will grab the time from the correct adios variable
        // if it is specified in the data model
        node["catalyst/state/time"].set(timestep);
        node["catalyst/channels/fides/type"].set(std::string("fides"));

        // catalyst requires the data node on a channel, but we don't actually
        // need it when using fides, so just create a dummy object to pass
        // the validation in catalyst
        conduit_cpp::Node dummy;
        dummy["dummy"].set(0);
        node["catalyst/channels/fides/data"].set(dummy);

        catalyst_execute(conduit_cpp::c_node(&node));
    }
};

ParaViewFidesEngine::ParaViewFidesEngine(adios2::core::IO &io,
                                         const std::string &name,
                                         adios2::helper::Comm comm)
: adios2::plugin::PluginEngineInterface(io, name, adios2::Mode::Write,
                                        comm.Duplicate()),
  Impl(new EngineImpl(io.m_ADIOS))
{
    // Need to define the Variables in the IO object used for the inline engine
    const auto &varMap = io.GetVariables();
    for (const auto &it : varMap)
    {
#define declare_type(T)                                                        \
    if (it.second->m_Type == adios2::helper::GetDataType<T>())                 \
    {                                                                          \
        this->Impl->Io->DefineVariable<T>(                                     \
            it.first, it.second->m_Shape, it.second->m_Start,                  \
            it.second->m_Count, it.second->IsConstantDims());                  \
        continue;                                                              \
    }
        ADIOS2_FOREACH_STDTYPE_1ARG(declare_type)
#undef declare_type
    }

    this->Impl->Rank = comm.Rank();

    const auto &scriptIt = this->m_IO.m_Parameters.find("Script");
    if (scriptIt != this->m_IO.m_Parameters.end())
    {
        this->Impl->ScriptFileName = scriptIt->second;
    }

    // TODO required for now, but support data model generation in the future
    const auto &fileIt = this->m_IO.m_Parameters.find("DataModel");
    if (fileIt == this->m_IO.m_Parameters.end())
    {
        throw std::runtime_error("couldn't find DataModel in parameters!");
    }
    this->Impl->JSONFileName = fileIt->second;

    this->Impl->CatalystInit();
}

ParaViewFidesEngine::~ParaViewFidesEngine()
{
    conduit_cpp::Node node;
    catalyst_finalize(conduit_cpp::c_node(&node));
}

adios2::StepStatus ParaViewFidesEngine::BeginStep(adios2::StepMode mode,
                                                  const float timeoutSeconds)
{
    return this->Impl->Writer->BeginStep(mode, timeoutSeconds);
}

size_t ParaViewFidesEngine::CurrentStep() const
{
    return this->Impl->Writer->CurrentStep();
}

void ParaViewFidesEngine::EndStep()
{
    this->Impl->Writer->EndStep();

    this->Impl->CatalystExecute();
}

void ParaViewFidesEngine::PerformPuts() { this->Impl->Writer->PerformPuts(); }

#define declare(T)                                                             \
    void ParaViewFidesEngine::DoPutSync(adios2::core::Variable<T> &variable,   \
                                        const T *values)                       \
    {                                                                          \
        adios2::core::Variable<T> *inlineVar =                                 \
            this->Impl->Io->InquireVariable<T>(variable.m_Name);               \
        this->Impl->Writer->Put(*inlineVar, values, adios2::Mode::Sync);       \
    }                                                                          \
    void ParaViewFidesEngine::DoPutDeferred(                                   \
        adios2::core::Variable<T> &variable, const T *values)                  \
    {                                                                          \
        adios2::core::Variable<T> *inlineVar =                                 \
            this->Impl->Io->InquireVariable<T>(variable.m_Name);               \
        this->Impl->Writer->Put(*inlineVar, values);                           \
    }
ADIOS2_FOREACH_STDTYPE_1ARG(declare)
#undef declare

void ParaViewFidesEngine::DoClose(const int transportIndex)
{
    this->Impl->Writer->Close(transportIndex);
}

} // end namespace fides_plugin

extern "C" {

fides_plugin::ParaViewFidesEngine *EngineCreate(adios2::core::IO &io,
                                                const std::string &name,
                                                const adios2::Mode mode,
                                                adios2::helper::Comm comm)
{
    (void)mode;
    return new fides_plugin::ParaViewFidesEngine(io, name, comm.Duplicate());
}

void EngineDestroy(fides_plugin::ParaViewFidesEngine *obj) { delete obj; }
}
<commit_msg>repeat setup from Init in Execute (Caitlin's fix)<commit_after>/*
 * Distributed under the OSI-approved Apache License, Version 2.0.  See
 * accompanying file Copyright.txt for details.
 *
 * ParaViewFidesEngine.cpp
 *
 *  Created on: Sep 21, 2022
 *      Author: Caitlin Ross <caitlin.ross@kitware.com>
 */

#include "ParaViewFidesEngine.h"

#include <adios2/engine/inline/InlineWriter.h>

#include <catalyst.hpp>

#include <iostream>
#include <sstream>

namespace fides_plugin
{

struct ParaViewFidesEngine::EngineImpl
{
    adios2::core::IO *Io;
    adios2::core::Engine *Writer;

    std::string ScriptFileName;
    std::string JSONFileName;

    int Rank;

    EngineImpl(adios2::core::ADIOS &adios)
    {
        this->Io = &adios.DeclareIO("InlinePluginIO");
        this->Io->SetEngine("inline");
        this->Writer = &Io->Open("write", adios2::Mode::Write);
    }

    void CatalystConfig()
    {
        std::cout << "\tCatalyst Library Version: " << CATALYST_VERSION << "\n";
        std::cout << "\tCatalyst ABI Version: " << CATALYST_ABI_VERSION << "\n";

        conduit_cpp::Node node;
        catalyst_about(conduit_cpp::c_node(&node));
        auto implementation = node.has_path("catalyst/implementation")
                                  ? node["catalyst/implementation"].as_string()
                                  : std::string("stub");
        std::cout << "\tImplementation: " << implementation << "\n\n";
    }

    void CatalystInit()
    {
        conduit_cpp::Node node;
        node["catalyst/scripts/script/filename"].set(this->ScriptFileName);

        // options to set up the fides reader in paraview
        std::ostringstream address;
        address << &Io;

        node["catalyst/fides/json_file"].set(this->JSONFileName);
        node["catalyst/fides/data_source_io/source"].set(std::string("source"));
        node["catalyst/fides/data_source_io/address"].set(address.str());
        node["catalyst/fides/data_source_path/source"].set(
            std::string("source"));
        node["catalyst/fides/data_source_path/path"].set(
            std::string("DataReader"));
        catalyst_initialize(conduit_cpp::c_node(&node));

        if (this->Rank == 0)
        {
            this->CatalystConfig();
        }
    }

    void CatalystExecute()
    {
        auto timestep = this->Writer->CurrentStep();
        conduit_cpp::Node node;
        node["catalyst/state/timestep"].set(timestep);
        // catalyst requires the next one, but when using Fides as the reader
        // for Catalyst, it will grab the time from the correct adios variable
        // if it is specified in the data model
        node["catalyst/state/time"].set(timestep);
        node["catalyst/channels/fides/type"].set(std::string("fides"));

        // options to set up the fides reader in paraview
        std::ostringstream address;
        address << &Io;

        node["catalyst/fides/json_file"].set(this->JSONFileName);
        node["catalyst/fides/data_source_io/source"].set(std::string("source"));
        node["catalyst/fides/data_source_io/address"].set(address.str());
        node["catalyst/fides/data_source_path/source"].set(
            std::string("source"));
        node["catalyst/fides/data_source_path/path"].set(
            std::string("DataReader"));

        // catalyst requires the data node on a channel, but we don't actually
        // need it when using fides, so just create a dummy object to pass
        // the validation in catalyst
        conduit_cpp::Node dummy;
        dummy["dummy"].set(0);
        node["catalyst/channels/fides/data"].set(dummy);

        catalyst_execute(conduit_cpp::c_node(&node));
    }
};

ParaViewFidesEngine::ParaViewFidesEngine(adios2::core::IO &io,
                                         const std::string &name,
                                         adios2::helper::Comm comm)
: adios2::plugin::PluginEngineInterface(io, name, adios2::Mode::Write,
                                        comm.Duplicate()),
  Impl(new EngineImpl(io.m_ADIOS))
{
    // Need to define the Variables in the IO object used for the inline engine
    const auto &varMap = io.GetVariables();
    for (const auto &it : varMap)
    {
#define declare_type(T)                                                        \
    if (it.second->m_Type == adios2::helper::GetDataType<T>())                 \
    {                                                                          \
        this->Impl->Io->DefineVariable<T>(                                     \
            it.first, it.second->m_Shape, it.second->m_Start,                  \
            it.second->m_Count, it.second->IsConstantDims());                  \
        continue;                                                              \
    }
        ADIOS2_FOREACH_STDTYPE_1ARG(declare_type)
#undef declare_type
    }

    this->Impl->Rank = comm.Rank();

    const auto &scriptIt = this->m_IO.m_Parameters.find("Script");
    if (scriptIt != this->m_IO.m_Parameters.end())
    {
        this->Impl->ScriptFileName = scriptIt->second;
    }

    // TODO required for now, but support data model generation in the future
    const auto &fileIt = this->m_IO.m_Parameters.find("DataModel");
    if (fileIt == this->m_IO.m_Parameters.end())
    {
        throw std::runtime_error("couldn't find DataModel in parameters!");
    }
    this->Impl->JSONFileName = fileIt->second;

    this->Impl->CatalystInit();
}

ParaViewFidesEngine::~ParaViewFidesEngine()
{
    conduit_cpp::Node node;
    catalyst_finalize(conduit_cpp::c_node(&node));
}

adios2::StepStatus ParaViewFidesEngine::BeginStep(adios2::StepMode mode,
                                                  const float timeoutSeconds)
{
    return this->Impl->Writer->BeginStep(mode, timeoutSeconds);
}

size_t ParaViewFidesEngine::CurrentStep() const
{
    return this->Impl->Writer->CurrentStep();
}

void ParaViewFidesEngine::EndStep()
{
    this->Impl->Writer->EndStep();

    this->Impl->CatalystExecute();
}

void ParaViewFidesEngine::PerformPuts() { this->Impl->Writer->PerformPuts(); }

#define declare(T)                                                             \
    void ParaViewFidesEngine::DoPutSync(adios2::core::Variable<T> &variable,   \
                                        const T *values)                       \
    {                                                                          \
        adios2::core::Variable<T> *inlineVar =                                 \
            this->Impl->Io->InquireVariable<T>(variable.m_Name);               \
        this->Impl->Writer->Put(*inlineVar, values, adios2::Mode::Sync);       \
    }                                                                          \
    void ParaViewFidesEngine::DoPutDeferred(                                   \
        adios2::core::Variable<T> &variable, const T *values)                  \
    {                                                                          \
        adios2::core::Variable<T> *inlineVar =                                 \
            this->Impl->Io->InquireVariable<T>(variable.m_Name);               \
        this->Impl->Writer->Put(*inlineVar, values);                           \
    }
ADIOS2_FOREACH_STDTYPE_1ARG(declare)
#undef declare

void ParaViewFidesEngine::DoClose(const int transportIndex)
{
    this->Impl->Writer->Close(transportIndex);
}

} // end namespace fides_plugin

extern "C" {

fides_plugin::ParaViewFidesEngine *EngineCreate(adios2::core::IO &io,
                                                const std::string &name,
                                                const adios2::Mode mode,
                                                adios2::helper::Comm comm)
{
    (void)mode;
    return new fides_plugin::ParaViewFidesEngine(io, name, comm.Duplicate());
}

void EngineDestroy(fides_plugin::ParaViewFidesEngine *obj) { delete obj; }
}
<|endoftext|>