cling/lib/Interpreter/InterpreterCallbacks.cpp
Vassil Vassilev 3bcfc8b25e Prepare dynamic scopes for integration in ROOT;
* Merge the DynamicIDHandler into InterpreterCallbacks. The DynamicIDHandler was
used to define the failed lookup names as dependent, so that later on they could
be "healed" by the EvaluateTSynthesizer - by generating a call to gCling->EvaluateT...

* Add a flag in the InterpreterCallbacks giving a hint when the failed lookup is
performed - during compilation time or during evaluation type, maybe its name is
misnomer. Maybe I should rename it to IsInRuntimeEvalMode or smth like that...

* Simplify the evaluation routine. The idea is to be merged with one of the 
existing - either echo or evaluate

* Implement proper mock object as test dynamic symbol resolver callback. Now it
fully relies on InterpreterCallbacks.

* Implement dynamic expression node marking. Every node that needs runtime 
resolution is marked as dependent. However, clang marks other nodes as dependent,
too (eg. templates). Until now it was very difficult to distinguish between both.
Now every "artificially" (marked by us) node's declaration is annotated so that 
it could be found back easily by using a simple RecursiveASTVisitor.

Generally we have two issues when using dynamic scopes - filtering the failed 
lookups that need to be evaluated at runtime, and filtering the "artificially"
marked as dependent dynamic nodes/decls. Whereas the second issue is solved by
using the annotations, the first one is more tricky to solve because clang doesn't
give us enough information to conclude what should be done. For now I have 
narrowed down the cases so that dynamic nodes can appear only in function decls.


git-svn-id: http://root.cern.ch/svn/root/trunk@48575 27541ba8-7e3a-0410-8455-c3a389f83636
2013-02-14 17:29:30 +00:00

193 lines
6.2 KiB
C++

