Parser - Prototyping call checks

This commit is contained in:
Emmanuel BENOîT 2017-11-07 13:24:01 +01:00
parent 67b644ac54
commit 14288e3c87
3 changed files with 198 additions and 28 deletions

View file

@ -614,6 +614,7 @@ M_INSTR_( Call )
}
auto& instr{ instructions.add< T_CallInstrNode >( input[ 1 ] ) };
instr.location( ) = input[ 0 ].location( );
for ( auto it = input.begin( ) + 2 ; it.valid( ) ; ++it ) {
instr.addArgument( parseExpression( instr , *it ) );
}

View file

@ -197,6 +197,8 @@ class T_RootNode : public A_Node
uint32_t nFunctions( ) const noexcept
{ return functions_.size( ); }
int32_t functionIndex( T_String const& name ) const noexcept
{ return functions_.indexOf( name ); }
A_FuncNode& function( const uint32_t index ) const noexcept
{ return *functions_.values( )[ index ]; }
};
@ -227,6 +229,9 @@ class T_FuncNode : public A_FuncNode
// of the initial argument.
T_Optional< T_SRDLocation > addArgument(
T_SRDToken const& token ) noexcept;
uint32_t arguments( ) const noexcept
{ return argNames_.size( ); }
};
/*----------------------------------------------------------------------------*/

View file

