No more long-lived parsers.

We've had too many bugs from improper re-initialization.

Fixes https://gitlab.com/kicad/code/kicad/issues/9429
This commit is contained in:
Jeff Young 2021-10-31 19:57:52 +00:00
parent 6c05e5d1a8
commit ceceda37cc
9 changed files with 61 additions and 146 deletions

View File

@ -857,16 +857,20 @@ void DS_DATA_MODEL::SetPageLayout( const char* aPageLayout, bool Append, const w
if( ! Append ) if( ! Append )
ClearList(); ClearList();
DRAWING_SHEET_PARSER lp_parser( aPageLayout, wxT( "Sexpr_string" ) ); DRAWING_SHEET_PARSER parser( aPageLayout, wxT( "Sexpr_string" ) );
try try
{ {
lp_parser.Parse( this ); parser.Parse( this );
} }
catch( const IO_ERROR& ioe ) catch( const IO_ERROR& ioe )
{ {
wxLogMessage( ioe.What() ); wxLogMessage( ioe.What() );
} }
catch( const std::bad_alloc& )
{
wxLogMessage( "Memory exhaustion reading drawing sheet" );
}
} }
@ -920,11 +924,11 @@ bool DS_DATA_MODEL::LoadDrawingSheet( const wxString& aFullFileName, bool Append
if( ! Append ) if( ! Append )
ClearList(); ClearList();
DRAWING_SHEET_PARSER pl_parser( buffer.get(), fullFileName ); DRAWING_SHEET_PARSER parser( buffer.get(), fullFileName );
try try
{ {
pl_parser.Parse( this ); parser.Parse( this );
} }
catch( const IO_ERROR& ioe ) catch( const IO_ERROR& ioe )
{ {

View File

@ -2,6 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2017 KiCad Developers, see AUTHORS.TXT for contributors. * Copyright (C) 2017 KiCad Developers, see AUTHORS.TXT for contributors.
* Copyright (C) 2017-2021 KiCad Developers, see AUTHORS.txt for contributors.
* @author Kristoffer Ödmark * @author Kristoffer Ödmark
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -25,7 +26,6 @@
#include <wx/clipbrd.h> #include <wx/clipbrd.h>
#include <wx/log.h> #include <wx/log.h>
#include <build_version.h>
#include <board.h> #include <board.h>
#include <ignore.h> #include <ignore.h>
#include <pad.h> #include <pad.h>
@ -402,18 +402,16 @@ BOARD* CLIPBOARD_IO::Load( const wxString& aFileName, BOARD* aAppendToMe,
} }
STRING_LINE_READER reader( result, wxT( "clipboard" ) ); STRING_LINE_READER reader( result, wxT( "clipboard" ) );
PCB_PARSER parser( &reader, aAppendToMe );
init( aProperties ); init( aProperties );
m_parser->SetLineReader( &reader );
m_parser->SetBoard( aAppendToMe );
BOARD_ITEM* item; BOARD_ITEM* item;
BOARD* board; BOARD* board;
try try
{ {
item = m_parser->Parse(); item = parser.Parse();
} }
catch( const FUTURE_FORMAT_ERROR& ) catch( const FUTURE_FORMAT_ERROR& )
{ {
@ -422,8 +420,8 @@ BOARD* CLIPBOARD_IO::Load( const wxString& aFileName, BOARD* aAppendToMe,
} }
catch( const PARSE_ERROR& parse_error ) catch( const PARSE_ERROR& parse_error )
{ {
if( m_parser->IsTooRecent() ) if( parser.IsTooRecent() )
throw FUTURE_FORMAT_ERROR( parse_error, m_parser->GetRequiredVersion() ); throw FUTURE_FORMAT_ERROR( parse_error, parser.GetRequiredVersion() );
else else
throw; throw;
} }
@ -431,9 +429,8 @@ BOARD* CLIPBOARD_IO::Load( const wxString& aFileName, BOARD* aAppendToMe,
if( item->Type() != PCB_T ) if( item->Type() != PCB_T )
{ {
// The parser loaded something that was valid, but wasn't a board. // The parser loaded something that was valid, but wasn't a board.
THROW_PARSE_ERROR( _( "Clipboard content is not KiCad compatible" ), THROW_PARSE_ERROR( _( "Clipboard content is not KiCad compatible" ), parser.CurSource(),
m_parser->CurSource(), m_parser->CurLine(), parser.CurLine(), parser.CurLineNumber(), parser.CurOffset() );
m_parser->CurLineNumber(), m_parser->CurOffset() );
} }
else else
{ {

View File

@ -1,11 +1,8 @@
/**
* @file kicad_netlist_reader.cpp
*/
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 1992-2011 Jean-Pierre Charras. * Copyright (C) 1992-2011 Jean-Pierre Charras.
* Copyright (C) 1992-2020 KiCad Developers, see change_log.txt for contributors. * Copyright (C) 1992-2021 KiCad Developers, see change_log.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -36,7 +33,9 @@ using namespace NL_T;
void KICAD_NETLIST_READER::LoadNetlist() void KICAD_NETLIST_READER::LoadNetlist()
{ {
m_parser->Parse(); KICAD_NETLIST_PARSER parser( m_lineReader, m_netlist );
parser.Parse();
if( m_footprintReader ) if( m_footprintReader )
{ {

View File

@ -1,10 +1,3 @@
#ifndef NETLIST_READER_H
#define NETLIST_READER_H
/**
* @file netlist_reader.h
*/
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
@ -30,6 +23,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/ */
#ifndef NETLIST_READER_H
#define NETLIST_READER_H
#include <boost/ptr_container/ptr_vector.hpp> #include <boost/ptr_container/ptr_vector.hpp>
#include <macros.h> #include <macros.h>
@ -172,11 +168,6 @@ public:
*/ */
virtual void LoadNetlist() = 0; virtual void LoadNetlist() = 0;
/**
* @return the #LINE_READER associated with the #NETLIST_READER.
*/
LINE_READER* GetLineReader();
protected: protected:
NETLIST* m_netlist; ///< The net list to read the file(s) into. NETLIST* m_netlist; ///< The net list to read the file(s) into.
bool m_loadFootprintFilters; ///< Load the component footprint filters section if true. bool m_loadFootprintFilters; ///< Load the component footprint filters section if true.
@ -280,10 +271,6 @@ class KICAD_NETLIST_PARSER : public NETLIST_LEXER
public: public:
KICAD_NETLIST_PARSER( LINE_READER* aReader, NETLIST* aNetlist ); KICAD_NETLIST_PARSER( LINE_READER* aReader, NETLIST* aNetlist );
void SetLineReader( LINE_READER* aLineReader );
void SetNetlist( NETLIST* aNetlist ) { m_netlist = aNetlist; }
/** /**
* Function Parse * Function Parse
* parse the full netlist * parse the full netlist
@ -364,20 +351,13 @@ public:
KICAD_NETLIST_READER( LINE_READER* aLineReader, KICAD_NETLIST_READER( LINE_READER* aLineReader,
NETLIST* aNetlist, NETLIST* aNetlist,
CMP_READER* aFootprintLinkReader = nullptr ) : CMP_READER* aFootprintLinkReader = nullptr ) :
NETLIST_READER( aLineReader, aNetlist, aFootprintLinkReader ), NETLIST_READER( aLineReader, aNetlist, aFootprintLinkReader )
m_parser( new KICAD_NETLIST_PARSER( aLineReader, aNetlist ) ) { }
{
}
virtual ~KICAD_NETLIST_READER() virtual ~KICAD_NETLIST_READER()
{ { }
delete m_parser;
}
virtual void LoadNetlist() override; virtual void LoadNetlist() override;
private:
KICAD_NETLIST_PARSER* m_parser; ///< The s-expression format parser.
}; };

View File

@ -72,48 +72,20 @@ class PROGRESS_REPORTER;
class PCB_PARSER : public PCB_LEXER class PCB_PARSER : public PCB_LEXER
{ {
public: public:
PCB_PARSER( LINE_READER* aReader = nullptr ) : PCB_PARSER( LINE_READER* aReader, BOARD* aBoard = nullptr,
PROGRESS_REPORTER* aProgressReporter = nullptr, unsigned aLineCount = 0 ) :
PCB_LEXER( aReader ), PCB_LEXER( aReader ),
m_board( nullptr ), m_board( aBoard ),
m_resetKIIDs( false ), m_resetKIIDs( aBoard != nullptr ),
m_progressReporter( nullptr ), m_progressReporter( aProgressReporter ),
m_lastProgressTime( std::chrono::steady_clock::now() ), m_lastProgressTime( std::chrono::steady_clock::now() ),
m_lineCount( 0 ) m_lineCount( aLineCount )
{ {
init(); init();
} }
// ~PCB_PARSER() {} // ~PCB_PARSER() {}
/**
* Set @a aLineReader into the parser, and returns the previous one, if any.
*
* @param aReader is what to read from for tokens, no ownership is received.
* @return LINE_READER* - previous LINE_READER or NULL if none.
*/
LINE_READER* SetLineReader( LINE_READER* aReader )
{
LINE_READER* ret = PopReader();
PushReader( aReader );
return ret;
}
void SetBoard( BOARD* aBoard )
{
init();
m_board = aBoard;
if( aBoard != nullptr )
m_resetKIIDs = true;
}
void SetProgressReporter( PROGRESS_REPORTER* aProgressReporter, unsigned aLineCount )
{
m_progressReporter = aProgressReporter;
m_lastProgressTime = std::chrono::steady_clock::now();
m_lineCount = aLineCount;
}
BOARD_ITEM* Parse(); BOARD_ITEM* Parse();
/** /**

View File

@ -263,15 +263,9 @@ void FP_CACHE::Load()
try try
{ {
FILE_LINE_READER reader( fn.GetFullPath() ); FILE_LINE_READER reader( fn.GetFullPath() );
PCB_PARSER parser( &reader );
m_owner->m_parser->SetLineReader( &reader ); FOOTPRINT* footprint = (FOOTPRINT*) parser.Parse();
// For better or worse (mostly worse), the parser is a long-lived object.
// Make sure we start with a fresh state.
m_owner->m_parser->InitParserState();
m_owner->m_parser->SetBoard( nullptr ); // calls PCB_PARSER::init()
FOOTPRINT* footprint = (FOOTPRINT*) m_owner->m_parser->Parse();
wxString fpName = fn.GetName(); wxString fpName = fn.GetName();
footprint->SetFPID( LIB_ID( wxEmptyString, fpName ) ); footprint->SetFPID( LIB_ID( wxEmptyString, fpName ) );
@ -380,17 +374,16 @@ BOARD_ITEM* PCB_PLUGIN::Parse( const wxString& aClipboardSourceInput )
std::string input = TO_UTF8( aClipboardSourceInput ); std::string input = TO_UTF8( aClipboardSourceInput );
STRING_LINE_READER reader( input, wxT( "clipboard" ) ); STRING_LINE_READER reader( input, wxT( "clipboard" ) );
PCB_PARSER parser( &reader );
m_parser->SetLineReader( &reader );
try try
{ {
return m_parser->Parse(); return parser.Parse();
} }
catch( const PARSE_ERROR& parse_error ) catch( const PARSE_ERROR& parse_error )
{ {
if( m_parser->IsTooRecent() ) if( parser.IsTooRecent() )
throw FUTURE_FORMAT_ERROR( parse_error, m_parser->GetRequiredVersion() ); throw FUTURE_FORMAT_ERROR( parse_error, parser.GetRequiredVersion() );
else else
throw; throw;
} }
@ -2285,7 +2278,6 @@ void PCB_PLUGIN::format( const ZONE* aZone, int aNestLevel ) const
PCB_PLUGIN::PCB_PLUGIN( int aControlFlags ) : PCB_PLUGIN::PCB_PLUGIN( int aControlFlags ) :
m_cache( nullptr ), m_cache( nullptr ),
m_ctl( aControlFlags ), m_ctl( aControlFlags ),
m_parser( new PCB_PARSER() ),
m_mapping( new NETINFO_MAPPING() ) m_mapping( new NETINFO_MAPPING() )
{ {
init( nullptr ); init( nullptr );
@ -2296,7 +2288,6 @@ PCB_PLUGIN::PCB_PLUGIN( int aControlFlags ) :
PCB_PLUGIN::~PCB_PLUGIN() PCB_PLUGIN::~PCB_PLUGIN()
{ {
delete m_cache; delete m_cache;
delete m_parser;
delete m_mapping; delete m_mapping;
} }
@ -2336,15 +2327,12 @@ BOARD* PCB_PLUGIN::DoLoad( LINE_READER& aReader, BOARD* aAppendToMe, const PROPE
{ {
init( aProperties ); init( aProperties );
m_parser->SetLineReader( &aReader ); PCB_PARSER parser( &aReader, aAppendToMe, aProgressReporter, aLineCount );
m_parser->SetBoard( aAppendToMe );
m_parser->SetProgressReporter( aProgressReporter, aLineCount );
BOARD* board; BOARD* board;
try try
{ {
board = dynamic_cast<BOARD*>( m_parser->Parse() ); board = dynamic_cast<BOARD*>( parser.Parse() );
} }
catch( const FUTURE_FORMAT_ERROR& ) catch( const FUTURE_FORMAT_ERROR& )
{ {
@ -2353,8 +2341,8 @@ BOARD* PCB_PLUGIN::DoLoad( LINE_READER& aReader, BOARD* aAppendToMe, const PROPE
} }
catch( const PARSE_ERROR& parse_error ) catch( const PARSE_ERROR& parse_error )
{ {
if( m_parser->IsTooRecent() ) if( parser.IsTooRecent() )
throw FUTURE_FORMAT_ERROR( parse_error, m_parser->GetRequiredVersion() ); throw FUTURE_FORMAT_ERROR( parse_error, parser.GetRequiredVersion() );
else else
throw; throw;
} }
@ -2362,8 +2350,8 @@ BOARD* PCB_PLUGIN::DoLoad( LINE_READER& aReader, BOARD* aAppendToMe, const PROPE
if( !board ) if( !board )
{ {
// The parser loaded something that was valid, but wasn't a board. // The parser loaded something that was valid, but wasn't a board.
THROW_PARSE_ERROR( _( "This file does not contain a PCB." ), m_parser->CurSource(), THROW_PARSE_ERROR( _( "This file does not contain a PCB." ), parser.CurSource(),
m_parser->CurLine(), m_parser->CurLineNumber(), m_parser->CurOffset() ); parser.CurLine(), parser.CurLineNumber(), parser.CurOffset() );
} }
return board; return board;

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2012 CERN. * Copyright (C) 2012 CERN.
* Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -297,7 +297,6 @@ protected:
STRING_FORMATTER m_sf; STRING_FORMATTER m_sf;
OUTPUTFORMATTER* m_out; ///< output any Format()s to this, no ownership OUTPUTFORMATTER* m_out; ///< output any Format()s to this, no ownership
int m_ctl; int m_ctl;
PCB_PARSER* m_parser;
NETINFO_MAPPING* m_mapping; ///< mapping for net codes, so only not empty net codes NETINFO_MAPPING* m_mapping; ///< mapping for net codes, so only not empty net codes
///< are stored with consecutive integers as net codes ///< are stored with consecutive integers as net codes
}; };

View File

@ -1,7 +1,7 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2019 KiCad Developers, see AUTHORS.TXT for contributors. * Copyright (C) 2019-2021 KiCad Developers, see AUTHORS.TXT for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -25,7 +25,6 @@
#include <cstdio> #include <cstdio>
#include <string> #include <string>
#include <common.h> #include <common.h>
#include <profile.h> #include <profile.h>
@ -35,11 +34,7 @@
#include <plugins/kicad/pcb_plugin.h> #include <plugins/kicad/pcb_plugin.h>
#include <plugins/kicad/pcb_parser.h> #include <plugins/kicad/pcb_parser.h>
#include <richio.h> #include <richio.h>
#include <wx/cmdline.h>
#include <qa_utils/stdstream_line_reader.h> #include <qa_utils/stdstream_line_reader.h>
#include <qa_utils/utility_registry.h>
using PARSE_DURATION = std::chrono::microseconds; using PARSE_DURATION = std::chrono::microseconds;
@ -57,10 +52,7 @@ bool parse( std::istream& aStream, bool aVerbose )
STDISTREAM_LINE_READER reader; STDISTREAM_LINE_READER reader;
reader.SetStream( aStream ); reader.SetStream( aStream );
PCB_PARSER parser; PCB_PARSER parser( &reader );
parser.SetLineReader( &reader );
BOARD_ITEM* board = nullptr; BOARD_ITEM* board = nullptr;
PARSE_DURATION duration{}; PARSE_DURATION duration{};
@ -110,10 +102,9 @@ int pcb_parser_main_func( int argc, char** argv )
wxMessageOutput::Set( new wxMessageOutputStderr ); wxMessageOutput::Set( new wxMessageOutputStderr );
wxCmdLineParser cl_parser( argc, argv ); wxCmdLineParser cl_parser( argc, argv );
cl_parser.SetDesc( g_cmdLineDesc ); cl_parser.SetDesc( g_cmdLineDesc );
cl_parser.AddUsageText( cl_parser.AddUsageText( _( "This program parses PCB files, either from the stdin stream or "
_( "This program parses PCB files, either from the " "from the given filenames. This can be used either for standalone "
"stdin stream or from the given filenames. This can be used either for " "testing of the parser or for fuzz testing." ) );
"standalone testing of the parser or for fuzz testing." ) );
int cmd_parsed_ok = cl_parser.Parse(); int cmd_parsed_ok = cl_parser.Parse();
if( cmd_parsed_ok != 0 ) if( cmd_parsed_ok != 0 )
@ -123,10 +114,8 @@ int pcb_parser_main_func( int argc, char** argv )
} }
const bool verbose = cl_parser.Found( "verbose" ); const bool verbose = cl_parser.Found( "verbose" );
bool ok = true; bool ok = true;
const size_t file_count = cl_parser.GetParamCount();
const auto file_count = cl_parser.GetParamCount();
if( file_count == 0 ) if( file_count == 0 )
{ {
@ -163,5 +152,6 @@ int pcb_parser_main_func( int argc, char** argv )
} }
static bool registered = UTILITY_REGISTRY::Register( static bool registered = UTILITY_REGISTRY::Register( { "pcb_parser",
{ "pcb_parser", "Parse a KiCad PCB file", pcb_parser_main_func } ); "Parse a KiCad PCB file",
pcb_parser_main_func } );

View File

@ -1,7 +1,7 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2019-2021 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -69,27 +69,13 @@ void DumpBoardToFile( BOARD& board, const std::string& aFilename )
} }
std::unique_ptr<BOARD_ITEM> ReadBoardItemFromFile( const std::string& aFilename )
{
FILE_LINE_READER reader( aFilename );
PCB_PARSER parser;
parser.SetLineReader( &reader );
return std::unique_ptr<BOARD_ITEM>( parser.Parse() );
}
std::unique_ptr<BOARD_ITEM> ReadBoardItemFromStream( std::istream& aStream ) std::unique_ptr<BOARD_ITEM> ReadBoardItemFromStream( std::istream& aStream )
{ {
// Take input from stdin // Take input from stdin
STDISTREAM_LINE_READER reader; STDISTREAM_LINE_READER reader;
reader.SetStream( aStream ); reader.SetStream( aStream );
PCB_PARSER parser; PCB_PARSER parser( &reader );
parser.SetLineReader( &reader );
std::unique_ptr<BOARD_ITEM> board; std::unique_ptr<BOARD_ITEM> board;
try try