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>
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 {
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 ;
if ( ! m_MetaProcessor - > m_PrevStdoutFileName . empty ( ) ) {
redirectionFile = m_MetaProcessor - > m_PrevStdoutFileName . back ( ) ;
redirect ( stdout , redirectionFile . str ( ) , kSTDOUT ) ;
2013-12-18 18:51:27 +04:00
}
2014-01-20 16:51:51 +04:00
if ( ! m_MetaProcessor - > m_PrevStderrFileName . empty ( ) ) {
redirectionFile = m_MetaProcessor - > m_PrevStderrFileName . back ( ) ;
// Deal with the case 2>&1 and 2&>1
if ( strcmp ( redirectionFile . data ( ) , " _IO_2_1_stdout_ " ) = = 0 ) {
// If out is redirected to a file.
if ( ! m_MetaProcessor - > m_PrevStdoutFileName . empty ( ) ) {
redirectionFile = m_MetaProcessor - > m_PrevStdoutFileName . back ( ) ;
} else {
unredirect ( m_MetaProcessor - > m_backupFDStderr , STDERR_FILENO , stderr ) ;
}
}
redirect ( stderr , redirectionFile . str ( ) , kSTDERR ) ;
2013-12-18 18:51:27 +04:00
}
}
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 ( ) {
if ( m_isCurrentlyRedirecting & kSTDOUT ) {
unredirect ( m_MetaProcessor - > m_backupFDStdout , STDOUT_FILENO , stdout ) ;
}
if ( m_isCurrentlyRedirecting & kSTDERR ) {
unredirect ( m_MetaProcessor - > m_backupFDStderr , STDERR_FILENO , stderr ) ;
2014-01-14 21:25:43 +04:00
}
}
2014-01-20 16:51:51 +04:00
void MetaProcessor : : MaybeRedirectOutputRAII : : unredirect ( int backupFD ,
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 ) ;
}
// Copy the original fd for the std.
if ( dup2 ( backupFD , expectedFD ) ! = expectedFD ) {
llvm : : errs ( ) < < " cling::MetaProcessor::unredirect "
< < " The unredirection file descriptor not valid "
< < backupFD < < " . \n " ;
}
}
2014-01-14 21:25:43 +04:00
2013-01-17 19:27:14 +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 ) ) ) ;
2014-01-20 16:51:51 +04:00
m_backupFDStdout = copyFileDescriptor ( STDOUT_FILENO ) ;
m_backupFDStderr = copyFileDescriptor ( STDERR_FILENO ) ;
2012-09-05 13:37:39 +04:00
}
MetaProcessor : : ~ MetaProcessor ( ) { }
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 ( ) ;
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 ;
2013-06-10 17:14:36 +04:00
if ( 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 ;
2012-10-29 18:23:53 +04:00
return expectedIndent ;
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.
std : : string input = m_InputValidator - > getInput ( ) ;
m_InputValidator - > reset ( ) ;
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
}
2012-10-30 17:40:25 +04:00
Interpreter : : CompilationResult
MetaProcessor : : readInputFromFile ( llvm : : StringRef filename ,
2014-02-27 01:37:16 +04:00
Value * result ,
2012-10-30 17:40:25 +04:00
bool ignoreOutmostBlock /*=false*/ ) {
2012-11-09 14:54:07 +04:00
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 ) ;
char magic [ 1024 ] = { 0 } ;
in . read ( magic , sizeof ( magic ) ) ;
size_t readMagic = in . gcount ( ) ;
if ( readMagic > = 4 ) {
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 ) ;
2013-09-19 19:05:28 +04:00
if ( fileType ! = llvm : : sys : : fs : : file_magic : : unknown ) {
2012-11-21 00:21:29 +04:00
llvm : : errs ( ) < < " Error in cling::MetaProcessor: "
" cannot read input from a binary file! \n " ;
return Interpreter : : kFailure ;
}
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.
llvm : : errs ( ) < < " Error in cling::MetaProcessor: "
" cannot read input from a (likely) binary file! \n " < < printable ;
return Interpreter : : kFailure ;
}
}
}
2012-10-30 17:40:25 +04:00
std : : ifstream in ( filename . str ( ) . c_str ( ) ) ;
2012-11-09 14:54:07 +04:00
in . seekg ( 0 , std : : ios : : end ) ;
size_t size = in . tellg ( ) ;
std : : string content ( size , ' ' ) ;
in . seekg ( 0 ) ;
in . read ( & content [ 0 ] , size ) ;
if ( ignoreOutmostBlock & & ! content . empty ( ) ) {
static const char whitespace [ ] = " \t \r \n " ;
std : : string : : size_type posNonWS = content . find_first_not_of ( whitespace ) ;
2014-04-08 19:03:28 +04:00
// Handle comments before leading {
while ( content [ posNonWS ] = = ' / ' & & content [ posNonWS + 1 ] = = ' / ' ) {
// Remove the comment line
2014-04-09 12:37:15 +04:00
posNonWS = content . find_first_of ( ' \n ' , posNonWS + 2 ) + 1 ;
2014-04-08 19:03:28 +04:00
}
2012-12-09 14:07:41 +04:00
std : : string : : size_type replaced = posNonWS ;
2012-10-30 17:40:25 +04:00
if ( posNonWS ! = std : : string : : npos ) {
2012-11-09 14:54:07 +04:00
if ( content [ posNonWS ] = = ' { ' ) {
2012-10-30 17:40:25 +04:00
// hide the curly brace:
2012-11-09 14:54:07 +04:00
content [ posNonWS ] = ' ' ;
2012-10-30 17:40:25 +04:00
// and the matching closing '}'
2012-11-09 14:54:07 +04:00
posNonWS = content . find_last_not_of ( whitespace ) ;
2012-10-30 17:40:25 +04:00
if ( posNonWS ! = std : : string : : npos ) {
2012-11-09 14:54:07 +04:00
if ( content [ posNonWS ] = = ' ; ' & & content [ posNonWS - 1 ] = = ' } ' ) {
content [ posNonWS - - ] = ' ' ; // replace ';' and enter next if
}
if ( content [ posNonWS ] = = ' } ' ) {
content [ posNonWS ] = ' ' ; // replace '}'
} else {
2014-04-08 19:03:28 +04:00
std : : string : : size_type posComment = content . find_last_of ( ' } ' ) ;
if ( content [ posComment ] = = ' } ' ) {
content [ posComment ] = ' ' ; // replace '}'
}
posComment = content . find_first_not_of ( whitespace , posComment ) ;
if ( content [ posComment ] = = ' / ' & & content [ posComment + 1 ] = = ' / ' ) {
2012-12-09 14:07:41 +04:00
// 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 })
2014-04-08 19:03:28 +04:00
while ( posComment < = posNonWS ) {
content [ posComment + + ] = ' ' ; // replace '}' and comment
}
} else {
content [ replaced ] = ' { ' ;
// 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 '}'
2012-11-09 14:54:07 +04:00
} // find '}'
} // have '{'
} // have non-whitespace
} // ignore outmost block
2012-11-21 00:21:29 +04:00
std : : string strFilename ( filename . str ( ) ) ;
m_CurrentlyExecutingFile = strFilename ;
bool topmost = ! m_TopExecutingFile . data ( ) ;
if ( topmost )
m_TopExecutingFile = m_CurrentlyExecutingFile ;
2013-06-10 17:14:36 +04:00
Interpreter : : CompilationResult ret ;
if ( process ( content . c_str ( ) , ret , result ) ) {
2013-05-17 13:17:08 +04:00
// Input file has to be complete.
llvm : : errs ( )
< < " Error in cling::MetaProcessor: file "
< < llvm : : sys : : path : : filename ( filename )
< < " is incomplete (missing parenthesis or similar)! \n " ;
ret = Interpreter : : kFailure ;
}
2012-11-21 00:21:29 +04:00
m_CurrentlyExecutingFile = llvm : : StringRef ( ) ;
if ( topmost )
m_TopExecutingFile = llvm : : StringRef ( ) ;
2012-10-30 17:40:25 +04:00
return ret ;
}
2014-01-20 16:51:51 +04:00
void MetaProcessor : : setFileStream ( llvm : : StringRef file , bool append , int fd ,
llvm : : SmallVector < llvm : : SmallString < 128 > , 2 > & prevFileStack ) {
// If we have a fileName to redirect to store it.
if ( ! file . empty ( ) ) {
prevFileStack . push_back ( file ) ;
// pop and push a null terminating 0.
// SmallVectorImpl<T> does not have a c_str(), thus instead of casting to
// a SmallString<T> we null terminate the data that we have and pop the
// 0 char back.
prevFileStack . back ( ) . push_back ( 0 ) ;
prevFileStack . back ( ) . pop_back ( ) ;
if ( ! append ) {
FILE * f ;
if ( ! ( f = fopen ( file . data ( ) , " w " ) ) ) {
llvm : : errs ( ) < < " cling::MetaProcessor::setFileStream: "
" The file path " < < file . data ( ) < < " is not valid. " ;
} else {
fclose ( f ) ;
2014-01-15 19:58:49 +04:00
}
}
2014-01-20 16:51:51 +04:00
// Else unredirection, so switch to the previous file.
} else {
// If there is no previous file on the stack we pop the file
if ( ! prevFileStack . empty ( ) ) {
prevFileStack . pop_back ( ) ;
}
}
2014-01-15 19:58:49 +04:00
}
2013-12-18 18:51:27 +04:00
void MetaProcessor : : setStdStream ( llvm : : StringRef file ,
2014-01-13 11:57:12 +04:00
RedirectionScope stream , bool append ) {
2014-01-14 12:58:35 +04:00
if ( stream & kSTDOUT ) {
2014-01-20 16:51:51 +04:00
setFileStream ( file , append , STDOUT_FILENO , m_PrevStdoutFileName ) ;
2013-12-18 18:51:27 +04:00
}
2014-01-14 12:58:35 +04:00
if ( stream & kSTDERR ) {
2014-01-20 16:51:51 +04:00
setFileStream ( file , append , STDERR_FILENO , m_PrevStderrFileName ) ;
}
}
int MetaProcessor : : copyFileDescriptor ( int fd ) {
int backupFD = dup ( fd ) ;
if ( backupFD < 0 ) {
llvm : : errs ( ) < < " MetaProcessor::copyFileDescriptor: Duplicating the file "
" descriptor " < < fd < < " resulted in an error. "
" Will not be able to unredirect. " ;
2013-12-18 18:51:27 +04:00
}
2014-01-20 16:51:51 +04:00
return backupFD ;
2013-12-18 18:51:27 +04:00
}
2012-10-30 17:40:25 +04:00
} // end namespace cling