2013-09-05 16:26:23 +02:00
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author: Baozeng Ding <sploving1@gmail.com>
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
2014-01-07 11:08:37 +01: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.
2013-09-05 16:26:23 +02:00
//------------------------------------------------------------------------------
2013-09-11 13:31:14 +02:00
# include "NullDerefProtectionTransformer.h"
2013-09-05 16:26:23 +02:00
# include "cling/Interpreter/Transaction.h"
2013-09-06 23:02:00 +02:00
# include "cling/Utils/AST.h"
2013-09-05 16:26:23 +02:00
# include "clang/AST/ASTContext.h"
2013-09-06 23:02:00 +02:00
# include "clang/AST/Decl.h"
# include "clang/AST/Mangle.h"
2013-09-11 10:08:38 +02:00
# include "clang/AST/StmtVisitor.h"
2013-09-06 23:02:00 +02:00
# include "clang/Basic/SourceLocation.h"
# include "clang/Sema/Lookup.h"
2013-09-05 16:26:23 +02:00
2013-09-11 10:08:38 +02:00
# include <bitset>
# include <map>
2013-09-05 16:26:23 +02:00
using namespace clang ;
namespace cling {
2013-09-11 13:31:14 +02:00
NullDerefProtectionTransformer : : NullDerefProtectionTransformer ( clang : : Sema * S )
2015-03-16 14:49:28 +01:00
: WrapperTransformer ( S ) {
2013-09-05 16:26:23 +02:00
}
2013-09-11 13:31:14 +02:00
NullDerefProtectionTransformer : : ~ NullDerefProtectionTransformer ( )
2013-09-05 16:26:23 +02:00
{ }
2013-09-11 10:08:38 +02:00
// Copied from clad - the clang/opencl autodiff project
class NodeContext {
public :
private :
typedef llvm : : SmallVector < clang : : Stmt * , 2 > Statements ;
Statements m_Stmts ;
private :
NodeContext ( ) { } ;
public :
NodeContext ( clang : : Stmt * s ) { m_Stmts . push_back ( s ) ; }
NodeContext ( clang : : Stmt * s0 , clang : : Stmt * s1 ) {
m_Stmts . push_back ( s0 ) ;
m_Stmts . push_back ( s1 ) ;
}
2013-09-05 16:26:23 +02:00
2013-09-11 10:08:38 +02:00
bool isSingleStmt ( ) const { return m_Stmts . size ( ) = = 1 ; }
2014-08-03 21:05:42 -05:00
2013-09-11 10:08:38 +02:00
clang : : Stmt * getStmt ( ) {
assert ( isSingleStmt ( ) & & " Cannot get multiple stmts. " ) ;
return m_Stmts . front ( ) ;
}
const clang : : Stmt * getStmt ( ) const { return getStmt ( ) ; }
const Statements & getStmts ( ) const {
return m_Stmts ;
}
2013-09-05 16:26:23 +02:00
2013-09-11 10:08:38 +02:00
CompoundStmt * wrapInCompoundStmt ( clang : : ASTContext & C ) const {
assert ( ! isSingleStmt ( ) & & " Must be more than 1 " ) ;
llvm : : ArrayRef < Stmt * > stmts
= llvm : : makeArrayRef ( m_Stmts . data ( ) , m_Stmts . size ( ) ) ;
clang : : SourceLocation noLoc ;
return new ( C ) clang : : CompoundStmt ( C , stmts , noLoc , noLoc ) ;
}
2014-08-03 21:05:42 -05:00
2013-09-11 10:08:38 +02:00
clang : : Expr * getExpr ( ) {
assert ( llvm : : isa < clang : : Expr > ( getStmt ( ) ) & & " Must be an expression. " ) ;
return llvm : : cast < clang : : Expr > ( getStmt ( ) ) ;
}
const clang : : Expr * getExpr ( ) const {
return getExpr ( ) ;
2013-09-05 16:26:23 +02:00
}
2013-09-11 10:08:38 +02:00
void prepend ( clang : : Stmt * S ) {
m_Stmts . insert ( m_Stmts . begin ( ) , S ) ;
2013-09-05 16:26:23 +02:00
}
2013-09-11 10:08:38 +02:00
void append ( clang : : Stmt * S ) {
m_Stmts . push_back ( S ) ;
}
} ;
class IfStmtInjector : public StmtVisitor < IfStmtInjector , NodeContext > {
private :
Sema & m_Sema ;
typedef std : : map < clang : : FunctionDecl * , std : : bitset < 32 > > decl_map_t ;
std : : map < clang : : FunctionDecl * , std : : bitset < 32 > > m_NonNullArgIndexs ;
2013-09-10 18:38:21 +02:00
2013-09-11 10:08:38 +02:00
public :
2014-08-03 21:05:42 -05:00
IfStmtInjector ( Sema & S ) : m_Sema ( S ) { }
2013-09-11 10:08:38 +02:00
CompoundStmt * Inject ( CompoundStmt * CS ) {
2013-09-11 13:18:06 +02:00
NodeContext result = VisitCompoundStmt ( CS ) ;
return cast < CompoundStmt > ( result . getStmt ( ) ) ;
2013-09-11 10:08:38 +02:00
}
2013-09-11 13:18:06 +02:00
2013-09-11 10:08:38 +02:00
NodeContext VisitStmt ( Stmt * S ) {
return NodeContext ( S ) ;
}
NodeContext VisitCompoundStmt ( CompoundStmt * CS ) {
ASTContext & C = m_Sema . getASTContext ( ) ;
llvm : : SmallVector < Stmt * , 16 > stmts ;
for ( CompoundStmt : : body_iterator I = CS - > body_begin ( ) , E = CS - > body_end ( ) ;
I ! = E ; + + I ) {
NodeContext nc = Visit ( * I ) ;
if ( nc . isSingleStmt ( ) )
stmts . push_back ( nc . getStmt ( ) ) ;
else
stmts . append ( nc . getStmts ( ) . begin ( ) , nc . getStmts ( ) . end ( ) ) ;
2013-09-10 18:05:44 +02:00
}
2013-09-11 10:08:38 +02:00
llvm : : ArrayRef < Stmt * > stmtsRef ( stmts . data ( ) , stmts . size ( ) ) ;
2014-08-03 21:05:42 -05:00
CompoundStmt * newCS = new ( C ) CompoundStmt ( C , stmtsRef ,
CS - > getLBracLoc ( ) ,
2013-09-11 13:18:06 +02:00
CS - > getRBracLoc ( ) ) ;
return NodeContext ( newCS ) ;
}
NodeContext VisitIfStmt ( IfStmt * If ) {
NodeContext result ( If ) ;
// check the condition
NodeContext cond = Visit ( If - > getCond ( ) ) ;
if ( ! cond . isSingleStmt ( ) )
result . prepend ( cond . getStmts ( ) [ 0 ] ) ;
return result ;
}
NodeContext VisitCastExpr ( CastExpr * CE ) {
NodeContext result = Visit ( CE - > getSubExpr ( ) ) ;
return result ;
}
NodeContext VisitBinaryOperator ( BinaryOperator * BinOp ) {
NodeContext result ( BinOp ) ;
2014-08-03 21:05:42 -05:00
2013-09-11 13:18:06 +02:00
// Here we might get if(check) throw; binop rhs.
NodeContext rhs = Visit ( BinOp - > getRHS ( ) ) ;
// Here we might get if(check) throw; binop lhs.
NodeContext lhs = Visit ( BinOp - > getLHS ( ) ) ;
2014-08-03 21:05:42 -05:00
// Prepend those checks. It will become:
2013-09-11 13:18:06 +02:00
// if(check_rhs) throw; if (check_lhs) throw; BinOp;
if ( ! rhs . isSingleStmt ( ) ) {
// FIXME:we need to loop from 0 to n-1
result . prepend ( rhs . getStmts ( ) [ 0 ] ) ;
}
if ( ! lhs . isSingleStmt ( ) ) {
// FIXME:we need to loop from 0 to n-1
result . prepend ( lhs . getStmts ( ) [ 0 ] ) ;
}
return result ;
2013-09-11 10:08:38 +02:00
}
NodeContext VisitUnaryOperator ( UnaryOperator * UnOp ) {
NodeContext result ( UnOp ) ;
if ( UnOp - > getOpcode ( ) = = UO_Deref ) {
2014-08-03 21:05:42 -05:00
result . prepend ( SynthesizeCheck ( UnOp - > getLocStart ( ) ,
2013-09-11 10:08:38 +02:00
UnOp - > getSubExpr ( ) ) ) ;
}
return result ;
}
NodeContext VisitMemberExpr ( MemberExpr * ME ) {
2013-09-11 13:18:06 +02:00
NodeContext result ( ME ) ;
2014-08-03 21:05:42 -05:00
if ( ME - > isArrow ( ) ) {
result . prepend ( SynthesizeCheck ( ME - > getLocStart ( ) ,
2013-09-11 13:18:06 +02:00
ME - > getBase ( ) - > IgnoreImplicit ( ) ) ) ;
}
return result ;
2013-09-11 10:08:38 +02:00
}
NodeContext VisitCallExpr ( CallExpr * CE ) {
FunctionDecl * FDecl = CE - > getDirectCallee ( ) ;
NodeContext result ( CE ) ;
if ( FDecl & & isDeclCandidate ( FDecl ) ) {
decl_map_t : : const_iterator it = m_NonNullArgIndexs . find ( FDecl ) ;
const std : : bitset < 32 > & ArgIndexs = it - > second ;
Sema : : ContextRAII pushedDC ( m_Sema , FDecl ) ;
for ( int index = 0 ; index < 32 ; + + index ) {
if ( ArgIndexs . test ( index ) ) {
// Get the argument with the nonnull attribute.
Expr * Arg = CE - > getArg ( index ) ;
result . prepend ( SynthesizeCheck ( Arg - > getLocStart ( ) , Arg ) ) ;
}
}
}
2013-09-11 13:18:06 +02:00
return result ;
2013-09-11 10:08:38 +02:00
}
private :
2013-09-11 23:04:36 +02:00
Stmt * SynthesizeCheck ( SourceLocation Loc , Expr * Arg ) {
2013-09-11 13:18:06 +02:00
assert ( Arg & & " Cannot call with Arg=0 " ) ;
2013-09-11 10:08:38 +02:00
ASTContext & Context = m_Sema . getASTContext ( ) ;
//copied from DynamicLookup.cpp
// Lookup Sema type
CXXRecordDecl * SemaRD
= dyn_cast < CXXRecordDecl > ( utils : : Lookup : : Named ( & m_Sema , " Sema " ,
utils : : Lookup : : Namespace ( & m_Sema , " clang " ) ) ) ;
QualType SemaRDTy = Context . getTypeDeclType ( SemaRD ) ;
Expr * VoidSemaArg = utils : : Synthesize : : CStyleCastPtrExpr ( & m_Sema , SemaRDTy ,
( uint64_t ) & m_Sema ) ;
// Lookup Expr type
CXXRecordDecl * ExprRD
= dyn_cast < CXXRecordDecl > ( utils : : Lookup : : Named ( & m_Sema , " Expr " ,
utils : : Lookup : : Namespace ( & m_Sema , " clang " ) ) ) ;
QualType ExprRDTy = Context . getTypeDeclType ( ExprRD ) ;
Expr * VoidExprArg = utils : : Synthesize : : CStyleCastPtrExpr ( & m_Sema , ExprRDTy ,
( uint64_t ) Arg ) ;
Expr * args [ ] = { VoidSemaArg , VoidExprArg } ;
2013-09-11 13:18:06 +02:00
Scope * S = m_Sema . getScopeForContext ( m_Sema . CurContext ) ;
2014-08-03 21:05:42 -05:00
DeclarationName Name
2013-09-11 23:04:36 +02:00
= & Context . Idents . get ( " cling__runtime__internal__throwNullDerefException " ) ;
SourceLocation noLoc ;
LookupResult R ( m_Sema , Name , noLoc , Sema : : LookupOrdinaryName ,
Sema : : ForRedeclaration ) ;
m_Sema . LookupQualifiedName ( R , Context . getTranslationUnitDecl ( ) ) ;
assert ( ! R . empty ( ) & & " Cannot find valuePrinterInternal::Select(...) " ) ;
CXXScopeSpec CSS ;
Expr * UnresolvedLookup
2014-06-06 10:18:32 +02:00
= m_Sema . BuildDeclarationNameExpr ( CSS , R , /*ADL*/ false ) . get ( ) ;
2013-09-11 10:08:38 +02:00
2013-09-11 23:04:36 +02:00
Expr * call = m_Sema . ActOnCallExpr ( S , UnresolvedLookup , noLoc ,
2014-06-06 10:18:32 +02:00
args , noLoc ) . get ( ) ;
2013-09-11 10:08:38 +02:00
// Check whether we can get the argument'value. If the argument is
// null, throw an exception direclty. If the argument is not null
// then ignore this argument and continue to deal with the next
// argument with the nonnull attribute.
bool Result = false ;
if ( Arg - > EvaluateAsBooleanCondition ( Result , Context ) ) {
if ( ! Result ) {
2013-09-11 23:04:36 +02:00
return call ;
2013-09-11 10:08:38 +02:00
}
return Arg ;
}
// The argument's value cannot be decided, so we add a UnaryOp
// operation to check its value at runtime.
2013-09-11 13:18:06 +02:00
ExprResult ER = m_Sema . ActOnUnaryOp ( S , Loc , tok : : exclaim , Arg ) ;
2013-09-11 10:08:38 +02:00
Decl * varDecl = 0 ;
Stmt * varStmt = 0 ;
2014-06-06 10:18:32 +02:00
Sema : : FullExprArg FullCond ( m_Sema . MakeFullExpr ( ER . get ( ) ) ) ;
2013-09-11 10:08:38 +02:00
StmtResult IfStmt = m_Sema . ActOnIfStmt ( Loc , FullCond , varDecl ,
2013-09-11 23:04:36 +02:00
call , Loc , varStmt ) ;
2014-06-06 10:18:32 +02:00
return IfStmt . get ( ) ;
2013-09-11 10:08:38 +02:00
}
bool isDeclCandidate ( FunctionDecl * FDecl ) {
if ( m_NonNullArgIndexs . count ( FDecl ) )
return true ;
std : : bitset < 32 > ArgIndexs ;
for ( specific_attr_iterator < NonNullAttr >
I = FDecl - > specific_attr_begin < NonNullAttr > ( ) ,
E = FDecl - > specific_attr_end < NonNullAttr > ( ) ; I ! = E ; + + I ) {
NonNullAttr * NonNull = * I ;
for ( NonNullAttr : : args_iterator i = NonNull - > args_begin ( ) ,
e = NonNull - > args_end ( ) ; i ! = e ; + + i ) {
ArgIndexs . set ( * i ) ;
}
}
if ( ArgIndexs . any ( ) ) {
m_NonNullArgIndexs . insert ( std : : make_pair ( FDecl , ArgIndexs ) ) ;
return true ;
}
return false ;
}
} ;
2013-09-10 18:05:44 +02:00
2015-03-16 14:49:28 +01:00
ASTTransformer : : Result
NullDerefProtectionTransformer : : Transform ( clang : : Decl * D ) {
FunctionDecl * FD = dyn_cast < FunctionDecl > ( D ) ;
if ( ! FD | | FD - > isFromASTFile ( ) )
return Result ( D , true ) ;
2013-09-05 16:26:23 +02:00
2015-03-16 14:49:28 +01:00
CompoundStmt * CS = dyn_cast_or_null < CompoundStmt > ( FD - > getBody ( ) ) ;
if ( ! CS )
return Result ( D , true ) ;
IfStmtInjector injector ( * m_Sema ) ;
2013-09-11 13:18:06 +02:00
CompoundStmt * newCS = injector . Inject ( CS ) ;
FD - > setBody ( newCS ) ;
2015-03-16 14:49:28 +01:00
return Result ( FD , true ) ;
2013-09-05 16:26:23 +02:00
}
} // end namespace cling