2012-09-05 13:37:39 +04:00
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author: Axel Naumann <axel@cern.ch>
2014-01-07 14:08:37 +04:00
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
2012-09-05 13:37:39 +04:00
//------------------------------------------------------------------------------
# include "cling/MetaProcessor/MetaProcessor.h"
2012-11-28 20:50:13 +04:00
# include "Display.h"
2012-09-05 13:37:39 +04:00
# include "InputValidator.h"
2012-12-01 04:45:43 +04:00
# include "MetaParser.h"
# include "MetaSema.h"
2012-09-05 13:37:39 +04:00
# include "cling/Interpreter/Interpreter.h"
2014-02-27 01:37:16 +04:00
# include "cling/Interpreter/Value.h"
2012-09-05 13:37:39 +04:00
# include "clang/Basic/FileManager.h"
# include "clang/Basic/TargetInfo.h"
# include "clang/Frontend/CompilerInstance.h"
# include "clang/Lex/Preprocessor.h"
2013-09-19 19:59:58 +04:00
# include "llvm/Support/Path.h"
2012-09-05 13:37:39 +04:00
2012-10-30 17:40:25 +04:00
# include <fstream>
2012-11-28 20:38:02 +04:00
# include <cstdlib>
2012-11-21 00:21:29 +04:00
# include <cctype>
2016-12-07 09:35:33 +03:00
# include <sstream>
2013-12-18 18:51:27 +04:00
# include <stdio.h>
2014-02-03 17:02:56 +04:00
# ifndef WIN32
# include <unistd.h>
# else
# include <io.h>
# define STDIN_FILENO 0
# define STDOUT_FILENO 1
# define STDERR_FILENO 2
# endif
2012-11-28 20:38:02 +04:00
2012-09-05 13:37:39 +04:00
using namespace clang ;
namespace cling {
2016-08-30 02:03:02 +03:00
class MetaProcessor : : RedirectOutput {
typedef std : : stack < std : : string > RedirectStack ;
enum { kNumRedirects = 2 } ;
int m_Backups [ kNumRedirects ] ;
RedirectStack m_Stacks [ kNumRedirects ] ;
static int translate ( int fd ) {
switch ( fd ) {
case STDOUT_FILENO : return 0 ;
case STDERR_FILENO : return 1 ;
default : break ;
}
llvm_unreachable ( " Cannot backup given file descriptor " ) ;
return - 1 ;
}
void backup ( int fd , int iFD ) {
// Have we already backed it up?
if ( m_Backups [ iFD ] = = 0 ) {
m_Backups [ iFD ] = dup ( fd ) ;
if ( m_Backups [ iFD ] < 0 ) {
llvm : : errs ( ) < < " RedirectOutput::copyFileDescriptor: Duplicating the "
" file descriptor " < < fd < < " resulted in an error. "
" Will not be able to unredirect. " ;
}
}
}
public :
RedirectOutput ( ) {
: : memset ( m_Backups , 0 , sizeof ( m_Backups ) ) ;
}
~ RedirectOutput ( ) {
for ( unsigned i = 0 ; i < kNumRedirects ; + + i ) {
if ( const int fd = m_Backups [ i ] )
: : close ( fd ) ;
}
}
void redirect ( llvm : : StringRef filePath , bool append , int fd ) {
const int iFD = translate ( fd ) ;
RedirectStack & rStack = m_Stacks [ iFD ] ;
// If we have a fileName to redirect to store it.
if ( ! filePath . empty ( ) ) {
backup ( fd , iFD ) ;
rStack . push ( filePath . str ( ) ) ;
if ( ! append ) {
FILE * f ;
if ( ! ( f = fopen ( rStack . top ( ) . c_str ( ) , " w " ) ) ) {
llvm : : errs ( ) < < " cling::MetaProcessor::setFileStream: "
" The file path " < < filePath < < " is not valid. " ;
} else
fclose ( f ) ;
}
} else {
// Else unredirection, so switch to the previous file.
// If there is no previous file on the stack we pop the file
if ( ! rStack . empty ( ) )
rStack . pop ( ) ;
}
}
void unredirect ( int expectedFD , FILE * file ) {
// Switch back to previous file after line is processed.
// Flush the current content if there is any.
if ( ! feof ( file ) )
fflush ( file ) ;
const int iFD = translate ( expectedFD ) ;
// Copy the original fd for the std.
if ( dup2 ( m_Backups [ iFD ] , expectedFD ) ! = expectedFD ) {
llvm : : errs ( ) < < " cling::MetaProcessor::unredirect "
< < " The unredirection file descriptor not valid "
< < m_Backups [ iFD ] < < " . \n " ;
}
}
const std : : string * file ( int fd ) const {
const int iFD = translate ( fd ) ;
return m_Stacks [ iFD ] . empty ( ) ? nullptr : & m_Stacks [ iFD ] . top ( ) ;
}
} ;
2014-01-15 18:27:58 +04:00
MetaProcessor : : MaybeRedirectOutputRAII : : MaybeRedirectOutputRAII (
MetaProcessor * p )
2014-01-20 16:51:51 +04:00
: m_MetaProcessor ( p ) , m_isCurrentlyRedirecting ( 0 ) {
StringRef redirectionFile ;
2016-08-30 02:03:02 +03:00
if ( RedirectOutput * output = m_MetaProcessor - > m_RedirectOutput . get ( ) ) {
const std : : string * stdOut = output - > file ( STDOUT_FILENO ) ;
if ( stdOut )
redirect ( stdout , * stdOut , kSTDOUT ) ;
if ( const std : : string * stdErr = output - > file ( STDERR_FILENO ) ) {
if ( * stdErr = = " _IO_2_1_stdout_ " ) {
// If out is redirected to a file.
if ( ! stdOut )
output - > unredirect ( STDERR_FILENO , stderr ) ;
else
stdErr = stdOut ;
2014-01-20 16:51:51 +04:00
}
2016-08-30 02:03:02 +03:00
redirect ( stderr , * stdErr , kSTDERR ) ;
2014-01-20 16:51:51 +04:00
}
2013-12-18 18:51:27 +04:00
}
}
2015-08-06 15:39:46 +03:00
MetaProcessor : : MaybeRedirectOutputRAII : : ~ MaybeRedirectOutputRAII ( ) {
pop ( ) ;
}
2014-01-20 16:51:51 +04:00
void MetaProcessor : : MaybeRedirectOutputRAII : : redirect ( FILE * file ,
const std : : string & fileName ,
MetaProcessor : : RedirectionScope scope ) {
2014-01-14 21:25:43 +04:00
if ( ! fileName . empty ( ) ) {
2014-01-20 16:51:51 +04:00
FILE * redirectionFile = freopen ( fileName . c_str ( ) , " a " , file ) ;
2014-01-14 21:25:43 +04:00
if ( ! redirectionFile ) {
2014-01-20 16:51:51 +04:00
llvm : : errs ( ) < < " cling::MetaProcessor::MaybeRedirectOutputRAII::redirect: "
" Not succefully reopened the redirection file "
< < fileName . c_str ( ) < < " \n . " ;
2014-01-14 21:25:43 +04:00
} else {
2014-01-20 16:51:51 +04:00
m_isCurrentlyRedirecting | = scope ;
2014-01-14 21:25:43 +04:00
}
}
}
2014-01-20 16:51:51 +04:00
void MetaProcessor : : MaybeRedirectOutputRAII : : pop ( ) {
2015-08-06 15:39:46 +03:00
//If we have only one redirection RAII
//only then do the unredirection.
2016-08-30 02:03:02 +03:00
if ( RedirectOutput * redirect = m_MetaProcessor - > m_RedirectOutput . get ( ) ) {
if ( m_isCurrentlyRedirecting & kSTDOUT )
redirect - > unredirect ( STDOUT_FILENO , stdout ) ;
if ( m_isCurrentlyRedirecting & kSTDERR )
redirect - > unredirect ( STDERR_FILENO , stderr ) ;
2014-01-20 16:51:51 +04:00
}
}
2014-01-14 21:25:43 +04:00
2014-08-04 06:05:42 +04:00
MetaProcessor : : MetaProcessor ( Interpreter & interp , raw_ostream & outs )
2014-02-25 17:11:10 +04:00
: m_Interp ( interp ) , m_Outs ( & outs ) {
2012-09-05 13:37:39 +04:00
m_InputValidator . reset ( new InputValidator ( ) ) ;
2012-12-01 04:45:43 +04:00
m_MetaParser . reset ( new MetaParser ( new MetaSema ( interp , * this ) ) ) ;
2012-09-05 13:37:39 +04:00
}
2016-07-15 16:47:37 +03:00
MetaProcessor : : ~ MetaProcessor ( ) {
}
2012-09-05 13:37:39 +04:00
2012-10-05 16:09:51 +04:00
int MetaProcessor : : process ( const char * input_text ,
2013-06-10 17:14:36 +04:00
Interpreter : : CompilationResult & compRes ,
2014-02-27 01:37:16 +04:00
Value * result ) {
2013-06-10 17:14:36 +04:00
if ( result )
2014-02-27 01:37:16 +04:00
* result = Value ( ) ;
2013-05-24 19:50:13 +04:00
compRes = Interpreter : : kSuccess ;
2012-10-29 18:23:53 +04:00
int expectedIndent = m_InputValidator - > getExpectedIndent ( ) ;
2014-08-04 06:05:42 +04:00
2013-05-24 19:50:13 +04:00
if ( expectedIndent )
compRes = Interpreter : : kMoreInputExpected ;
2012-10-29 18:23:53 +04:00
if ( ! input_text | | ! input_text [ 0 ] ) {
// nullptr / empty string, nothing to do.
return expectedIndent ;
2012-09-05 13:37:39 +04:00
}
std : : string input_line ( input_text ) ;
if ( input_line = = " \n " ) { // just a blank line, nothing to do.
2012-10-29 18:23:53 +04:00
return expectedIndent ;
2012-09-05 13:37:39 +04:00
}
// Check for and handle meta commands.
2012-12-01 04:45:43 +04:00
m_MetaParser - > enterNewInputLine ( input_line ) ;
2013-05-24 19:50:13 +04:00
MetaSema : : ActionResult actionResult = MetaSema : : AR_Success ;
2016-07-03 05:35:49 +03:00
if ( ! m_InputValidator - > inBlockComment ( ) & &
m_MetaParser - > isMetaCommand ( actionResult , result ) ) {
2012-12-06 15:47:24 +04:00
if ( m_MetaParser - > isQuitRequested ( ) )
return - 1 ;
2013-05-24 19:50:13 +04:00
if ( actionResult ! = MetaSema : : AR_Success )
compRes = Interpreter : : kFailure ;
2014-05-29 18:59:34 +04:00
// ExpectedIndent might have changed after meta command.
return m_InputValidator - > getExpectedIndent ( ) ;
2012-09-05 13:37:39 +04:00
}
// Check if the current statement is now complete. If not, return to
// prompt for more.
2012-12-01 04:45:43 +04:00
if ( m_InputValidator - > validate ( input_line ) = = InputValidator : : kIncomplete ) {
2013-05-24 19:50:13 +04:00
compRes = Interpreter : : kMoreInputExpected ;
2012-09-05 13:37:39 +04:00
return m_InputValidator - > getExpectedIndent ( ) ;
}
// We have a complete statement, compile and execute it.
2016-07-12 04:32:59 +03:00
std : : string input ;
m_InputValidator - > reset ( & input ) ;
2012-12-06 14:40:20 +04:00
// if (m_Options.RawInput)
// compResLocal = m_Interp.declare(input);
// else
2013-06-10 17:14:36 +04:00
compRes = m_Interp . process ( input , result ) ;
2012-09-05 13:37:39 +04:00
return 0 ;
}
2012-12-06 15:47:24 +04:00
void MetaProcessor : : cancelContinuation ( ) const {
2012-12-04 12:05:33 +04:00
m_InputValidator - > reset ( ) ;
}
2012-12-06 15:47:24 +04:00
int MetaProcessor : : getExpectedIndent ( ) const {
return m_InputValidator - > getExpectedIndent ( ) ;
2012-09-05 13:37:39 +04:00
}
2016-12-07 09:34:16 +03:00
static Interpreter : : CompilationResult reportIOErr ( llvm : : StringRef File ,
const char * What ) {
llvm : : errs ( ) < < " Error in cling::MetaProcessor: "
" cannot " < < What < < " input: ' " < < File < < " ' \n " ;
return Interpreter : : kFailure ;
}
2012-10-30 17:40:25 +04:00
Interpreter : : CompilationResult
MetaProcessor : : readInputFromFile ( llvm : : StringRef filename ,
2014-05-19 18:34:21 +04:00
Value * result ,
2016-12-07 09:35:33 +03:00
size_t posOpenCurly ,
bool lineByLine ) {
2012-11-09 14:54:07 +04:00
2016-12-07 09:34:16 +03:00
// FIXME: This will fail for Unicode BOMs (and seems really weird)
2012-11-21 00:21:29 +04:00
{
// check that it's not binary:
std : : ifstream in ( filename . str ( ) . c_str ( ) , std : : ios : : in | std : : ios : : binary ) ;
2016-12-07 09:34:16 +03:00
if ( in . fail ( ) )
return reportIOErr ( filename , " open " ) ;
2012-11-21 00:21:29 +04:00
char magic [ 1024 ] = { 0 } ;
in . read ( magic , sizeof ( magic ) ) ;
size_t readMagic = in . gcount ( ) ;
2016-07-27 02:24:36 +03:00
// Binary files < 300 bytes are rare, and below newlines etc make the
// heuristic unreliable.
2016-12-07 09:34:16 +03:00
if ( ! in . fail ( ) & & readMagic > = 300 ) {
2013-11-21 20:52:57 +04:00
llvm : : StringRef magicStr ( magic , in . gcount ( ) ) ;
2013-09-19 19:05:28 +04:00
llvm : : sys : : fs : : file_magic fileType
2013-11-21 20:52:57 +04:00
= llvm : : sys : : fs : : identify_magic ( magicStr ) ;
2016-12-07 09:34:16 +03:00
if ( fileType ! = llvm : : sys : : fs : : file_magic : : unknown )
return reportIOErr ( filename , " read from binary " ) ;
2012-11-21 00:21:29 +04:00
unsigned printable = 0 ;
2012-11-21 14:51:41 +04:00
for ( size_t i = 0 ; i < readMagic ; + + i )
2012-11-21 00:21:29 +04:00
if ( isprint ( magic [ i ] ) )
+ + printable ;
if ( 10 * printable < 5 * readMagic ) {
// 50% printable for ASCII files should be a safe guess.
2016-12-07 09:34:16 +03:00
return reportIOErr ( filename , " won't read from likely binary " ) ;
2012-11-21 00:21:29 +04:00
}
}
}
2012-10-30 17:40:25 +04:00
std : : ifstream in ( filename . str ( ) . c_str ( ) ) ;
2016-12-07 09:34:16 +03:00
if ( in . fail ( ) )
return reportIOErr ( filename , " open " ) ;
2012-11-09 14:54:07 +04:00
in . seekg ( 0 , std : : ios : : end ) ;
2016-12-07 09:34:16 +03:00
if ( in . fail ( ) )
return reportIOErr ( filename , " seek " ) ;
2012-11-09 14:54:07 +04:00
size_t size = in . tellg ( ) ;
2016-12-07 09:34:16 +03:00
if ( in . fail ( ) )
return reportIOErr ( filename , " tell " ) ;
2012-11-09 14:54:07 +04:00
in . seekg ( 0 ) ;
2016-12-07 09:34:16 +03:00
if ( in . fail ( ) )
return reportIOErr ( filename , " rewind " ) ;
std : : string content ( size , ' ' ) ;
2014-08-04 06:05:42 +04:00
in . read ( & content [ 0 ] , size ) ;
2016-12-07 09:34:16 +03:00
if ( in . fail ( ) )
return reportIOErr ( filename , " read " ) ;
2012-11-09 14:54:07 +04:00
2016-07-27 02:24:36 +03:00
if ( posOpenCurly ! = ( size_t ) - 1 & & ! content . empty ( ) ) {
assert ( content [ posOpenCurly ] = = ' { '
& & " No curly at claimed position of opening curly! " ) ;
// hide the curly brace:
content [ posOpenCurly ] = ' ' ;
// and the matching closing '}'
2012-11-09 14:54:07 +04:00
static const char whitespace [ ] = " \t \r \n " ;
2016-07-27 02:24:36 +03:00
size_t posCloseCurly = content . find_last_not_of ( whitespace ) ;
if ( posCloseCurly ! = std : : string : : npos ) {
if ( content [ posCloseCurly ] = = ' ; ' & & content [ posCloseCurly - 1 ] = = ' } ' ) {
content [ posCloseCurly - - ] = ' ' ; // replace ';' and enter next if
}
if ( content [ posCloseCurly ] = = ' } ' ) {
content [ posCloseCurly ] = ' ' ; // replace '}'
} else {
std : : string : : size_type posBlockClose = content . find_last_of ( ' } ' ) ;
if ( posBlockClose ! = std : : string : : npos ) {
content [ posBlockClose ] = ' ' ; // replace '}'
2015-06-08 12:14:41 +03:00
}
2016-07-27 02:24:36 +03:00
std : : string : : size_type posComment
= content . find_first_not_of ( whitespace , posBlockClose ) ;
if ( posComment ! = std : : string : : npos
& & content [ posComment ] = = ' / ' & & content [ posComment + 1 ] = = ' / ' ) {
// More text (comments) are okay after the last '}', but
// we can not easily find it to remove it (so we need to upgrade
// this code to better handle the case with comments or
// preprocessor code before and after the leading { and
// trailing })
while ( posComment < = posCloseCurly ) {
content [ posComment + + ] = ' ' ; // replace '}' and comment
2012-11-09 14:54:07 +04:00
}
2016-07-27 02:24:36 +03:00
} else {
content [ posCloseCurly ] = ' { ' ;
// By putting the '{' back, we keep the code as consistent as
// the user wrote it ... but we should still warn that we not
// goint to treat this file an unamed macro.
llvm : : errs ( )
< < " Warning in cling::MetaProcessor: can not find the closing '}', "
< < llvm : : sys : : path : : filename ( filename )
< < " is not handled as an unamed script! \n " ;
} // did not find "//"
} // remove comments after the trailing '}'
} // find '}'
} // ignore outermost block
2012-11-09 14:54:07 +04:00
2016-12-07 09:49:22 +03:00
# ifndef NDEBUG
2016-12-07 09:34:16 +03:00
m_CurrentlyExecutingFile = filename ;
2012-11-21 00:21:29 +04:00
bool topmost = ! m_TopExecutingFile . data ( ) ;
if ( topmost )
m_TopExecutingFile = m_CurrentlyExecutingFile ;
2016-12-07 09:49:22 +03:00
# endif
2016-12-07 09:35:33 +03:00
content . insert ( 0 , " #line 2 \" " + filename . str ( ) + " \" \n " ) ;
2014-05-19 17:54:04 +04:00
// We don't want to value print the results of a unnamed macro.
2016-12-07 09:35:33 +03:00
if ( content . back ( ) ! = ' ; ' )
content . append ( " ; " ) ;
Interpreter : : CompilationResult ret = Interpreter : : kSuccess ;
if ( lineByLine ) {
int rslt = 0 ;
std : : string line ;
std : : stringstream ss ( content ) ;
while ( std : : getline ( ss , line , ' \n ' ) ) {
rslt = process ( line . c_str ( ) , ret , result ) ;
if ( ret = = Interpreter : : kFailure )
break ;
}
if ( rslt ) {
llvm : : errs ( ) < < " Error in cling::MetaProcessor: file "
< < llvm : : sys : : path : : filename ( filename )
< < " is incomplete (missing parenthesis or similar)! \n " ;
}
} else
ret = m_Interp . process ( content , result ) ;
2016-12-07 09:49:22 +03:00
# ifndef NDEBUG
2012-11-21 00:21:29 +04:00
m_CurrentlyExecutingFile = llvm : : StringRef ( ) ;
if ( topmost )
m_TopExecutingFile = llvm : : StringRef ( ) ;
2016-12-07 09:49:22 +03:00
# endif
2012-10-30 17:40:25 +04:00
return ret ;
}
2016-08-30 02:03:02 +03:00
void MetaProcessor : : setStdStream ( llvm : : StringRef file , RedirectionScope scope ,
bool append ) {
assert ( ( scope & kSTDOUT | | scope & kSTDERR ) & & " Invalid RedirectionScope " ) ;
if ( ! m_RedirectOutput )
m_RedirectOutput . reset ( new RedirectOutput ) ;
if ( scope & kSTDOUT )
m_RedirectOutput - > redirect ( file , append , STDOUT_FILENO ) ;
if ( scope & kSTDERR )
m_RedirectOutput - > redirect ( file , append , STDERR_FILENO ) ;
2013-12-18 18:51:27 +04:00
}
2015-01-21 18:51:20 +03:00
void MetaProcessor : : registerUnloadPoint ( const Transaction * T ,
llvm : : StringRef filename ) {
m_MetaParser - > getActions ( ) . registerUnloadPoint ( T , filename ) ;
}
2012-10-30 17:40:25 +04:00
} // end namespace cling