@ -8,9 +8,98 @@ using namespace opast;
namespace {
/*============================================================================*/
void PrintStreamError(
char const* const prefix ,
T_String const& name ,
X_StreamError const& error )
{
T_StringBuilder sb;
sb << prefix << " '" << name << "': " << error.what( );
if ( error.code( ) == E_StreamError::SYSTEM_ERROR ) {
sb << " (error code " << error.systemError( ) << ")";
}
sb << '\n' << '\0';
fprintf( stderr , "%s" , sb.data( ) );
}
void WriteSRDError(
T_StringBuilder& sb ,
T_SRDError const& error )
{
sb << error.location( ) << " - " << error.error( ) << "\n";
}
/*============================================================================*/
// FIXME TESTING, MOVE THIS LATER
template<
typename Enum ,
typename Storage = uint32_t
> class T_Flags
{
private:
Storage flags_;
public:
constexpr T_Flags( ) noexcept
: flags_( 0 ) {}
constexpr T_Flags( T_Flags const& other ) noexcept
: flags_( other.flags_ ) { }
constexpr T_Flags( const Enum flag ) noexcept
: flags_( 1 << int( flag ) ) {}
constexpr T_Flags( std::initializer_list< Enum > flags ) noexcept
: flags_( 0 )
{
for ( auto f : flags ) {
flags_ |= ( 1 << int( f ) );
}
}
explicit constexpr T_Flags( const Storage flags ) noexcept
: flags_( flags ) { }
constexpr T_Flags operator |=( T_Flags other ) noexcept
{ flags_ |= other.flags_; return *this; }
constexpr T_Flags operator &=( T_Flags other ) noexcept
{ flags_ &= other.flags_; return *this; }
constexpr T_Flags operator ^=( T_Flags other ) noexcept
{ flags_ ^= other.flags_; return *this; }
constexpr T_Flags operator ~( ) const noexcept
{ return T_Flags( ~flags_ ); }
constexpr T_Flags operator &( T_Flags other ) const noexcept
{ return T_Flags( flags_ & other.flags_ ); }
constexpr T_Flags operator |( T_Flags other ) const noexcept
{ return T_Flags( flags_ & other.flags_ ); }
constexpr T_Flags operator ^( T_Flags other ) const noexcept
{ return T_Flags( flags_ ^ other.flags_ ); }
constexpr operator bool( ) const noexcept
{ return flags_ != 0; }
constexpr bool operator!( ) const noexcept
{ return flags_ == 0; }
explicit constexpr operator Storage( ) const noexcept
{ return flags_; }
constexpr bool operator ==( const T_Flags other ) const noexcept
{ return flags_ == other.flags_; }
constexpr bool operator !=( const T_Flags other ) const noexcept
{ return flags_ != other.flags_; }
constexpr bool isSet( const T_Flags value ) const noexcept
{ return ( *this & value ) == value; }
constexpr bool isClear( const T_Flags value ) const noexcept
{ return !( *this & value ); }
};
template< typename NodeType >
class T_Visitor
{
@ -45,7 +134,7 @@ class T_Visitor
public:
T_Visitor( ) = delete;
T_Visitor( T_Visitor const& ) noexcept = default;
T_Visitor( T_Visitor const& ) = default;
T_Visitor( T_Visitor&& ) noexcept = default;
explicit T_Visitor( F_NodeBrowser browser ) noexcept;
@ -231,30 +320,111 @@ A_Node* OpASTBrowser(
}
/*============================================================================*/
void PrintStreamError(
char const* const prefix ,
T_String const& name ,
X_StreamError const& error )
bool checkCalls( T_RootNode& root )
{
T_StringBuilder sb;
sb << prefix << " '" << name << "': " << error.what( );
if ( error.code( ) == E_StreamError::SYSTEM_ERROR ) {
sb << " (error code " << error.systemError( ) << ")";
T_Visitor< A_Node > visitor( OpASTBrowser );
T_Array< T_SRDError > errors;
T_MultiArray< uint32_t > calls;
uint32_t cfi;
for ( cfi = 0 ; cfi < root.nFunctions( ) ; cfi ++ ) {
calls.next( );
visitor.visit( root.function( cfi ) , [&]( A_Node& node , bool exit ) -> bool {
if ( exit || dynamic_cast< A_ExpressionNode* >( &node ) ) {
return false;
}
if ( node.type( ) != A_Node::OP_CALL ) {
return true;
}
auto& call( (T_CallInstrNode&) node );
const auto callee( root.functionIndex( call.id( ) ) );
if ( callee >= 0 ) {
if ( !calls.contains( cfi , callee ) ) {
calls.add( (uint32_t) callee );
}
// Check argument count while we're at it
auto& fn( (T_FuncNode&) root.function( callee ) );
if ( fn.arguments( ) != call.arguments( ) ) {
T_StringBuilder sb;
sb << "function expects " << fn.arguments( )
<< " argument" << ( fn.arguments( ) == 1 ? "" : "s" )
<< ", " << call.arguments( )
<< " argument" << ( call.arguments( ) == 1 ? "" : "s" )
<< " provided";
errors.addNew( std::move( sb ) , call.location( ) );
}
} else {
errors.addNew( "unknown function" , call.idLocation( ) );
}
return false;
} );
}
sb << '\n' << '\0';
fprintf( stderr , "%s" , sb.data( ) );
}
void WriteSRDError(
T_StringBuilder& sb ,
T_SRDError const& error )
{
sb << error.location( ) << " - " << error.error( ) << "\n";
if ( !errors.empty( ) ) {
T_StringBuilder sb;
for ( auto const& err : errors ) {
WriteSRDError( sb , err );
}
sb << "Parser failed\n" << '\0';
fprintf( stderr , "%s" , sb.data( ) );
return false;
}
T_Visitor< uint32_t > callGraphVisitor(
[&]( uint32_t& v , uint32_t child ) -> uint32_t* {
const uint32_t nc( calls.sizeOf( v ) );
if ( child < nc ) {
return &calls.get( v , child );
}
return nullptr;
} );
enum class E_CallInfo_ {
INIT_CHECKED , FRAME_CHECKED ,
INIT_CALLED , FRAME_CALLED
};
using T_CallInfo_ = T_Flags< E_CallInfo_ , uint8_t >;
T_CallInfo_ callInfo[ calls.size( ) ];
uint32_t initId( root.functionIndex( "*init*" ) );
callGraphVisitor.visit( initId , [&]( uint32_t& id , const bool exit ) -> bool {
if ( exit || callInfo[ id ] & E_CallInfo_::INIT_CALLED ) {
return false;
}
callInfo[ id ] |= E_CallInfo_::INIT_CALLED;
return true;
} );
uint32_t frameId( root.functionIndex( "*frame*" ) );
callGraphVisitor.visit( frameId , [&]( uint32_t& id , const bool exit ) -> bool {
if ( exit || callInfo[ id ] & E_CallInfo_::FRAME_CALLED ) {
return false;
}
callInfo[ id ] |= E_CallInfo_::FRAME_CALLED;
return true;
} );
T_StringBuilder sb;
for ( auto callerId = 0u ; callerId < calls.size( ) ; callerId ++ ) {
const auto nCallees( calls.sizeOf( callerId ) );
if ( !nCallees ) {
continue;
}
sb << root.function( callerId ).name( ) << " calls";
for ( auto i = 0u ; i < nCallees ; i ++ ) {
auto const& callee( root.function( calls.get( callerId , i ) ) );
sb << ' ' << callee.name( );
}
sb << '\n';
}
sb << '\0';
printf( "%s" , sb.data( ) );
return true;
}
/*============================================================================*/
} // namespace
@ -297,13 +467,7 @@ int main( int argc , char** argv )
printf( "Success!\n" );
auto result( parser.result( ) );
T_Visitor< A_Node > visitor( OpASTBrowser );
visitor.visit( *result , []( A_Node& node , bool enter ) {
if ( enter ) {
printf( "Enter node %p\n" , &node );
}
return true;
} );
checkCalls( *result );
return 0;
} else {
T_StringBuilder sb;