//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vvasilev@cern.ch>
//------------------------------------------------------------------------------
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/Interpreter.h"
#include "clang/Sema/Sema.h"
using namespace clang;
namespace cling {
// pin the vtable here
InterpreterExternalSemaSource::~InterpreterExternalSemaSource() {}
bool InterpreterExternalSemaSource::LookupUnqualified(LookupResult& R,
Scope* S) {
if (m_Callbacks)
return m_Callbacks->LookupObject(R, S);
return false;
}
InterpreterCallbacks::InterpreterCallbacks(Interpreter* interp,
InterpreterExternalSemaSource* IESS)
: m_Interpreter(interp), m_SemaExternalSource(IESS), m_IsRuntime(false) {
if (!IESS)
m_SemaExternalSource.reset(new InterpreterExternalSemaSource(this));
m_Interpreter->getSema().addExternalSource(m_SemaExternalSource.get());
}
// pin the vtable here
InterpreterCallbacks::~InterpreterCallbacks() {
// FIXME: we have to remove the external source at destruction time. Needs
// further tweaks of the patch in clang. This will be done later once the
// patch is in clang's mainline.
}
bool InterpreterCallbacks::LookupObject(LookupResult&, Scope*) {
return false;
}
} // end namespace cling
// TODO: Make the build system in the testsuite aware how to build that class
// and extract it out there again.
#include "DynamicLookup.h"
#include "cling/Utils/AST.h"
#include "clang/AST/ASTContext.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Scope.h"
namespace cling {
namespace test {
TestProxy* Tester = 0;
extern "C" int printf(const char* fmt, ...);
TestProxy::TestProxy(){}
int TestProxy::Draw(){ return 12; }
const char* TestProxy::getVersion(){ return "Interpreter.cpp"; }
int TestProxy::Add10(int num) { return num + 10;}
int TestProxy::Add(int a, int b) {
return a + b;
}
void TestProxy::PrintString(std::string s) { printf("%s\n", s.c_str()); }
bool TestProxy::PrintArray(int a[], size_t size) {
for (unsigned i = 0; i < size; ++i)
printf("%i", a[i]);
printf("%s", "\n");
return true;
}
void TestProxy::PrintArray(float a[][5], size_t size) {
for (unsigned i = 0; i < size; ++i)
for (unsigned j = 0; j < 5; ++j)
printf("%i", (int)a[i][j]);
printf("%s", "\n");
}
void TestProxy::PrintArray(int a[][4][5], size_t size) {
for (unsigned i = 0; i < size; ++i)
for (unsigned j = 0; j < 4; ++j)
for (unsigned k = 0; k < 5; ++k)
printf("%i", a[i][j][k]);
printf("%s", "\n");
}
SymbolResolverCallback::SymbolResolverCallback(Interpreter* interp)
: InterpreterCallbacks(interp), m_TesterDecl(0) {
m_Interpreter->process("cling::test::Tester = new cling::test::TestProxy();");
}
SymbolResolverCallback::~SymbolResolverCallback() { }
bool SymbolResolverCallback::LookupObject(LookupResult& R, Scope* S) {
if (m_IsRuntime) {
// Only for demo resolve all unknown objects to cling::test::Tester
if (!m_TesterDecl) {
clang::Sema& SemaR = m_Interpreter->getSema();
clang::NamespaceDecl* NSD = utils::Lookup::Namespace(&SemaR, "cling");
NSD = utils::Lookup::Namespace(&SemaR, "test", NSD);
m_TesterDecl = utils::Lookup::Named(&SemaR, "Tester", NSD);
}
assert (m_TesterDecl && "Tester not found!");
R.addDecl(m_TesterDecl);
return true; // Tell clang to continue.
}
if (ShouldResolveAtRuntime(R, S)) {
ASTContext& C = R.getSema().getASTContext();
DeclContext* DC = 0;
// For DeclContext-less scopes like if (dyn_expr) {}
while (!DC) {
DC = static_cast<DeclContext*>(S->getEntity());
S = S->getParent();
}
DeclarationName Name = R.getLookupName();
IdentifierInfo* II = Name.getAsIdentifierInfo();
SourceLocation Loc = R.getNameLoc();
VarDecl* Res = VarDecl::Create(C, DC, Loc, Loc, II, C.DependentTy,
/*TypeSourceInfo*/0, SC_None, SC_None);
// Annotate the decl to give a hint in cling. FIXME: Current implementation
// is a gross hack, because TClingCallbacks shouldn't know about
// EvaluateTSynthesizer at all!
SourceRange invalidRange;
Res->addAttr(new (C) AnnotateAttr(invalidRange, C, "__ResolveAtRuntime"));
R.addDecl(Res);
DC->addDecl(Res);
// Say that we can handle the situation. Clang should try to recover
return true;
}
return false;
}
bool SymbolResolverCallback::ShouldResolveAtRuntime(LookupResult& R,
Scope* S) {
if (R.getLookupKind() != Sema::LookupOrdinaryName)
return false;
if (R.isForRedeclaration())
return false;
if (!R.empty())
return false;
// FIXME: Figure out better way to handle:
// C++ [basic.lookup.classref]p1:
// In a class member access expression (5.2.5), if the . or -> token is
// immediately followed by an identifier followed by a <, the
// identifier must be looked up to determine whether the < is the
// beginning of a template argument list (14.2) or a less-than operator.
// The identifier is first looked up in the class of the object
// expression. If the identifier is not found, it is then looked up in
// the context of the entire postfix-expression and shall name a class
// or function template.
//
// We want to ignore object(.|->)member<template>
if (R.getSema().PP.LookAhead(0).getKind() == tok::less)
// TODO: check for . or -> in the cached token stream
return false;
for (Scope* DepScope = S; DepScope; DepScope = DepScope->getParent()) {
if (DeclContext* Ctx = static_cast<DeclContext*>(DepScope->getEntity())) {
if (!Ctx->isDependentContext())
// For now we support only the prompt.
if (isa<FunctionDecl>(Ctx))
return true;
}
}
return false;
}
} // end test
} // end cling