Move cling from cint/ to interpreter/ (Will add a "we have moved" readme to cint/cling.)

git-svn-id: http://root.cern.ch/svn/root/trunk@45844 27541ba8-7e3a-0410-8455-c3a389f83636
This commit is contained in:
Axel Naumann 2012-09-05 09:37:39 +00:00
commit 05ba8a3a07
185 changed files with 17922 additions and 0 deletions

167
CMakeLists.txt Normal file
View File

@ -0,0 +1,167 @@
# If we are not building as a part of LLVM, build Cling as an
# standalone project, using LLVM as an external library:
if( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR )
project(Cling)
cmake_minimum_required(VERSION 2.8)
set(CLING_PATH_TO_LLVM_SOURCE "" CACHE PATH
"Path to LLVM source code. Not necessary if using an installed LLVM.")
set(CLING_PATH_TO_LLVM_BUILD "" CACHE PATH
"Path to the directory where LLVM was built or installed.")
if( CLING_PATH_TO_LLVM_SOURCE )
if( NOT EXISTS "${CLING_PATH_TO_LLVM_SOURCE}/cmake/config-ix.cmake" )
message(FATAL_ERROR "Please set CLING_PATH_TO_LLVM_SOURCE to the root directory of LLVM source code.")
else()
get_filename_component(LLVM_MAIN_SRC_DIR ${CLING_PATH_TO_LLVM_SOURCE}
ABSOLUTE)
list(APPEND CMAKE_MODULE_PATH "${LLVM_MAIN_SRC_DIR}/cmake/modules")
endif()
endif()
if( NOT EXISTS "${CLING_PATH_TO_LLVM_BUILD}/bin/tblgen${CMAKE_EXECUTABLE_SUFFIX}" )
message(FATAL_ERROR "Please set CLING_PATH_TO_LLVM_BUILD to a directory containing a LLVM build.")
endif()
list(APPEND CMAKE_MODULE_PATH "${CLING_PATH_TO_LLVM_BUILD}/share/llvm/cmake")
get_filename_component(PATH_TO_LLVM_BUILD ${CLING_PATH_TO_LLVM_BUILD}
ABSOLUTE)
include(AddLLVM)
include("${CLING_PATH_TO_LLVM_BUILD}/share/llvm/cmake/LLVMConfig.cmake")
include(HandleLLVMOptions)
set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}")
set(LLVM_MAIN_INCLUDE_DIR "${LLVM_MAIN_SRC_DIR}/include")
set(LLVM_BINARY_DIR ${CMAKE_BINARY_DIR})
set(CMAKE_INCLUDE_CURRENT_DIR ON)
include_directories("${PATH_TO_LLVM_BUILD}/include" "${LLVM_MAIN_INCLUDE_DIR}")
link_directories("${PATH_TO_LLVM_BUILD}/lib")
set(LLVM_TABLEGEN_EXE "${PATH_TO_LLVM_BUILD}/bin/tblgen")
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin )
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
set( CLING_BUILT_STANDALONE 1 )
endif()
set(C_INCLUDE_DIRS "" CACHE STRING
"Colon separated list of directories cling will search for headers.")
set(CLING_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(CLING_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
if( CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR AND NOT MSVC_IDE )
message(FATAL_ERROR "In-source builds are not allowed. CMake would overwrite "
"the makefiles distributed with LLVM. Please create a directory and run cmake "
"from there, passing the path to this source directory as the last argument. "
"This process created the file `CMakeCache.txt' and the directory "
"`CMakeFiles'. Please delete them.")
endif()
# Add appropriate flags for GCC
if (LLVM_COMPILER_IS_GCC_COMPATIBLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-common -Woverloaded-virtual -Wcast-qual -fno-strict-aliasing -pedantic -Wno-long-long -Wall -W -Wno-unused-parameter -Wwrite-strings")
endif ()
if (APPLE)
set(CMAKE_MODULE_LINKER_FLAGS "-Wl,-flat_namespace -Wl,-undefined -Wl,suppress")
endif ()
include(LLVMParseArguments)
macro(add_cling_library name)
llvm_process_sources(srcs ${ARGN})
if(MSVC_IDE OR XCODE)
string( REGEX MATCHALL "/[^/]+" split_path ${CMAKE_CURRENT_SOURCE_DIR})
list( GET split_path -1 dir)
file( GLOB_RECURSE headers
../../include/cling${dir}/*.h
../../include/cling${dir}/*.td
../../include/cling${dir}/*.def)
set(srcs ${srcs} ${headers})
endif(MSVC_IDE OR XCODE)
if (MODULE)
set(libkind MODULE)
elseif (SHARED_LIBRARY)
set(libkind SHARED)
else()
set(libkind)
endif()
add_library( ${name} ${libkind} ${srcs} )
if( LLVM_COMMON_DEPENDS )
add_dependencies( ${name} ${LLVM_COMMON_DEPENDS} )
endif( LLVM_COMMON_DEPENDS )
target_link_libraries( ${name} ${LLVM_USED_LIBS} )
llvm_config( ${name} ${LLVM_LINK_COMPONENTS} )
target_link_libraries( ${name} ${LLVM_COMMON_LIBS} )
link_system_libs( ${name} )
if(MSVC)
get_target_property(cflag ${name} COMPILE_FLAGS)
if(NOT cflag)
set(cflag "")
endif(NOT cflag)
set(cflag "${cflag} /Za")
set_target_properties(${name} PROPERTIES COMPILE_FLAGS ${cflag})
endif(MSVC)
install(TARGETS ${name}
LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX}
ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX})
set_target_properties(${name} PROPERTIES FOLDER "Cling libraries")
endmacro(add_cling_library)
macro(add_cling_executable name)
add_llvm_executable( ${name} ${ARGN} )
set_target_properties(${name} PROPERTIES FOLDER "Cling executables")
endmacro(add_cling_executable)
include_directories(BEFORE
${CMAKE_CURRENT_BINARY_DIR}/../clang/include
${CMAKE_CURRENT_SOURCE_DIR}/../clang/include
)
include_directories(BEFORE
${CMAKE_CURRENT_BINARY_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/include
)
install(DIRECTORY include/
DESTINATION include
FILES_MATCHING
PATTERN "*.def"
PATTERN "*.h"
PATTERN ".svn" EXCLUDE
)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/
DESTINATION include
FILES_MATCHING
PATTERN "CMakeFiles" EXCLUDE
PATTERN "*.inc"
)
add_definitions( -D_GNU_SOURCE
-DCLING_SRCDIR_INCL="${CLING_SOURCE_DIR}/include"
-DCLING_INSTDIR_INCL="${CLING_BINARY_DIR}/include" )
add_subdirectory(lib)
add_subdirectory(tools)
# TODO: docs.
#add_subdirectory(test)
# Workaround for MSVS10 to avoid the Dialog Hell
# FIXME: This could be removed with future version of CMake.
if( CLING_BUILT_STANDALONE AND MSVC_VERSION EQUAL 1600 )
set(CLING_SLN_FILENAME "${CMAKE_CURRENT_BINARY_DIR}/Cling.sln")
if( EXISTS "${CLING_SLN_FILENAME}" )
file(APPEND "${CLING_SLN_FILENAME}" "\n# This should be regenerated!\n")
endif()
endif()

View File

@ -0,0 +1 @@
162891

111
Makefile Normal file
View File

@ -0,0 +1,111 @@
##===- Makefile --------------------------------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
# If CLING_LEVEL is not set, then we are the top-level Makefile. Otherwise, we
# are being included from a subdirectory makefile.
ifndef CLING_LEVEL
IS_TOP_LEVEL := 1
CLING_LEVEL := .
DIRS := include lib tools docs
PARALLEL_DIRS :=
ifeq ($(BUILD_EXAMPLES),1)
PARALLEL_DIRS += examples
endif
endif
ifeq ($(MAKECMDGOALS),libs-only)
DIRS := $(filter-out tools docs, $(DIRS))
OPTIONAL_DIRS :=
endif
###
# Common Makefile code, shared by all Cling Makefiles.
# Set LLVM source root level.
LEVEL := $(CLING_LEVEL)/../..
# Include LLVM common makefile.
include $(LEVEL)/Makefile.common
ifneq ($(ENABLE_DOCS),1)
DIRS := $(filter-out docs, $(DIRS))
endif
# Set common Cling build flags.
CPP.Flags += -I$(PROJ_SRC_DIR)/$(CLING_LEVEL)/include -I$(PROJ_OBJ_DIR)/$(CLING_LEVEL)/include
ifdef CLING_VENDOR
CPP.Flags += -DCLING_VENDOR='"$(CLING_VENDOR) "'
endif
# Disable -fstrict-aliasing. Darwin disables it by default (and LLVM doesn't
# work with it enabled with GCC), Cling/llvm-gc don't support it yet, and newer
# GCC's have false positive warnings with it on Linux (which prove a pain to
# fix). For example:
# http://gcc.gnu.org/PR41874
# http://gcc.gnu.org/PR41838
#
# We can revisit this when LLVM/Cling support it.
CXX.Flags += -fno-strict-aliasing
###
# Cling Top Level specific stuff.
ifeq ($(IS_TOP_LEVEL),1)
ifneq ($(PROJ_SRC_ROOT),$(PROJ_OBJ_ROOT))
$(RecursiveTargets)::
$(Verb) if [ ! -f test/Makefile ]; then \
$(MKDIR) test; \
$(CP) $(PROJ_SRC_DIR)/test/Makefile test/Makefile; \
fi
endif
test::
@ $(MAKE) -C test
report::
@ $(MAKE) -C test report
clean::
@ $(MAKE) -C test clean
libs-only: all
tags::
$(Verb) etags `find . -type f -name '*.h' -or -name '*.cpp' | \
grep -v /lib/Headers | grep -v /test/`
cscope.files:
find tools lib include -name '*.cpp' \
-or -name '*.def' \
-or -name '*.td' \
-or -name '*.h' > cscope.files
CurrentSVNRev := $(shell svn info $(PROJ_SRC_ROOT)/Makefile.common | grep -E '^Revision: ' | sed 's,^Revision: ,,')
ifneq ($(CurrentSVNRev),)
LastKnownGoodSVNFile := $(PROJ_SRC_ROOT)/tools/cling/LastKnownGoodLLVMSVNRevision.txt
PreviousSVNRev := $(shell cat $(LastKnownGoodSVNFile))
ifneq (skip,$(shell python -c 'if $(PreviousSVNRev) >= $(CurrentSVNRev): print "skip"'))
# revisions are not the same, update the file.
Makefile: $(LastKnownGoodSVNFile)
$(LastKnownGoodSVNFile): ALWAYS
@echo $(CurrentSVNRev) > $@
.PHONY: ALWAYS
endif # ifneq (skip,...
endif # ifneq ($(CurrentSVNRev),)
.PHONY: test report clean cscope.files
endif

83
Module.mk Normal file
View File

@ -0,0 +1,83 @@
# Module.mk for cling module
# Copyright (c) 2011 Rene Brun and Fons Rademakers
#
# Author: Axel Naumann, 2011-10-18
MODNAME := cling
MODDIR := $(ROOT_SRCDIR)/interpreter/$(MODNAME)
CLINGDIR := $(MODDIR)
##### libCling #####
CLINGS := $(wildcard $(MODDIR)/lib/Interpreter/*.cpp) \
$(wildcard $(MODDIR)/lib/MetaProcessor/*.cpp) \
$(wildcard $(MODDIR)/lib/Utils/*.cpp)
CLINGO := $(call stripsrc,$(CLINGS:.cpp=.o))
CLINGDEP := $(CLINGO:.o=.d)
CLINGETC := $(addprefix etc/cling/Interpreter/,RuntimeUniverse.h ValuePrinter.h ValuePrinterInfo.h) \
$(addprefix etc/cling/cint/,multimap multiset)
# used in the main Makefile
ALLHDRS += $(CLINGETC)
# include all dependency files
INCLUDEFILES += $(CLINGDEP)
# include dir for picking up RuntimeUniverse.h etc - need to
# 1) copy relevant headers to include/
# 2) rely on TCling to addIncludePath instead of using CLING_..._INCL below
CLINGCXXFLAGS = $(shell $(LLVMCONFIG) --cxxflags) -I$(CLINGDIR)/include \
-fno-strict-aliasing
CLINGLIBEXTRA = -L$(shell $(LLVMCONFIG) --libdir) \
$(addprefix -lclang,\
Frontend Serialization Driver CodeGen Parse Sema Analysis Rewrite AST Lex Basic Edit) \
$(patsubst -lLLVM%Disassembler,,\
$(filter-out -lLLVMipa,\
$(shell $(LLVMCONFIG) --libs linker jit executionengine debuginfo \
archive bitreader all-targets codegen selectiondag asmprinter \
mcparser scalaropts instcombine transformutils analysis target))) \
$(shell $(LLVMCONFIG) --ldflags)
##### local rules #####
.PHONY: all-$(MODNAME) clean-$(MODNAME) distclean-$(MODNAME)
all-$(MODNAME):
clean-$(MODNAME):
@rm -f $(CLINGO)
clean:: clean-$(MODNAME)
distclean-$(MODNAME): clean-$(MODNAME)
@rm -f $(CLINGDEP) $(CLINGETC)
distclean:: distclean-$(MODNAME)
etc/cling/%.h: $(CLINGDIR)/include/cling/%.h
+@[ -d $(dir $@) ] || mkdir -p $(dir $@)
@cp $< $@
etc/cling/%.h: $(call stripsrc,$(CLINGDIR)/%.o)/include/cling/%.h
+@[ -d $(dir $@) ] || mkdir -p $(dir $@)
@cp $< $@
etc/cling/cint/multimap: $(CLINGDIR)/include/cling/cint/multimap
+@[ -d $(dir $@) ] || mkdir -p $(dir $@)
@cp $< $@
etc/cling/cint/multiset: $(CLINGDIR)/include/cling/cint/multiset
+@[ -d $(dir $@) ] || mkdir -p $(dir $@)
@cp $< $@
$(CLINGDIR)/%.o: $(CLINGDIR)/%.cpp $(LLVMDEP)
$(MAKEDEP) -R -f$(@:.o=.d) -Y -w 1000 -- $(CXXFLAGS) $(CLINGCXXFLAGS) -D__cplusplus -- $<
$(CXX) $(OPT) $(CLINGCXXFLAGS) $(CXXOUT)$@ -c $<
$(call stripsrc,$(CLINGDIR)/%.o): $(CLINGDIR)/%.cpp $(LLVMDEP)
$(MAKEDIR)
$(MAKEDEP) -R -f$(@:.o=.d) -Y -w 1000 -- $(CXXFLAGS) $(CLINGCXXFLAGS) -D__cplusplus -- $<
$(CXX) $(OPT) $(CLINGCXXFLAGS) $(CXXOUT)$@ -c $<
##### extra rules ######

70
README Normal file
View File

@ -0,0 +1,70 @@
_ _ _ _ _ _ _ _ _ _ _ _
_ (_)(_)(_) _ (_) (_)(_)(_) (_) _ (_) _ (_)(_)(_) _
(_) (_) (_) (_) (_)(_)_ (_) (_) (_)
(_) (_) (_) (_) (_)_ (_) (_) _ _ _
(_) (_) (_) (_) (_)_ (_) (_) (_)(_)(_)
(_) _ (_) (_) (_) (_)(_) (_) (_)
(_) _ _ _ (_) (_) _ _ _ (_) _ (_) (_) (_) _ _ _ (_)
(_)(_)(_) (_)(_)(_) (_)(_)(_) (_) (_) (_)(_)(_)(_)
--------------------------------------------------------------------------------
1. I N S T A L L A T I O N I N S T R U C T I O N S
1.1 Dependencies
CLING source depends on the LLVM [1] and CLANG [2] headers and libraries.
You will also need CMake [3] >= 2.6.1 or GNU Make to build all of those
packages and subversion [4] to get the source code.
[1] http://llvm.org
[2] http://clang.llvm.org
[3] http://cmake.org
[4] http://subversion.tigris.org/
1.2 Building
To build LLVM and CLANG you must:
* Check out the sources:
mkdir src
cd src
svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
cd llvm/tools
svn co http://llvm.org/svn/llvm-project/cfe/trunk clang
svn co http://root.cern.ch/svn/root/trunk/interpreter/cling cling
cd ..
* Apply some patches
cat tools/cling/patches/* | patch -p0
* Configure, build and install them, either using CMake:
cd ..
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=/some/install/dir \
-DLLVM_TARGETS_TO_BUILD=CBackend\;CppBackend\;X86 \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_CXX_COMPILER=`which c++` \
-DCMAKE_C_COMPILER=`which gcc` \
../llvm
make
make install
or GNU Make (see ../src/configure --help for all options):
cd ..
mkdir build
cd build
../llvm/configure --prefix=/some/install/dir
make
make install
2. U S A G E
To interpret a C file type: "./cling test.c". To invoke cling in the
interactive mode type "cling" without file name. Use ".L filename" to
load libraries or C++ files. Type C++ code at the prompt to interpret it.

View File

@ -0,0 +1,23 @@
// The demo shows ambiguities in the virtual tables and the nice, expressive
// and accurate errors from clang.
// Author: Vassil Vassilev <vvasilev@cern.ch>
class Person {
public:
virtual Person* Clone() const;
};
class Student : public Person {
public:
};
class Teacher : public Person {
public:
};
class TeachingAssistant : public Student, Teacher {
public:
virtual TeachingAssistant* Clone() const;
};
void Ambiguities() {}

View File

@ -0,0 +1,10 @@
// The demo shows that cling (clang) can report even the column number of the
// error and emit caret
// Author: Vassil Vassilev <vvasilev@cern.ch>
#include <stdio.h>
void CaretDiagnostics() {
int i = 5;
printf("%.*d\n",i);
}

View File

@ -0,0 +1,13 @@
// The demo shows the Fix-it hints that try to guess what the user had meant
// when he did the error
// Author: Vassil Vassilev <vvasilev@cern.ch>
struct Point {
float x;
float y;
};
void FixItHints() {
struct Point origin = { x: 0.0, y: 0.0 };
floot p;
}

View File

@ -0,0 +1,15 @@
// The demo shows if there was an error in a macro, the expansion location
// macro definition is shown
// Author: Vassil Vassilev <vvasilev@cern.ch>
#define MAX(A, B) ((A) > (B) ? (A) : (B))
struct A {
int Y;
};
void MacroExpansionInformation () {
int X = 1;
A* SomeA = new A();
X = MAX(X, *SomeA);
}

View File

@ -0,0 +1,12 @@
// The demo shows the Fix-it hints that try to guess what the user had meant
// when he did the error
// Author: Vassil Vassilev <vvasilev@cern.ch>
struct A {
int X;
};
int PreciseWording () {
A SomeA;
int y = *SomeA.X;
}

View File

@ -0,0 +1,20 @@
// The demo shows when there is an error in a subexpression the relevant
// subexpression is highlighted
// Author: Vassil Vassilev <vvasilev@cern.ch>
#include <stdio.h>
struct A {
int X;
};
int func(int val) {
printf("%d\n",val);
return val - 10;
}
int RangeHighlight() {
A SomeA;
int y = SomeA.X;
return y + func(y ? ((SomeA.X + 40) + SomeA) / 42 + SomeA.X : SomeA.X);
}

7
demo/README Normal file
View File

@ -0,0 +1,7 @@
AUTHOR: Vassil Vassilev <vvasilev@cern.ch>
Here are tiny examples of the strengths of cling and the underlying compiler
clang.
If you want to see them start cling and do .x filename or .L filename,
unless this README is overriden by another, deeper in the folders.

104
docs/Makefile Normal file
View File

@ -0,0 +1,104 @@
##===- docs/Makefile ---------------------------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
CLING_LEVEL := ..
PROJ_OBJ_DIR = .
include $(CLING_LEVEL)/Makefile
DOXYGEN = doxygen
PROJ_docsdir := $(PROJ_docsdir)/cling
HTML := $(wildcard $(PROJ_SRC_DIR)/doxygen/html/*.html) \
$(wildcard $(PROJ_SRC_DIR)/*.css)
#IMAGES := $(wildcard $(PROJ_SRC_DIR)/img/*.*)
DOXYFILES := doxygen.cfg.in doxygen.css doxygen.footer doxygen.header \
doxygen.intro
EXTRA_DIST := $(HTML) $(DOXYFILES) llvm.css CommandGuide img
.PHONY: install-html install-doxygen doxygen generated
install_targets :=
ifndef ONLY_MAN_DOCS
install_targets += install-html
endif
ifeq ($(ENABLE_DOXYGEN),1)
install_targets += install-doxygen
endif
install-local:: $(install_targets)
# Live documentation is generated for the web site using this target:
# 'make generated BUILD_FOR_WEBSITE=1'
generated:: doxygen
install-html: $(PROJ_OBJ_DIR)/html.tar.gz
$(Verb) if test -d $(PROJ_OBJ_DIR)/doxygen ; then \
echo Installing HTML documentation ;\
$(MKDIR) $(DESTDIR)$(PROJ_docsdir)/html; \
$(MKDIR) $(DESTDIR)$(PROJ_docsdir)/html/img; \
$(DataInstall) $(HTML) $(DESTDIR)$(PROJ_docsdir)/html; \
$(DataInstall) $(PROJ_OBJ_DIR)/html.tar.gz $(DESTDIR)$(PROJ_docsdir); \
fi
$(PROJ_OBJ_DIR)/html.tar.gz: $(HTML)
$(Verb) if test -d $(PROJ_OBJ_DIR)/doxygen ; then \
echo Packaging HTML documentation; \
$(RM) -rf $@ $(PROJ_OBJ_DIR)/html.tar; \
cd $(PROJ_OBJ_DIR) && \
$(TAR) cf html.tar doxygen/html/; \
$(GZIPBIN) $(PROJ_OBJ_DIR)/html.tar; \
fi
install-doxygen: doxygen
$(Echo) Installing doxygen documentation
$(Verb) $(MKDIR) $(DESTDIR)$(PROJ_docsdir)/html/doxygen
$(Verb) $(DataInstall) $(PROJ_OBJ_DIR)/doxygen.tar.gz $(DESTDIR)$(PROJ_docsdir)
$(Verb) cd $(PROJ_OBJ_DIR)/doxygen && \
$(FIND) . -type f -exec \
$(DataInstall) {} $(DESTDIR)$(PROJ_docsdir)/html/doxygen \;
doxygen: regendoc $(PROJ_OBJ_DIR)/doxygen.tar.gz
regendoc: gen-doxygen-cfg
$(Echo) Building doxygen documentation
$(Verb) if test -e $(PROJ_OBJ_DIR)/doxygen ; then \
$(RM) -rf $(PROJ_OBJ_DIR)/doxygen ; \
fi
$(Verb) $(DOXYGEN) $(PROJ_OBJ_DIR)/doxygen.cfg
$(Verb) sed -i "s/[$$]LatestRev[$$]/`svnversion $(PROJ_SRC_DIR)`/g" \
$(PROJ_OBJ_DIR)/doxygen/html/*.html
$(PROJ_OBJ_DIR)/doxygen.tar.gz: $(DOXYFILES) $(PROJ_OBJ_DIR)/doxygen.cfg
$(Echo) Packaging doxygen documentation
$(Verb) $(RM) -rf $@ $(PROJ_OBJ_DIR)/doxygen.tar
$(Verb) $(TAR) cf $(PROJ_OBJ_DIR)/doxygen.tar doxygen
$(Verb) $(GZIPBIN) $(PROJ_OBJ_DIR)/doxygen.tar
$(Verb) $(CP) $(PROJ_OBJ_DIR)/doxygen.tar.gz $(PROJ_OBJ_DIR)/doxygen/html/
gen-doxygen-cfg:
$(Echo) Generating doxygen configuration fil
$(Echo) $(PROJ_OBJ_DIR)/doxygen.cfg
cat $(PROJ_SRC_DIR)/doxygen.cfg.in | sed \
-e 's,@docs_srcdir@,'$(PROJ_SRC_DIR)",g" \
-e 's,@cling_srcdir@,'$(call realpath, $(PROJ_SRC_DIR)/$(CLING_LEVEL))",g" \
-e 's/@DOT@/dot/g' \
-e 's/@PACKAGE_VERSION@/mainline/' \
-e 's/@docs_builddir@/./g' > $(PROJ_OBJ_DIR)/doxygen.cfg
userloc: $(LLVM_SRC_ROOT)/docs/userloc.html
$(LLVM_SRC_ROOT)/docs/userloc.html:
$(Echo) Making User LOC Table
$(Verb) cd $(LLVM_SRC_ROOT) ; ./utils/userloc.pl -details -recurse \
-html lib include tools runtime utils examples autoconf test > docs/userloc.html
uninstall-local::
$(Echo) Uninstalling Documentation
$(Verb) $(RM) -rf $(DESTDIR)$(PROJ_docsdir)

1562
docs/doxygen.cfg.in Normal file

File diff suppressed because it is too large Load Diff

408
docs/doxygen.css Normal file
View File

@ -0,0 +1,408 @@
BODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV {
font-family: Verdana,Geneva,Arial,Helvetica,sans-serif;
}
BODY,TD {
font-size: 90%;
}
H1 {
text-align: center;
font-size: 140%;
font-weight: bold;
}
H2 {
font-size: 120%;
font-style: italic;
}
H3 {
font-size: 100%;
}
CAPTION { font-weight: bold }
DIV.qindex {
width: 100%;
background-color: #eeeeff;
border: 1px solid #b0b0b0;
text-align: center;
margin: 2px;
padding: 2px;
line-height: 140%;
}
DIV.nav {
width: 100%;
background-color: #eeeeff;
border: 1px solid #b0b0b0;
text-align: center;
margin: 2px;
padding: 2px;
line-height: 140%;
}
DIV.navtab {
background-color: #eeeeff;
border: 1px solid #b0b0b0;
text-align: center;
margin: 2px;
margin-right: 15px;
padding: 2px;
}
TD.navtab {
font-size: 70%;
}
A.qindex {
text-decoration: none;
font-weight: bold;
color: #1A419D;
}
A.qindex:visited {
text-decoration: none;
font-weight: bold;
color: #1A419D
}
A.qindex:hover {
text-decoration: none;
background-color: #ddddff;
}
A.qindexHL {
text-decoration: none;
font-weight: bold;
background-color: #6666cc;
color: #ffffff;
border: 1px double #9295C2;
}
A.qindexHL:hover {
text-decoration: none;
background-color: #6666cc;
color: #ffffff;
}
A.qindexHL:visited {
text-decoration: none; background-color: #6666cc; color: #ffffff }
A.el { text-decoration: none; font-weight: bold }
A.elRef { font-weight: bold }
A.code:link { text-decoration: none; font-weight: normal; color: #0000FF}
A.code:visited { text-decoration: none; font-weight: normal; color: #0000FF}
A.codeRef:link { font-weight: normal; color: #0000FF}
A.codeRef:visited { font-weight: normal; color: #0000FF}
A:hover { text-decoration: none; background-color: #f2f2ff }
DL.el { margin-left: -1cm }
.fragment {
font-family: Fixed, monospace;
font-size: 95%;
}
PRE.fragment {
border: 1px solid #CCCCCC;
background-color: #f5f5f5;
margin-top: 4px;
margin-bottom: 4px;
margin-left: 2px;
margin-right: 8px;
padding-left: 6px;
padding-right: 6px;
padding-top: 4px;
padding-bottom: 4px;
}
DIV.ah { background-color: black; font-weight: bold; color: #ffffff; margin-bottom: 3px; margin-top: 3px }
TD.md { background-color: #F4F4FB; font-weight: bold; }
TD.mdPrefix {
background-color: #F4F4FB;
color: #606060;
font-size: 80%;
}
TD.mdname1 { background-color: #F4F4FB; font-weight: bold; color: #602020; }
TD.mdname { background-color: #F4F4FB; font-weight: bold; color: #602020; width: 600px; }
DIV.groupHeader {
margin-left: 16px;
margin-top: 12px;
margin-bottom: 6px;
font-weight: bold;
}
DIV.groupText { margin-left: 16px; font-style: italic; font-size: 90% }
BODY {
background: white;
color: black;
margin-right: 20px;
margin-left: 20px;
}
TD.indexkey {
background-color: #eeeeff;
font-weight: bold;
padding-right : 10px;
padding-top : 2px;
padding-left : 10px;
padding-bottom : 2px;
margin-left : 0px;
margin-right : 0px;
margin-top : 2px;
margin-bottom : 2px;
border: 1px solid #CCCCCC;
}
TD.indexvalue {
background-color: #eeeeff;
font-style: italic;
padding-right : 10px;
padding-top : 2px;
padding-left : 10px;
padding-bottom : 2px;
margin-left : 0px;
margin-right : 0px;
margin-top : 2px;
margin-bottom : 2px;
border: 1px solid #CCCCCC;
}
TR.memlist {
background-color: #f0f0f0;
}
P.formulaDsp { text-align: center; }
IMG.formulaDsp { }
IMG.formulaInl { vertical-align: middle; }
SPAN.keyword { color: #008000 }
SPAN.keywordtype { color: #604020 }
SPAN.keywordflow { color: #e08000 }
SPAN.comment { color: #800000 }
SPAN.preprocessor { color: #806020 }
SPAN.stringliteral { color: #002080 }
SPAN.charliteral { color: #008080 }
.mdTable {
border: 1px solid #868686;
background-color: #F4F4FB;
}
.mdRow {
padding: 8px 10px;
}
.mdescLeft {
padding: 0px 8px 4px 8px;
font-size: 80%;
font-style: italic;
background-color: #FAFAFA;
border-top: 1px none #E0E0E0;
border-right: 1px none #E0E0E0;
border-bottom: 1px none #E0E0E0;
border-left: 1px none #E0E0E0;
margin: 0px;
}
.mdescRight {
padding: 0px 8px 4px 8px;
font-size: 80%;
font-style: italic;
background-color: #FAFAFA;
border-top: 1px none #E0E0E0;
border-right: 1px none #E0E0E0;
border-bottom: 1px none #E0E0E0;
border-left: 1px none #E0E0E0;
margin: 0px;
}
.memItemLeft {
padding: 1px 0px 0px 8px;
margin: 4px;
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
border-top-color: #E0E0E0;
border-right-color: #E0E0E0;
border-bottom-color: #E0E0E0;
border-left-color: #E0E0E0;
border-top-style: solid;
border-right-style: none;
border-bottom-style: none;
border-left-style: none;
background-color: #FAFAFA;
font-size: 80%;
}
.memItemRight {
padding: 1px 8px 0px 8px;
margin: 4px;
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
border-top-color: #E0E0E0;
border-right-color: #E0E0E0;
border-bottom-color: #E0E0E0;
border-left-color: #E0E0E0;
border-top-style: solid;
border-right-style: none;
border-bottom-style: none;
border-left-style: none;
background-color: #FAFAFA;
font-size: 80%;
}
.memTemplItemLeft {
padding: 1px 0px 0px 8px;
margin: 4px;
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
border-top-color: #E0E0E0;
border-right-color: #E0E0E0;
border-bottom-color: #E0E0E0;
border-left-color: #E0E0E0;
border-top-style: none;
border-right-style: none;
border-bottom-style: none;
border-left-style: none;
background-color: #FAFAFA;
font-size: 80%;
}
.memTemplItemRight {
padding: 1px 8px 0px 8px;
margin: 4px;
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
border-top-color: #E0E0E0;
border-right-color: #E0E0E0;
border-bottom-color: #E0E0E0;
border-left-color: #E0E0E0;
border-top-style: none;
border-right-style: none;
border-bottom-style: none;
border-left-style: none;
background-color: #FAFAFA;
font-size: 80%;
}
.memTemplParams {
padding: 1px 0px 0px 8px;
margin: 4px;
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
border-top-color: #E0E0E0;
border-right-color: #E0E0E0;
border-bottom-color: #E0E0E0;
border-left-color: #E0E0E0;
border-top-style: solid;
border-right-style: none;
border-bottom-style: none;
border-left-style: none;
color: #606060;
background-color: #FAFAFA;
font-size: 80%;
}
.search { color: #003399;
font-weight: bold;
}
FORM.search {
margin-bottom: 0px;
margin-top: 0px;
}
INPUT.search { font-size: 75%;
color: #000080;
font-weight: normal;
background-color: #eeeeff;
}
TD.tiny { font-size: 75%;
}
a {
color: #252E78;
}
a:visited {
color: #3D2185;
}
.dirtab { padding: 4px;
border-collapse: collapse;
border: 1px solid #b0b0b0;
}
TH.dirtab { background: #eeeeff;
font-weight: bold;
}
HR { height: 1px;
border: none;
border-top: 1px solid black;
}
/*
* LLVM Modifications.
* Note: Everything above here is generated with "doxygen -w htlm" command. See
* "doxygen --help" for details. What follows are CSS overrides for LLVM
* specific formatting. We want to keep the above so it can be replaced with
* subsequent doxygen upgrades.
*/
.footer {
font-size: 80%;
font-weight: bold;
text-align: center;
vertical-align: middle;
}
.title {
font-size: 25pt;
color: black; background: url("../img/lines.gif");
font-weight: bold;
border-width: 1px;
border-style: solid none solid none;
text-align: center;
vertical-align: middle;
padding-left: 8pt;
padding-top: 1px;
padding-bottom: 2px
}
A:link {
cursor: pointer;
text-decoration: none;
font-weight: bolder;
}
A:visited {
cursor: pointer;
text-decoration: underline;
font-weight: bolder;
}
A:hover {
cursor: pointer;
text-decoration: underline;
font-weight: bolder;
}
A:active {
cursor: pointer;
text-decoration: underline;
font-weight: bolder;
font-style: italic;
}
H1 {
text-align: center;
font-size: 140%;
font-weight: bold;
}
H2 {
font-size: 120%;
font-style: italic;
}
H3 {
font-size: 100%;
}
H2, H3 {
border-bottom: 2px solid;
margin-top: 2em;
}
A.qindex {}
A.qindexRef {}
A.el { text-decoration: none; font-weight: bold }
A.elRef { font-weight: bold }
A.code { text-decoration: none; font-weight: normal; color: #4444ee }
A.codeRef { font-weight: normal; color: #4444ee }
div.memitem {
border: 1px solid #999999;
margin-top: 1.0em;
margin-bottom: 1.0em;
-webkit-border-radius: 0.5em;
-webkit-box-shadow: 3px 3px 6px #777777;
-moz-border-radius: 0.5em;
-moz-box-shadow: black 3px 3px 3px;
}
div.memproto {
background-color: #E3E4E5;
padding: 0.25em 0.5em;
-webkit-border-top-left-radius: 0.5em;
-webkit-border-top-right-radius: 0.5em;
-moz-border-radius-topleft: 0.5em;
-moz-border-radius-topright: 0.5em;
}
div.memdoc {
padding-left: 1em;
padding-right: 1em;
}

10
docs/doxygen.footer Normal file
View File

@ -0,0 +1,10 @@
<hr>
<p class="footer">
Generated on $datetime for r$LatestRev$ by <a href="http://www.doxygen.org">Doxygen
$doxygenversion</a>.</p>
<p class="footer">
See the <a href="http://cling.web.cern.ch/cling/">Main Cling Web Page</a> for more
information.</p>
</body>
</html>

9
docs/doxygen.header Normal file
View File

@ -0,0 +1,9 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head>
<meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1"/>
<meta name="keywords" content="cling,clang,LLVM,Low Level Virtual Machine,C,C++,doxygen,API,frontend,documentation"/>
<meta name="description" content="C++ source code API documentation for cling."/>
<title>cling: $title</title>
<link href="doxygen.css" rel="stylesheet" type="text/css"/>
</head><body>
<p class="title">cling API Documentation</p>

15
docs/doxygen.intro Normal file
View File

@ -0,0 +1,15 @@
/// @mainpage cling
///
/// @section main_intro Introduction
/// Welcome to the cling project.
///
/// This documentation describes the @b internal software that makes
/// up cling, not the @b external use of cling. There are no instructions
/// here on how to use cling, only the APIs that make up the software. For
/// usage instructions, please see the programmer's guide or reference
/// manual.
///
/// @section main_caveat Caveat
/// This documentation is generated directly from the source code with doxygen.
/// Since cling is constantly under active development, what you're about to
/// read is out of date!

256
docs/manpage.css Normal file
View File

@ -0,0 +1,256 @@
/* Based on http://www.perldoc.com/css/perldoc.css */
@import url("../llvm.css");
body { font-family: Arial,Helvetica; }
blockquote { margin: 10pt; }
h1, a { color: #336699; }
/*** Top menu style ****/
.mmenuon {
font-family: Arial,Helvetica; font-weight: bold; text-decoration: none;
color: #ff6600; font-size: 10pt;
}
.mmenuoff {
font-family: Arial,Helvetica; font-weight: bold; text-decoration: none;
color: #ffffff; font-size: 10pt;
}
.cpyright {
font-family: Arial,Helvetica; font-weight: bold; text-decoration: none;
color: #ffffff; font-size: xx-small;
}
.cpyrightText {
font-family: Arial,Helvetica; font-weight: bold; text-decoration: none;
color: #ffffff; font-size: xx-small;
}
.sections {
font-family: Arial,Helvetica; font-weight: bold; text-decoration: none;
color: #336699; font-size: 11pt;
}
.dsections {
font-family: Arial,Helvetica; font-weight: bold; text-decoration: none;
color: #336699; font-size: 12pt;
}
.slink {
font-family: Arial,Helvetica; font-weight: normal; text-decoration: none;
color: #000000; font-size: 9pt;
}
.slink2 { font-family: Arial,Helvetica; text-decoration: none; color: #336699; }
.maintitle {
font-family: Arial,Helvetica; font-weight: bold; text-decoration: none;
color: #336699; font-size: 18pt;
}
.dblArrow {
font-family: Arial,Helvetica; font-weight: bold; text-decoration: none;
color: #336699; font-size: small;
}
.menuSec {
font-family: Arial,Helvetica; font-weight: bold; text-decoration: none;
color: #336699; font-size: small;
}
.newstext {
font-family: Arial,Helvetica; font-size: small;
}
.linkmenu {
font-family: Arial,Helvetica; color: #000000; font-weight: bold;
text-decoration: none;
}
P {
font-family: Arial,Helvetica;
}
PRE {
font-size: 10pt;
}
.quote {
font-family: Times; text-decoration: none;
color: #000000; font-size: 9pt; font-style: italic;
}
.smstd { font-family: Arial,Helvetica; color: #000000; font-size: x-small; }
.std { font-family: Arial,Helvetica; color: #000000; }
.meerkatTitle {
font-family: sans-serif; font-size: x-small; color: black; }
.meerkatDescription { font-family: sans-serif; font-size: 10pt; color: black }
.meerkatCategory {
font-family: sans-serif; font-size: 9pt; font-weight: bold; font-style: italic;
color: brown; }
.meerkatChannel {
font-family: sans-serif; font-size: 9pt; font-style: italic; color: brown; }
.meerkatDate { font-family: sans-serif; font-size: xx-small; color: #336699; }
.tocTitle {
font-family: Arial,Helvetica; font-weight: bold; text-decoration: none;
color: #333333; font-size: 10pt;
}
.toc-item {
font-family: Arial,Helvetica; font-weight: bold;
color: #336699; font-size: 10pt; text-decoration: underline;
}
.perlVersion {
font-family: Arial,Helvetica; font-weight: bold;
color: #336699; font-size: 10pt; text-decoration: none;
}
.podTitle {
font-family: Arial,Helvetica; font-weight: bold; text-decoration: none;
color: #000000;
}
.docTitle {
font-family: Arial,Helvetica; font-weight: bold; text-decoration: none;
color: #000000; font-size: 10pt;
}
.dotDot {
font-family: Arial,Helvetica; font-weight: bold;
color: #000000; font-size: 9pt;
}
.docSec {
font-family: Arial,Helvetica; font-weight: normal;
color: #333333; font-size: 9pt;
}
.docVersion {
font-family: Arial,Helvetica; font-weight: bold; text-decoration: none;
color: #336699; font-size: 10pt;
}
.docSecs-on {
font-family: Arial,Helvetica; font-weight: normal; text-decoration: none;
color: #ff0000; font-size: 10pt;
}
.docSecs-off {
font-family: Arial,Helvetica; font-weight: normal; text-decoration: none;
color: #333333; font-size: 10pt;
}
h2 {
font-family: Arial,Helvetica; font-weight: bold; text-decoration: none;
color: #336699; font-size: medium;
}
h1 {
font-family: Verdana,Arial,Helvetica; font-weight: bold; text-decoration: none;
color: #336699; font-size: large;
}
DL {
font-family: Arial,Helvetica; font-weight: normal; text-decoration: none;
color: #333333; font-size: 10pt;
}
UL > LI > A {
font-family: Arial,Helvetica; font-weight: bold;
color: #336699; font-size: 10pt;
}
.moduleInfo {
font-family: Arial,Helvetica; font-weight: bold; text-decoration: none;
color: #333333; font-size: 11pt;
}
.moduleInfoSec {
font-family: Arial,Helvetica; font-weight: bold; text-decoration: none;
color: #336699; font-size: 10pt;
}
.moduleInfoVal {
font-family: Arial,Helvetica; font-weight: normal; text-decoration: underline;
color: #000000; font-size: 10pt;
}
.cpanNavTitle {
font-family: Arial,Helvetica; font-weight: bold;
color: #ffffff; font-size: 10pt;
}
.cpanNavLetter {
font-family: Arial,Helvetica; font-weight: bold; text-decoration: none;
color: #333333; font-size: 9pt;
}
.cpanCat {
font-family: Arial,Helvetica; font-weight: bold; text-decoration: none;
color: #336699; font-size: 9pt;
}
.bttndrkblue-bkgd-top {
background-color: #225688;
background-image: url(/global/mvc_objects/images/bttndrkblue_bgtop.gif);
}
.bttndrkblue-bkgd-left {
background-color: #225688;
background-image: url(/global/mvc_objects/images/bttndrkblue_bgleft.gif);
}
.bttndrkblue-bkgd {
padding-top: 0px;
padding-bottom: 0px;
margin-bottom: 0px;
margin-top: 0px;
background-repeat: no-repeat;
background-color: #225688;
background-image: url(/global/mvc_objects/images/bttndrkblue_bgmiddle.gif);
vertical-align: top;
}
.bttndrkblue-bkgd-right {
background-color: #225688;
background-image: url(/global/mvc_objects/images/bttndrkblue_bgright.gif);
}
.bttndrkblue-bkgd-bottom {
background-color: #225688;
background-image: url(/global/mvc_objects/images/bttndrkblue_bgbottom.gif);
}
.bttndrkblue-text a {
color: #ffffff;
text-decoration: none;
}
a.bttndrkblue-text:hover {
color: #ffDD3C;
text-decoration: none;
}
.bg-ltblue {
background-color: #f0f5fa;
}
.border-left-b {
background: #f0f5fa url(/i/corner-leftline.gif) repeat-y;
}
.border-right-b {
background: #f0f5fa url(/i/corner-rightline.gif) repeat-y;
}
.border-top-b {
background: #f0f5fa url(/i/corner-topline.gif) repeat-x;
}
.border-bottom-b {
background: #f0f5fa url(/i/corner-botline.gif) repeat-x;
}
.border-right-w {
background: #ffffff url(/i/corner-rightline.gif) repeat-y;
}
.border-top-w {
background: #ffffff url(/i/corner-topline.gif) repeat-x;
}
.border-bottom-w {
background: #ffffff url(/i/corner-botline.gif) repeat-x;
}
.bg-white {
background-color: #ffffff;
}
.border-left-w {
background: #ffffff url(/i/corner-leftline.gif) repeat-y;
}

4
include/Makefile Normal file
View File

@ -0,0 +1,4 @@
CLING_LEVEL := ..
DIRS := cling
include $(CLING_LEVEL)/Makefile

View File

@ -0,0 +1,46 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_CIFACTORY_H
#define CLING_CIFACTORY_H
#include "clang/Frontend/CompilerInstance.h"
#include "llvm/ADT/StringRef.h"
namespace llvm {
class LLVMContext;
class MemoryBuffer;
}
namespace clang {
}
namespace cling {
class CIFactory {
public:
// TODO: Add overload that takes file not MemoryBuffer
static clang::CompilerInstance* createCI(llvm::StringRef code,
int argc,
const char* const *argv,
const char* llvmdir);
static clang::CompilerInstance* createCI(llvm::MemoryBuffer* buffer,
int argc,
const char* const *argv,
const char* llvmdir);
private:
//---------------------------------------------------------------------
//! Constructor
//---------------------------------------------------------------------
CIFactory() {}
~CIFactory() {}
static void SetClingCustomLangOpts(clang::LangOptions& Opts);
static void SetClingTargetLangOpts(clang::LangOptions& Opts,
const clang::TargetInfo& Target);
};
} // namespace cling
#endif // CLING_CIFACTORY_H

View File

@ -0,0 +1,16 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_VALUEPRINTERC_H
#define CLING_VALUEPRINTERC_H
#ifdef __cplusplus
extern "C"
#endif
void cling_PrintValue(void* /*clang::Expr**/ E,
void* /*clang::ASTContext**/ C,
const void* value);
#endif // CLING_VALUEPRINTERC_H

View File

@ -0,0 +1,73 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_CALLABLE_H
#define CLING_CALLABLE_H
#include <vector>
namespace llvm {
class ExecutionEngine;
class Function;
struct GenericValue;
}
namespace clang {
class FunctionDecl;
}
namespace cling {
class Interpreter;
class Value;
///\brief A representation of a function.
//
/// A callable combines a clang::FunctionDecl for AST-based reflection
/// with the llvm::Function to call the function. It uses the
/// interpreter's ExecutionContext to place the call passing in the
/// normalized arguments. As no lookup is performed after constructing
/// a Callable, calls are very efficient.
class Callable {
protected:
/// \brief declaration; can also be a clang::CXXMethodDecl
const clang::FunctionDecl* decl;
/// \brief llvm::ExecutionEngine's function representation
llvm::Function* func;
/// \brief Interpreter that will run the function
llvm::ExecutionEngine* exec;
public:
/// \brief Default constructor, creates a Callable that IsInvalid().
Callable(): decl(0), func(0) {}
/// \brief Construct a valid Value.
Callable(const clang::FunctionDecl& Decl,
const cling::Interpreter& Interp);
/// \brief Determine whether the Callable can be invoked.
//
/// Determine whether the Callable can be invoked. Checking
/// whether the llvm::Function pointer is valid.
bool isValid() const { return func; }
/// \brief Get the declaration of the function.
//
/// Retrieve the Callable's function declaration.
const clang::FunctionDecl* getDecl() const { return decl; }
/// \brief Invoke a function passing arguments, possibly getting result.
//
/// Invoke the function. Pass in the parameters ArgValues; the return
/// value of the function call ends up in Result.
/// If the function represents a CXXMethodDecl, the first argument is
/// expected to be the this pointer of the object to un the function on.
/// \return true if the call was successful.
bool Invoke(const std::vector<llvm::GenericValue>& ArgValues,
Value* Result = 0) const;
};
} // end namespace cling
#endif // CLING_CALLABLE_H

View File

@ -0,0 +1,26 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_CLINGOPTIONS_H
#define CLING_CLINGOPTIONS_H
namespace cling {
namespace driver {
namespace clingoptions {
enum ID {
OPT_INVALID = 0, // This is not an option ID.
#define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \
HELPTEXT, METAVAR) OPT_##ID,
#include "cling/Interpreter/ClingOptions.inc"
LastOption
#undef OPTION
};
}
}
}
#endif // CLING_CLINGOPTIONS_H

View File

@ -0,0 +1,21 @@
OPTION("<input>", INPUT, Input, INVALID, INVALID, 0, 0, 0, 0)
OPTION("<unknown>", UNKNOWN, Unknown, INVALID, INVALID, 0, 0, 0, 0)
// Re-implement to forward to our help
OPTION("--help", _help, Flag, INVALID, help, 0, 0, 0, 0)
OPTION("--metastr=", _metastr_EQ, Joined, INVALID, INVALID, 0, 0,
"Set the meta command tag, default '.'", 0)
OPTION("--metastr", _metastr, Separate, INVALID, INVALID, 0, 0,
"Set the meta command tag, default '.'", 0)
OPTION("--nologo", _nologo, Flag, INVALID, INVALID, 0, 0,
"Do not show startup-banner", 0)
OPTION("--version", _version, Flag, INVALID, version, 0, 0, 0, 0)
OPTION("-L", L, JoinedOrSeparate, INVALID, INVALID, 0, 0,
"Add directory to library search path", "<directory>")
OPTION("-help", help, Flag, INVALID, INVALID, 0, 0,
"Print this help text", 0)
OPTION("-l", l, JoinedOrSeparate, INVALID, INVALID, 0, 0,
"Load a library before prompt", "<library>")
OPTION("-version", version, Flag, INVALID, INVALID, 0, 0,
"Print the compiler version", 0)
OPTION("-v", v, Flag, INVALID, INVALID, 0, 0,
"Enable verbose output", 0)

View File

@ -0,0 +1,59 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id: DynamicLookupRuntimeUniverse.h 44568 2012-06-05 12:48:15Z axel $
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_DYNAMIC_EXPR_INFO_H
#define CLING_DYNAMIC_EXPR_INFO_H
#include <string>
namespace cling {
namespace runtime {
namespace internal {
///\brief Helper structure used to provide specific context of the evaluated
/// expression, when needed.
///
/// Consider:
/// @code
/// int a = 5;
/// const char* b = dep->Symbol(a);
/// @endcode
/// In the particular case we need to pass a context to the evaluator of the
/// unknown symbol. The addresses of the items in the context are not known at
/// compile time, so they cannot be embedded directly. Instead of that we
/// need to create an array of addresses of those context items (mainly
/// variables) and insert them into the evaluated expression at runtime
/// This information is kept using the syntax: "dep->Symbol(*(int*)@)",
/// where @ denotes that the runtime address the variable "a" is needed.
///
class DynamicExprInfo {
private:
/// \brief The expression template.
const char* m_Template;
std::string m_Result;
/// \brief The variable list.
void** m_Addresses;
/// \brief The variable is set if it is required to print out the result of
/// the dynamic expression after evaluation
bool m_ValuePrinterReq;
public:
DynamicExprInfo(const char* templ, void* addresses[], bool valuePrinterReq)
: m_Template(templ), m_Result(templ), m_Addresses(addresses),
m_ValuePrinterReq(valuePrinterReq) {}
///\brief Performs the insertions of the context in the expression just
/// before evaluation. To be used only at runtime.
///
const char* getExpr();
bool isValuePrinterRequested() { return m_ValuePrinterReq; }
};
} // end namespace internal
} // end namespace runtime
} // end namespace cling
#endif // CLING_DYNAMIC_EXPR_INFO_H

View File

@ -0,0 +1,134 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#ifndef __CLING__
#error "This file must not be included by compiled programs."
#endif
#ifdef CLING_DYNAMIC_LOOKUP_RUNTIME_UNIVERSE_H
#error "CLING_DYNAMIC_LOOKUP_RUNTIME_UNIVERSE_H Must only include once."
#endif
#define CLING_DYNAMIC_LOOKUP_RUNTIME_UNIVERSE_H
#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/DynamicExprInfo.h"
#include "cling/Interpreter/ValuePrinter.h"
#include "cling/Interpreter/Value.h"
#include "llvm/Support/raw_ostream.h"
#include <stdio.h>
namespace cling {
/// \brief Used to stores the declarations, which are going to be
/// available only at runtime. These are cling runtime builtins.
namespace runtime {
/// \brief Provides builtins, which are neccessary for the dynamic scopes
/// and runtime bindings. These builtins should be used for other purposes.
namespace internal {
/// \brief EvaluateT is used to replace all invalid source code that
/// occurs, when cling's dynamic extensions are enabled.
///
/// When the interpreter "sees" invalid code it marks it and skip all the
/// semantic checks (like with templates). Afterwords all these marked
/// nodes are replaced with a call to EvaluateT, which makes valid
/// C++ code. It is templated because it can be used in expressions and
/// T is the type of the evaluated expression.
///
/// @tparam T The type of the evaluated expression.
/// @param[in] ExprInfo Helper structure that keeps information about the
/// expression that is being replaced and the addresses of the variables
/// that the replaced expression contains.
/// @param[in] DC The declaration context, in which the expression will be
/// evaluated at runtime.
template<typename T>
T EvaluateT(DynamicExprInfo* ExprInfo, clang::DeclContext* DC ) {
Value result(gCling->Evaluate(ExprInfo->getExpr(), DC,
ExprInfo->isValuePrinterRequested())
);
// Check whether the expected return type and the actual return type are
// compatible with Sema::CheckAssingmentConstraints or
// ASTContext::typesAreCompatible.
return result.getAs<T>();
}
/// \brief EvaluateT specialization for the case where we instantiate with
/// void.
template<>
void EvaluateT(DynamicExprInfo* ExprInfo, clang::DeclContext* DC ) {
gCling->Evaluate(ExprInfo->getExpr(), DC,
ExprInfo->isValuePrinterRequested());
}
/// \brief LifetimeHandler is used in case of initialization using address
/// on the automatic store (stack) instead of EvaluateT.
///
/// The reason is to avoid the copy constructors that might be private.
/// This is part of complex transformation, which aims to preserve the
/// code behavior. For example:
/// @code
/// int i = 5;
/// MyClass my(dep->Symbol(i))
/// @endcode
/// where dep->Symbol() is a symbol not known at compile-time
/// transformed into:
/// @code
/// cling::runtime::internal::LifetimeHandler
/// __unique("dep->Sybmol(*(int*)@)",(void*[]){&i}, DC, "MyClass");
/// MyClass &my(*(MyClass*)__unique.getMemory());
/// @endcode
class LifetimeHandler {
private:
/// \brief The memory on the free store, where the object will be
/// created.
void* m_Memory;
/// \brief The type of the object that will be created.
std::string m_Type;
public:
/// \brief Constructs an expression, which creates the object on the
/// free store and tells the interpreter to evaluate it.
///
/// @param[in] ExprInfo Helper structure that keeps information about
/// the expression that is being replaced and the addresses of the
/// variables that the replaced expression contains.
/// @param[in] DC The declaration context, in which the expression will
/// be evaluated at runtime
/// @param[in] type The type of the object, which will help to delete
/// it, when the LifetimeHandler goes out of scope.
///
LifetimeHandler(DynamicExprInfo* ExprInfo,
clang::DeclContext* DC,
const char* type) {
m_Type = type;
std::string ctor("new ");
ctor += type;
ctor += ExprInfo->getExpr();
Value res = gCling->Evaluate(ctor.c_str(), DC,
ExprInfo->isValuePrinterRequested()
);
m_Memory = (void*)res.value.PointerVal;
}
///\brief Returns the created object.
void* getMemory() const { return m_Memory; }
/// \brief Clears up the free store, when LifetimeHandler goes out of
/// scope.
///
~LifetimeHandler() {
std::string str;
llvm::raw_string_ostream stream(str);
stream<<"delete ("<< m_Type << "*) "<< m_Memory << ";";
stream.flush();
gCling->evaluate(str);
}
};
}
} // end namespace runtime
} // end namespace cling

View File

@ -0,0 +1,425 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Lukasz Janyst <ljanyst@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_INTERPRETER_H
#define CLING_INTERPRETER_H
#include "cling/Interpreter/InvocationOptions.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include <string>
namespace llvm {
class raw_ostream;
struct GenericValue;
class ExecutionEngine;
class LLVMContext;
class Module;
}
namespace clang {
class ASTContext;
class CompilerInstance;
class Decl;
class DeclContext;
class NamedDecl;
}
namespace cling {
class CompilationOptions;
namespace runtime {
namespace internal {
class DynamicExprInfo;
template <typename T>
T EvaluateT(DynamicExprInfo* ExprInfo, clang::DeclContext* DC);
class LifetimeHandler;
}
}
class ExecutionContext;
class IncrementalParser;
class InterpreterCallbacks;
class LookupHelper;
class Value;
///\brief Class that implements the interpreter-like behavior. It manages the
/// incremental compilation.
///
class Interpreter {
public:
///\brief Describes the return result of the different routines that do the
/// incremental compilation.
///
enum CompilationResult {
kSuccess,
kFailure,
kMoreInputExpected
};
private:
///\brief Interpreter invocation options.
///
InvocationOptions m_Opts;
///\brief The llvm library state, a per-thread object.
///
llvm::OwningPtr<llvm::LLVMContext> m_LLVMContext;
///\brief Cling's execution engine - a well wrapped llvm execution engine.
///
llvm::OwningPtr<ExecutionContext> m_ExecutionContext;
///\brief Cling's worker class implementing the incremental compilation.
///
llvm::OwningPtr<IncrementalParser> m_IncrParser;
///\brief Cling's reflection information query.
///
llvm::OwningPtr<LookupHelper> m_LookupHelper;
///\brief Counter used when we need unique names.
///
unsigned long long m_UniqueCounter;
///\brief Flag toggling the AST printing on or off.
///
bool m_PrintAST;
///\brief Flag toggling the dynamic scopes on or off.
///
bool m_DynamicLookupEnabled;
///\brief Stream to dump values into.
///
/// TODO: Since it is only used by the ValuePrinterSynthesizer it should be
/// somewhere else.
///
llvm::OwningPtr<llvm::raw_ostream> m_ValuePrintStream;
///\breif Helper that manages when the destructor of an object to be called.
///
/// The object is registered first as an CXAAtExitElement and then cling
/// takes the control of it's destruction.
///
struct CXAAtExitElement {
///\brief Constructs an element, whose destruction time will be managed by
/// the interpreter. (By registering a function to be called by exit
/// or when a shared library is unloaded.)
///
/// Registers destructors for objects with static storage duration with
/// the _cxa atexit function rather than the atexit function. This option
/// is required for fully standards-compliant handling of static
/// destructors(many of them created by cling), but will only work if
/// your C library supports __cxa_atexit (means we have our own work
/// around for Windows). More information about __cxa_atexit could be
/// found in the Itanium C++ ABI spec.
///
///\param [in] func - The function to be called on exit or unloading of
/// shared lib.(The destructor of the object.)
///\param [in] arg - The argument the func to be called with.
///\param [in] dso - The dynamic shared object handle.
///\param [in] fromTLD - The unloading of this top level declaration will
/// trigger the atexit function.
///
CXAAtExitElement(void (*func) (void*), void* arg, void* dso,
clang::Decl* fromTLD):
m_Func(func), m_Arg(arg), m_DSO(dso), m_FromTLD(fromTLD) {}
///\brief The function to be called.
///
void (*m_Func)(void*);
///\brief The single argument passed to the function.
///
void* m_Arg;
/// \brief The DSO handle.
///
void* m_DSO;
///\brief Clang's top level declaration, whose unloading will trigger the
/// call this atexit function.
///
clang::Decl* m_FromTLD;
};
///\brief Static object, which are bound to unloading of certain declaration
/// to be destructed.
///
llvm::SmallVector<CXAAtExitElement, 20> m_AtExitFuncs;
///\brief Processes the invocation options.
///
void handleFrontendOptions();
///\brief Worker function, building block for interpreter's public
/// interfaces.
///
///\param [in] input - The input being compiled.
///\param [in] CompilationOptions - The option set driving the compilation.
///\param [out] D - The first declaration of the compiled input.
///
///\returns Whether the operation was fully successful.
///
CompilationResult Declare(const std::string& input,
const CompilationOptions& CO,
const clang::Decl** D = 0);
///\brief Worker function, building block for interpreter's public
/// interfaces.
///
///\param [in] input - The input being compiled.
///\param [in] CompilationOptions - The option set driving the compilation.
///\param [in,out] V - The result of the evaluation of the input. Must be
/// initialized to point to the return value's location if the
/// expression result is an aggregate.
///
///\returns Whether the operation was fully successful.
///
CompilationResult Evaluate(const std::string& input,
const CompilationOptions& CO,
Value* V = 0);
///\brief Wraps a given input.
///
/// The interpreter must be able to run statements on the fly, which is not
/// C++ standard-compliant operation. In order to do that we must wrap the
/// input into a artificial function, containing the statements and run it.
///\param [out] input - The input to wrap.
///\param [out] fname - The wrapper function's name.
///
void WrapInput(std::string& input, std::string& fname);
///\brief Runs given function.
///
///\param [in] fname - The function name.
///\param [in,out] res - The return result of the run function. Must be
/// initialized to point to the return value's location if the
/// expression result is an aggregate.
///
///\returns true if successful otherwise false.
///
bool RunFunction(llvm::StringRef fname, llvm::GenericValue* res = 0);
///\brief Super efficient way of creating unique names, which will be used
/// as a part of the compilation process.
///
/// Creates the name directly in the compiler's identifier table, so that
/// next time the compiler looks for that name it will find it directly
/// there.
///
llvm::StringRef createUniqueWrapper();
///\brief Forwards to cling::ExecutionContext::addSymbol.
///
bool addSymbol(const char* symbolName, void* symbolAddress);
public:
void unload();
///\brief Implements named parameter idiom - allows the idiom
/// LookupDecl().LookupDecl()...
///
class NamedDeclResult {
private:
Interpreter* m_Interpreter;
clang::ASTContext& m_Context;
const clang::DeclContext* m_CurDeclContext;
clang::NamedDecl* m_Result;
NamedDeclResult(llvm::StringRef Decl, Interpreter* interp,
const clang::DeclContext* Within = 0);
public:
NamedDeclResult& LookupDecl(llvm::StringRef);
operator clang::NamedDecl* () const { return getSingleDecl(); }
clang::NamedDecl* getSingleDecl() const;
template<class T> T* getAs(){
clang::NamedDecl *result = getSingleDecl();
if (result) {
return llvm::dyn_cast<T>(result);
} else {
return 0;
}
}
friend class Interpreter;
};
Interpreter(int argc, const char* const *argv, const char* llvmdir = 0);
virtual ~Interpreter();
const InvocationOptions& getOptions() const { return m_Opts; }
InvocationOptions& getOptions() { return m_Opts; }
const llvm::LLVMContext* getLLVMContext() const {
return m_LLVMContext.get();
}
llvm::LLVMContext* getLLVMContext() { return m_LLVMContext.get(); }
const LookupHelper& getLookupHelper() const { return *m_LookupHelper; }
///\brief Shows the current version of the project.
///
///\returns The current svn revision (svn Id).
///
const char* getVersion() const;
///\brief Creates unique name that can be used for various aims.
///
void createUniqueName(std::string& out);
///\brief Adds an include path (-I).
///
void AddIncludePath(llvm::StringRef incpath);
///\brief Prints the current include paths that are used.
///
void DumpIncludePath();
///\brief Compiles the given input.
///
/// This interface helps to run everything that cling can run. From
/// declaring header files to running or evaluating single statements.
/// Note that this should be used when there is no idea of what kind of
/// input is going to be processed. Otherwise if is known, for example
/// only header files are going to be processed it is much faster to run the
/// specific interface for doing that - in the particular case - declare().
///
///\param[in] input - The input to be compiled.
///\param[in,out] V - The result of the evaluation of the input. Must be
/// initialized to point to the return value's location if the
/// expression result is an aggregate.
///\param[out] D - The first declaration of the compiled input.
///
///\returns Whether the operation was fully successful.
///
CompilationResult process(const std::string& input, Value* V = 0,
const clang::Decl** D = 0);
///\brief Parses input line, which doesn't contain statements. No code
/// generation is done.
///
/// Same as declare without codegening. Useful when a library is loaded and
/// the header files need to be imported.
///
///\param[in] input - The input containing the declarations.
///
///\returns Whether the operation was fully successful.
///
CompilationResult parse(const std::string& input);
///\brief Compiles input line, which doesn't contain statements.
///
/// The interface circumvents the most of the extra work necessary to
/// compile and run statements.
///
/// @param[in] input - The input containing only declarations (aka
/// Top Level Declarations)
/// @param[out] D - The first compiled declaration from the input
///
///\returns Whether the operation was fully successful.
///
CompilationResult declare(const std::string& input,
const clang::Decl** D = 0);
///\brief Compiles input line, which contains only expressions.
///
/// The interface circumvents the most of the extra work necessary extract
/// the declarations from the input.
///
/// @param[in] input - The input containing only expressions
/// @param[in,out] V - The value of the executed input. Must be
/// initialized to point to the return value's location if the
/// expression result is an aggregate.
///
///\returns Whether the operation was fully successful.
///
CompilationResult evaluate(const std::string& input,
Value* V = 0);
///\brief Compiles input line, which contains only expressions and prints
/// out the result of its execution.
///
/// The interface circumvents the most of the extra work necessary extract
/// the declarations from the input.
///
/// @param[in] input - The input containing only expressions.
/// @param[in,out] V - The value of the executed input. Must be
/// initialized to point to the return value's location if the
/// expression result is an aggregate.
///
///\returns Whether the operation was fully successful.
///
CompilationResult echo(const std::string& input, Value* V = 0);
///\brief Loads header file or shared library.
///
///\param [in] filename - The file to loaded.
///\param [in] allowSharedLib - Whether to try to load the file as shared
/// library.
///
///\returns true for happiness.
///
bool loadFile(const std::string& filename, bool allowSharedLib = true);
void enableDynamicLookup(bool value = true);
bool isDynamicLookupEnabled() { return m_DynamicLookupEnabled; }
bool isPrintingAST() { return m_PrintAST; }
void enablePrintAST(bool print = true) { m_PrintAST = print;}
clang::CompilerInstance* getCI() const;
llvm::ExecutionEngine* getExecutionEngine() const;
llvm::Module* getModule() const;
void installLazyFunctionCreator(void* (*fp)(const std::string&));
llvm::raw_ostream& getValuePrinterStream() const {
return *m_ValuePrintStream; }
void runStaticInitializersOnce() const;
int CXAAtExit(void (*func) (void*), void* arg, void* dso);
///\brief Evaluates given expression within given declaration context.
///
///\param[in] expr - The expression.
///\param[in] DC - The declaration context in which the expression is going
/// to be evaluated.
///\param[in] ValuePrinterReq - Whether the value printing is requested.
///
///\returns The result of the evaluation if the expression.
///
Value Evaluate(const char* expr, clang::DeclContext* DC,
bool ValuePrinterReq = false);
///\brief Looks up declaration within given declaration context. Does top
/// down lookup.
///
///@param[in] Decl Declaration name.
///@param[in] Within Starting declaration context.
///
NamedDeclResult LookupDecl(llvm::StringRef Decl,
const clang::DeclContext* Within = 0);
///\brief Sets callbacks needed for the dynamic lookup.
///
void setCallbacks(InterpreterCallbacks* C);
friend class runtime::internal::LifetimeHandler;
};
} // namespace cling
#endif // CLING_INTERPRETER_H

View File

@ -0,0 +1,49 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_INTERPRETER_CALLBACKS_H
#define CLING_INTERPRETER_CALLBACKS_H
namespace clang {
class LookupResult;
class Scope;
}
namespace cling {
/// \brief This interface provides a way to observe the actions of the
/// interpreter as it does its thing. Clients can define their hooks here to
/// implement interpreter level tools.
class InterpreterCallbacks {
private:
// The callbacks should contain the interpreter in case of more than one
InterpreterCallbacks(){}
protected:
Interpreter* m_Interpreter;
bool m_Enabled;
public:
InterpreterCallbacks(Interpreter* interp, bool enabled = false)
: m_Interpreter(interp) {
setEnabled(enabled);
}
virtual ~InterpreterCallbacks() {}
void setEnabled(bool e = true) {
m_Enabled = e;
}
bool isEnabled() { return m_Enabled; }
/// \brief This callback is invoked whenever the interpreter needs to
/// resolve the type and the adress of an object, which has been marked for
/// delayed evaluation from the interpreter's dynamic lookup extension
virtual bool LookupObject(clang::LookupResult& R, clang::Scope* S) = 0;
};
} // end namespace cling
#endif // CLING_INTERPRETER_CALLBACKS_H

View File

@ -0,0 +1,40 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_INVOCATIONOPTIONS_H
#define CLING_INVOCATIONOPTIONS_H
#include "llvm/Support/Path.h"
#include <vector>
namespace cling {
class InvocationOptions {
public:
InvocationOptions():
NoLogo(false), ShowVersion(false), Verbose(false), Help(false),
MetaString(".") {}
bool NoLogo;
bool ShowVersion;
bool Verbose;
bool Help;
/// \brief A line starting with this string is assumed to contain a
/// directive for the MetaProcessor. Defaults to "."
std::string MetaString;
std::vector<std::string> LibsToLoad;
std::vector<llvm::sys::Path> LibSearchPath;
static InvocationOptions CreateFromArgs(int argc, const char* const argv[],
std::vector<unsigned>& leftoverArgs
/* , Diagnostic &Diags */);
void PrintHelp();
};
}
#endif // INVOCATIONOPTIONS

View File

@ -0,0 +1,84 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id: Interpreter.h 45404 2012-08-06 08:30:06Z vvassilev $
// author: Vassil Vassilev <vvasilev@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_LOOKUP_HELPER_H
#define CLING_LOOKUP_HELPER_H
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/SmallVector.h"
namespace clang {
class Decl;
class Expr;
class FunctionDecl;
class Parser;
class QualType;
class Type;
}
namespace llvm {
template<typename T, unsigned N> class SmallVector;
}
namespace cling {
///\brief Reflection information query interface. The class performs lookups
/// in the currently loaded information in the AST, using the same Parser, Sema
/// and Preprocessor objects.
///
class LookupHelper {
private:
clang::Parser* m_Parser; //doesn't own it.
public:
LookupHelper(clang::Parser* P) : m_Parser(P) {}
///\brief Lookup a type by name, starting from the global
/// namespace.
///
/// \param [in] typeName - The type to lookup.
///
/// \retval retval - On a failed lookup retval.isNull() will be true.
///
clang::QualType findType(llvm::StringRef typeName) const;
///\brief Lookup a class declaration by name, starting from the global
/// namespace, also handles struct, union, namespace, and enum.
///
///\param [in] className - The name of the class, struct, union,
/// namespace, or enum to lookup.
///\param [out] resultType - The type of the class, struct, union,
/// or enum to lookup; NULL otherwise.
///\returns The found declaration or null.
///
const clang::Decl* findScope(llvm::StringRef className,
const clang::Type** resultType = 0) const;
const clang::FunctionDecl* findFunctionProto(const clang::Decl* scopeDecl,
llvm::StringRef funcName,
llvm::StringRef funcProto) const;
const clang::FunctionDecl* findFunctionArgs(const clang::Decl* scopeDecl,
llvm::StringRef funcName,
llvm::StringRef funcArgs) const;
///\brief Lookup given argument list and return each argument as an
/// expression.
///
///\param[in] argList - The string representation of the argument list.
///
///\param[out] argExprs - The corresponding expressions to the argList.
///
void findArgList(llvm::StringRef argList,
llvm::SmallVector<clang::Expr*, 4>& argExprs) const;
};
} // end namespace
#endif // CLING_LOOKUP_HELPER_H

View File

@ -0,0 +1,104 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#ifndef __CLING__
#error "This file must not be included by compiled programs."
#else
#ifdef CLING_RUNTIME_UNIVERSE_H
#error "CLING_RUNTIME_UNIVERSE_H Must only include once."
#else
#define CLING_RUNTIME_UNIVERSE_H
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS // needed by System/DataTypes.h
#endif
#ifndef __STDC_CONSTANT_MACROS
#define __STDC_CONSTANT_MACROS // needed by System/DataTypes.h
#endif
#ifdef __cplusplus
namespace cling {
class Interpreter;
/// \brief Used to stores the declarations, which are going to be
/// available only at runtime. These are cling runtime builtins
namespace runtime {
/// \brief The interpreter provides itself as a builtin, i.e. it
/// interprets itself. This is particularly important for implementing
/// the dynamic scopes and the runtime bindings
Interpreter* gCling = 0;
namespace internal {
/// \brief Some of clang's routines rely on valid source locations and
/// source ranges. This member can be looked up and source locations and
/// ranges can be passed in as parameters to these routines.
///
/// Use instead of SourceLocation() and SourceRange(). This might help,
/// when clang emits diagnostics on artificially inserted AST node.
int InterpreterGeneratedCodeDiagnosticsMaybeIncorrect;
//__cxa_atexit is declared later for WIN32
#if (!_WIN32)
// Implemented in Interpreter.cpp
int local_cxa_atexit(void (*func) (void*), void* arg,
void* dso, cling::Interpreter* interp);
// Force the module to define __cxa_atexit, we need it.
struct __trigger__cxa_atexit {
~__trigger__cxa_atexit(); // implemented in Interpreter.cpp
} S;
#endif
} // end namespace internal
} // end namespace runtime
} // end namespace cling
using namespace cling::runtime;
// Global d'tors only for C++:
#if !_WIN32
extern "C"
int cling_cxa_atexit(void (*func) (void*), void* arg, void* dso) {
return cling::runtime::internal::local_cxa_atexit(func, arg, dso, gCling);
}
#else
extern "C" {
///\brief Fake definition to avoid compilation missing function in windows
/// environment it wont ever be called
void __dso_handle(){}
//Fake definition to avoid compilation missing function in windows environment
//it wont ever be called
int __cxa_atexit(void (*func) (), void* arg, void* dso) {
return 0;
}
///\brief Manually provided by cling missing function resolution using
/// sys::DynamicLibrary::AddSymbol()
/// Included in extern C so its name is not mangled and easier to register
int local_cxa_atexit(void (*func) (void*), void* arg,
void* dso, cling::Interpreter* interp);
//cling _cxa_atexit replacement
int cling_cxa_atexit(void (*func) (void*), void* arg, void* dso) {
return local_cxa_atexit(func, arg, dso, cling::runtime::gCling);
}
}
#endif
#endif // __cplusplus
#endif // CLING_RUNTIME_UNIVERSE_H (error)
#endif // __CLING__

View File

@ -0,0 +1,161 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_VALUE_H
#define CLING_VALUE_H
#include "llvm/ExecutionEngine/GenericValue.h"
#include "clang/AST/Type.h"
#include "clang/AST/CanonicalType.h"
namespace clang {
class ASTContext;
}
namespace cling {
///\brief A type, value pair.
//
/// Type-safe value access and setting. Simple (built-in) casting is
/// available, but better extract the value using the template
/// parameter that matches the Value's type.
///
/// The class represents a llvm::GenericValue with its corresponding
/// clang::QualType. Use-cases:
/// 1. Expression evaluation: we need to know the type of the GenericValue
/// that we have gotten from the JIT
/// 2. Value printer: needs to know the type in order to skip the printing of
/// void types
/// 3. Parameters for calls given an llvm::Function and a clang::FunctionDecl.
class Value {
private:
/// \brief Forward decl for typed access specializations
template <typename T> struct TypedAccess;
public:
/// \brief value
llvm::GenericValue value;
/// \brief the value's type
clang::QualType type;
/// \brief Default constructor, creates a value that IsInvalid().
Value() {}
/// \brief Construct a valid Value.
Value(const llvm::GenericValue& v, clang::QualType t) :
value(v), type(t){}
/// \brief Determine whether the Value has been set.
//
/// Determine whether the Value has been set by checking
/// whether the type is valid.
bool isValid() const { return !type.isNull(); }
/// \brief Determine whether the Value is set but void.
bool isVoid(const clang::ASTContext& ASTContext) const {
return isValid() && type.getDesugaredType(ASTContext)->isVoidType(); }
/// \brief Determine whether the Value is set and not void.
//
/// Determine whether the Value is set and not void.
/// Only in this case can getAs() or simplisticCastAs() be called.
bool hasValue(const clang::ASTContext& ASTContext) const {
return isValid() && !isVoid(ASTContext); }
/// \brief Get the value without type checking.
template <typename T>
T getAs() const;
/// \brief Get the value.
//
/// Get the value cast to T. This is similar to reinterpret_cast<T>(value),
/// casting the value of builtins (except void), enums and pointers.
/// Values referencing an object are treated as pointers to the object.
template <typename T>
T simplisticCastAs() const;
};
template<typename T>
struct Value::TypedAccess{
T extract(const llvm::GenericValue& value) {
return *reinterpret_cast<T*>(value.PointerVal);
}
};
template<typename T>
struct Value::TypedAccess<T*>{
T* extract(const llvm::GenericValue& value) {
return reinterpret_cast<T*>(value.PointerVal);
}
};
#define CLING_VALUE_TYPEDACCESS(TYPE, GETTER) \
template<> \
struct Value::TypedAccess<TYPE> { \
TYPE extract(const llvm::GenericValue& value) { \
return value.GETTER; \
} \
}
#define CLING_VALUE_TYPEDACCESS_SIGNED(TYPE) \
CLING_VALUE_TYPEDACCESS(signed TYPE, IntVal.getSExtValue())
#define CLING_VALUE_TYPEDACCESS_UNSIGNED(TYPE) \
CLING_VALUE_TYPEDACCESS(unsigned TYPE, IntVal.getZExtValue())
#define CLING_VALUE_TYPEDACCESS_BOTHSIGNS(TYPE) \
CLING_VALUE_TYPEDACCESS_SIGNED(TYPE); \
CLING_VALUE_TYPEDACCESS_UNSIGNED(TYPE);
CLING_VALUE_TYPEDACCESS(double, DoubleVal);
//CLING_VALUE_TYPEDACCESS(long double, ???);
CLING_VALUE_TYPEDACCESS(float, FloatVal);
CLING_VALUE_TYPEDACCESS(bool, IntVal.getBoolValue());
CLING_VALUE_TYPEDACCESS_BOTHSIGNS(char)
CLING_VALUE_TYPEDACCESS_BOTHSIGNS(short)
CLING_VALUE_TYPEDACCESS_BOTHSIGNS(int)
CLING_VALUE_TYPEDACCESS_BOTHSIGNS(long)
CLING_VALUE_TYPEDACCESS_BOTHSIGNS(long long)
#undef CLING_VALUE_TYPEDACCESS_BOTHSIGNS
#undef CLING_VALUE_TYPEDACCESS_UNSIGNED
#undef CLING_VALUE_TYPEDACCESS_SIGNED
#undef CLING_VALUE_TYPEDACCESS
template <typename T>
T Value::getAs() const {
// T *must* correspond to type. Else use simplisticCastAs()!
TypedAccess<T> VI;
return VI.extract(value);
}
template <typename T>
T Value::simplisticCastAs() const {
const clang::Type* desugCanon = type->getUnqualifiedDesugaredType();
desugCanon = desugCanon->getCanonicalTypeUnqualified()->getTypePtr()
->getUnqualifiedDesugaredType();
if (desugCanon->isSignedIntegerOrEnumerationType()) {
return (T) getAs<signed long long>();
} else if (desugCanon->isUnsignedIntegerOrEnumerationType()) {
return (T) getAs<unsigned long long>();
} else if (desugCanon->isRealFloatingType()) {
const clang::BuiltinType* BT = desugCanon->getAs<clang::BuiltinType>();
if (BT->getKind() == clang::BuiltinType::Double)
return (T) getAs<double>();
else if (BT->getKind() == clang::BuiltinType::Float)
return (T) getAs<float>();
/* not yet supported in JIT:
else if (BT->getKind() == clang::BuiltinType::LongDouble)
return (T) getAs<long double>();
*/
} else if (desugCanon->isPointerType() || desugCanon->isObjectType()) {
return (T) getAs<void*>();
}
assert("unsupported type in Value, cannot cast simplistically!" && 0);
return T();
}
} // end namespace cling
#endif // CLING_VALUE_H

View File

@ -0,0 +1,78 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_VALUEPRINTER_H
#define CLING_VALUEPRINTER_H
#include "cling/Interpreter/ValuePrinterInfo.h"
namespace llvm {
class raw_ostream;
}
namespace cling {
// Can be re-implemented to print type-specific details, e.g. as
// template <typename ACTUAL>
// void dumpPtr(llvm::raw_ostream& o, const clang::Decl* a, ACTUAL* ac,
// int flags, const char* tname);
template <typename TY>
void printValue(llvm::raw_ostream& o, const void* const p,
TY* const u, const ValuePrinterInfo& VPI);
void printValueDefault(llvm::raw_ostream& o, const void* const p,
const ValuePrinterInfo& PVI);
void flushOStream(llvm::raw_ostream& o);
namespace valuePrinterInternal {
template <typename T>
const T& PrintValue(llvm::raw_ostream* o, clang::Expr* E,
clang::ASTContext* C, const T& value) {
ValuePrinterInfo VPI(E, C);
printValue(*o, &value, &value, VPI);
// Only because we don't want to include llvm::raw_ostream in the header
flushOStream(*o);
return value;
}
template <typename T>
const T* PrintValue(llvm::raw_ostream* o, clang::Expr* E,
clang::ASTContext* C, const T* value) {
ValuePrinterInfo VPI(E, C);
printValue(*o, value, value, VPI);
// Only because we don't want to include llvm::raw_ostream in the header
flushOStream(*o);
return value;
}
template <typename T>
const T* PrintValue(llvm::raw_ostream* o, clang::Expr* E,
clang::ASTContext* C, T* value) {
ValuePrinterInfo VPI(E, C);
printValue(*o, value, value, VPI);
// Only because we don't want to include llvm::raw_ostream in the header
flushOStream(*o);
return value;
}
} // namespace valuePrinterInternal
// Can be re-implemented to print type-specific details, e.g. as
// template <typename ACTUAL>
// void dumpPtr(llvm::raw_ostream& o, const clang::Decl* a,
// ACTUAL* ap, int flags, const char* tname);
template <typename TY>
void printValue(llvm::raw_ostream& o, const void* const p,
TY* const u, const ValuePrinterInfo& PVI) {
printValueDefault(o, p, PVI);
}
}
#endif // CLING_VALUEPRINTER_H

View File

@ -0,0 +1,38 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_VALUE_PRINTER_INFO_H
#define CLING_VALUE_PRINTER_INFO_H
namespace clang {
class ASTContext;
class Expr;
}
namespace cling {
class ValuePrinterInfo {
private:
const clang::Expr* m_Expr;
const clang::ASTContext* m_Context;
unsigned m_Flags;
public:
enum ValuePrinterFlags {
VPI_Ptr = 1,
VPI_Const = 2,
VPI_Polymorphic = 4
};
ValuePrinterInfo(clang::Expr* E, clang::ASTContext* Ctx);
const clang::Expr* getExpr() const { return m_Expr; }
const clang::ASTContext* getASTContext() const { return m_Context; }
unsigned getFlags() { return m_Flags; }
};
} // end namespace cling
#endif // CLING_VALUE_PRINTER_INFO_H

30
include/cling/Makefile Normal file
View File

@ -0,0 +1,30 @@
CLING_LEVEL := ../..
include $(CLING_LEVEL)/Makefile
install-local::
$(Echo) Installing Cling include files
$(Verb) $(MKDIR) $(DESTDIR)$(PROJ_includedir)
$(Verb) if test -d "$(PROJ_SRC_ROOT)/tools/cling/include/cling" ; then \
cd $(PROJ_SRC_ROOT)/tools/cling/include && \
for hdr in `find cling -type f '!' '(' -name '*~' \
-o -name '.#*' -o -name '*.in' -o -name '*.txt' \
-o -name 'Makefile' -o -name '*.td' -o -name '*.orig' ')' -print \
| grep -v CVS | grep -v .svn | grep -v .dir` ; do \
instdir=$(DESTDIR)`dirname "$(PROJ_includedir)/$$hdr"` ; \
if test \! -d "$$instdir" ; then \
$(EchoCmd) Making install directory $$instdir ; \
$(MKDIR) $$instdir ;\
fi ; \
$(DataInstall) $$hdr $(DESTDIR)$(PROJ_includedir)/$$hdr ; \
done ; \
fi
ifneq ($(PROJ_SRC_ROOT),$(PROJ_OBJ_ROOT))
$(Verb) if test -d "$(PROJ_OBJ_ROOT)/tools/cling/include/cling" ; then \
cd $(PROJ_OBJ_ROOT)/tools/cling/include && \
for hdr in `find cling -type f '!' '(' -name 'Makefile' ')' -print \
| grep -v CVS | grep -v .tmp | grep -v .dir` ; do \
$(DataInstall) $$hdr $(DESTDIR)$(PROJ_includedir)/$$hdr ; \
done ; \
fi
endif

View File

@ -0,0 +1,151 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Lukasz Janyst <ljanyst@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_METAPROCESSOR_H
#define CLING_METAPROCESSOR_H
#include "clang/Lex/Token.h"
#include "llvm/ADT/OwningPtr.h"
#include <string>
namespace clang {
class Lexer;
}
namespace cling {
class Interpreter;
class InputValidator;
class Value;
class MetaProcessorOpts {
public:
///\brief is quit requested
///
bool Quitting : 1;
///\brief is printAST requested
///
bool PrintingAST : 1;
///\brief is using wrappers requested
///
bool RawInput : 1;
///\brief is using dynamic scopes enabled
///
bool DynamicLookup : 1;
MetaProcessorOpts() {
Quitting = 0;
PrintingAST = 0;
RawInput = 0;
DynamicLookup = 0;
}
};
///\brief Class that helps processing meta commands, which add extra
/// interactivity. Syntax .Command [arg0 arg1 ... argN]
///
class MetaProcessor {
private:
///\brief Reference to the interpreter
///
Interpreter& m_Interp;
///\brief The input validator is used to figure out whether to switch to
/// multiline mode or not. Checks for balanced parenthesis, etc.
///
llvm::OwningPtr<InputValidator> m_InputValidator;
///\brief MetaProcessor's options
///
MetaProcessorOpts m_Options;
private:
///\brief Handle one of the special commands in cling.
/// Syntax .Command [arg0 arg1 ... argN]
///
/// Known commands:
/// @code .q @endcode - Quits
/// @code .L <filename> @endcode - Loads the filename (It might be lib too)
/// @code .(x|X) <filename>[(args)] @endcode - Loads the filename and
/// executes the function with signature void filename(args)
/// @code .printAST [0|1] @endcode - Toggles the printing of input's
/// corresponding AST nodes.
/// @code .rawInput [0|1] @endcode - Toggles wrapping and value printing of
/// the input
/// @code .I [path] @endcode - Dumps the include path. If argument is given
/// adds the path to the list of include paths.
/// @code .@ @endcode - Cancels multiline input
/// @code .dynamicExtensions [0|1] @endcode - Toggles the use of the dynamic
/// scopes and the late bining.
/// @code .help @endcode - Show information about the usage of the commands
/// @code .file @endcode - Show information about the loaded files
///
///\returns true if the command was known and thus handled.
///
bool ProcessMeta(const std::string& input_line, cling::Value* result);
///\brief This method is used to get the token's value. That is usually done
/// by the Lexer by attaching the IdentifierInfo directly. However we are
/// in raw lexing mode and we cannot do that.
///
///\returns This function is the dummy implementation of
/// Token.getIdentifierInfo()->getName() for the raw lexer
///
std::string GetRawTokenName(const clang::Token& Tok);
llvm::StringRef ReadToEndOfBuffer(clang::Lexer& RawLexer,
llvm::MemoryBuffer* MB);
///\brief Removes leading and trailing spaces, new lines and tabs if any
///
llvm::StringRef SanitizeArg(const std::string& Str);
///\brief Shows help for the use of interpreter's meta commands
///
void PrintCommandHelp();
///\brief Shows statistics about the loaded files
///
void PrintFileStats();
public:
MetaProcessor(Interpreter& interp);
~MetaProcessor();
MetaProcessorOpts& getMetaProcessorOpts();
///\brief Process the input coming from the prompt and possibli returns
/// result of the execution of the last statement
/// @param[in] input_line - the user input
/// @param[out] result - the cling::Value as result of the execution of the
/// last statement
///
///\returns 0 on success or the indentation of the next input line should
/// have in case of multi input mode.
///
int process(const char* input_line, cling::Value* result = 0);
///\brief Executes a file given the CINT specific rules. Mainly used as:
/// .x filename[(args)], which in turn includes the filename and runs a
/// function with signature void filename(args)
/// @param[in] fileWithArgs - the filename(args)
/// @param[out] result - the cling::Value as result of the execution of the
/// last statement
///
///\returns true on success
///
bool executeFile(const std::string& fileWithArgs, cling::Value* result = 0);
};
} // end namespace cling
#endif // CLING_METAPROCESSOR_H

View File

@ -0,0 +1,36 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Lukasz Janyst <ljanyst@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_USERINTERFACE_H
#define CLING_USERINTERFACE_H
namespace cling {
class Interpreter;
class MetaProcessor;
///\brief Makes the interpreter interactive
///
class UserInterface {
private:
MetaProcessor* m_MetaProcessor;
///\brief Prints cling's startup logo
///
void PrintLogo();
public:
UserInterface(Interpreter& interp);
~UserInterface();
MetaProcessor* getMetaProcessor() { return m_MetaProcessor; }
///\brief Drives the interactive prompt talking to the user.
/// @param[in] nologo - whether to show cling's welcome logo or not
///
void runInteractively(bool nologo = false);
};
}
#endif // CLING_USERINTERFACE_H

74
include/cling/Utils/AST.h Normal file
View File

@ -0,0 +1,74 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_UTILS_AST_H
#define CLING_UTILS_AST_H
#include "llvm/ADT/SmallSet.h"
namespace clang {
class ASTContext;
class Expr;
class DeclContext;
class NamedDecl;
class NamespaceDecl;
class QualType;
class Sema;
class Type;
}
namespace cling {
namespace utils {
///\brief Class containing static utility functions synthesizing AST nodes or
/// types.
///
class Synthesize {
public:
///\brief Synthesizes c-style cast in the AST from given pointer and type to
/// cast to.
///
static clang::Expr* CStyleCastPtrExpr(clang::Sema* S,
clang::QualType Ty, uint64_t Ptr);
};
///\brief Class containing static utility functions transforming AST nodes or
/// types.
///
class Transform {
public:
///\brief "Desugars" a type while skipping the ones in the set.
///
/// Desugars a given type recursively until strips all sugar or until gets a
/// sugared type, which is to be skipped.
///\param[in] Ctx - The ASTContext.
///\param[in] QT - The type to be partially desugared.
///\param[in] TypesToSkip - The set of sugared types which shouldn't be
/// desugared.
///\param[in] fullyQualify - if true insert Elaborated where needed.
///\returns Partially desugared QualType
///
static clang::QualType GetPartiallyDesugaredType(const clang::ASTContext& Ctx,
clang::QualType QT,
const llvm::SmallSet<const clang::Type*, 4>& TypesToSkip,
bool fullyQualify = true);
};
class Lookup {
public:
static clang::NamespaceDecl* Namespace(clang::Sema* S,
const char* Name,
clang::DeclContext* Within = 0);
static clang::NamedDecl* Named(clang::Sema* S,
const char* Name,
clang::DeclContext* Within = 0);
};
} // end namespace utils
} // end namespace cling
#endif // CLING_UTILS_AST_H

View File

@ -0,0 +1,5 @@
#ifdef __CINT__
#include <map>
#else
#error "This header (multimap) is only expected to be used when emulating CINT"
#endif

View File

@ -0,0 +1,5 @@
#ifdef __CINT__
#include <set>
#else
#error "This header (multiset) is only expected to be used when emulating CINT"
#endif

4
lib/CMakeLists.txt Normal file
View File

@ -0,0 +1,4 @@
add_subdirectory(Interpreter)
add_subdirectory(MetaProcessor)
add_subdirectory(UserInterface)
add_subdirectory(Utils)

View File

@ -0,0 +1,52 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#include "ASTDumper.h"
#include "Transaction.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Stmt.h"
#include "clang/Sema/Sema.h"
using namespace clang;
namespace cling {
// pin the vtable to this file
ASTDumper::~ASTDumper() {}
void ASTDumper::Transform() {
if (!getTransaction()->getCompilationOpts().Debug)
return;
for (Transaction::const_iterator I = getTransaction()->decls_begin(),
E = getTransaction()->decls_end(); I != E; ++I)
for (DeclGroupRef::const_iterator J = (*I).begin(),
JE = (*I).end(); J != JE; ++J)
printDecl(*J);
}
void ASTDumper::printDecl(Decl* D) {
PrintingPolicy Policy = D->getASTContext().getPrintingPolicy();
if (m_Dump)
Policy.DumpSourceManager = &m_Sema->getSourceManager();
else
Policy.DumpSourceManager = 0;
if (D) {
llvm::outs() << "\n-------------------Declaration---------------------\n";
D->dump();
if (Stmt* Body = D->getBody()) {
llvm::outs() << "\n------------------Declaration Body---------------\n";
Body->dump();
}
llvm::outs() << "\n---------------------------------------------------\n";
}
}
} // namespace cling

View File

@ -0,0 +1,39 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_AST_DUMPER_H
#define CLING_AST_DUMPER_H
#include "TransactionTransformer.h"
namespace clang {
class Decl;
}
namespace cling {
class Transaction;
// TODO : This is not really a transformer. Factor out.
class ASTDumper : public TransactionTransformer {
private:
bool m_Dump;
public:
ASTDumper(bool Dump = false)
: TransactionTransformer(0), m_Dump(Dump) { }
virtual ~ASTDumper();
virtual void Transform();
private:
void printDecl(clang::Decl* D);
};
} // namespace cling
#endif // CLING_AST_DUMPER_H

View File

@ -0,0 +1,522 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vvasilev@cern.ch>
//------------------------------------------------------------------------------
#include "ASTNodeEraser.h"
#include "Transaction.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/AST/DependentDiagnostic.h"
#include "clang/AST/Mangle.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/FileManager.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/Sema.h"
#include "llvm/Constants.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Module.h"
using namespace clang;
namespace cling {
///\brief The class does the actual work of removing a declaration and
/// resetting the internal structures of the compiler
///
class DeclReverter : public DeclVisitor<DeclReverter, bool> {
private:
typedef llvm::DenseSet<const FileEntry*> FileEntries;
///\brief The Sema object being reverted (contains the AST as well).
///
Sema* m_Sema;
///\brief The current transaction being reverted.
///
const Transaction* m_CurTransaction;
///\brief The mangler used to get the mangled names of the declarations
/// that we are removing from the module.
///
llvm::OwningPtr<MangleContext> m_Mangler;
///\brief Reverted declaration contains a SourceLocation, representing a
/// place in the file where it was seen. Clang caches that file and even if
/// a declaration is removed and the file is edited we hit the cached entry.
/// This ADT keeps track of the files from which the reverted declarations
/// came from so that in the end they could be removed from clang's cache.
///
FileEntries m_FilesToUncache;
public:
DeclReverter(Sema* S, const Transaction* T): m_Sema(S), m_CurTransaction(T) {
m_Mangler.reset(m_Sema->getASTContext().createMangleContext());
}
~DeclReverter();
///\brief Interface with nice name, forwarding to Visit.
///
///\param[in] D - The declaration to forward.
///\returns true on success.
///
bool RevertDecl(Decl* D) { return Visit(D); }
///\brief Function that contains common actions, done for every removal of
/// declaration.
///
/// For example: We must uncache the cached include, which brought that
/// declaration in the AST.
///\param[in] D - A declaration.
///
void PreVisitDecl(Decl* D);
///\brief If it falls back in the base class just remove the declaration
/// only from the declaration context.
/// @param[in] D - The declaration to be removed.
///
///\returns true on success.
///
bool VisitDecl(Decl* D);
///\brief Removes the declaration from the lookup chains and from the
/// declaration context.
/// @param[in] ND - The declaration to be removed.
///
///\returns true on success.
///
bool VisitNamedDecl(NamedDecl* ND);
///\brief Removes the declaration from the lookup chains and from the
/// declaration context and it rebuilds the redeclaration chain.
/// @param[in] VD - The declaration to be removed.
///
///\returns true on success.
///
bool VisitVarDecl(VarDecl* VD);
///\brief Removes the declaration from the lookup chains and from the
/// declaration context and it rebuilds the redeclaration chain.
/// @param[in] FD - The declaration to be removed.
///
///\returns true on success.
///
bool VisitFunctionDecl(FunctionDecl* FD);
///\brief Removes the enumerator and its enumerator constants.
/// @param[in] ED - The declaration to be removed.
///
///\returns true on success.
///
bool VisitEnumDecl(EnumDecl* ED);
///\brief Removes the namespace.
/// @param[in] NSD - The declaration to be removed.
///
///\returns true on success.
///
bool VisitNamespaceDecl(NamespaceDecl* NSD);
/// @name Helpers
/// @{
///\brief Checks whether the declaration was pushed onto the declaration
/// chains.
/// @param[in] ND - The declaration that is being checked.
///
///\returns true if the ND was found in the lookup chain.
///
bool isOnScopeChains(clang::NamedDecl* ND);
///\brief Removes given declaration from the chain of redeclarations.
/// Rebuilds the chain and sets properly first and last redeclaration.
/// @param[in] R - The redeclarable, its chain to be rebuilt.
///
///\returns the most recent redeclaration in the new chain.
///
template <typename T>
T* RemoveFromRedeclChain(clang::Redeclarable<T>* R) {
llvm::SmallVector<T*, 4> PrevDecls;
T* PrevDecl = 0;
// [0]=>C [1]=>B [2]=>A ...
while ((PrevDecl = R->getPreviousDecl())) {
PrevDecls.push_back(PrevDecl);
R = PrevDecl;
}
if (!PrevDecls.empty()) {
// Put 0 in the end of the array so that the loop will reset the
// pointer to latest redeclaration in the chain to itself.
//
PrevDecls.push_back(0);
// 0 <- A <- B <- C
for(unsigned i = PrevDecls.size() - 1; i > 0; --i) {
PrevDecls[i-1]->setPreviousDeclaration(PrevDecls[i]);
}
}
return PrevDecls.empty() ? 0 : PrevDecls[0]->getMostRecentDecl();
}
/// @}
};
DeclReverter::~DeclReverter() {
FileManager& FM = m_Sema->getSourceManager().getFileManager();
for (FileEntries::iterator I = m_FilesToUncache.begin(),
E = m_FilesToUncache.end(); I != E; ++I) {
FM.invalidateCache(*I);
}
// Clean up the pending instantiations
m_Sema->PendingInstantiations.clear();
m_Sema->PendingLocalImplicitInstantiations.clear();
}
void DeclReverter::PreVisitDecl(Decl *D) {
const SourceLocation Loc = D->getLocStart();
const SourceManager& SM = m_Sema->getSourceManager();
const FileEntry* OldEntry = SM.getFileEntryForID(SM.getFileID(Loc));
if (OldEntry && !m_FilesToUncache.count(OldEntry))
m_FilesToUncache.insert(OldEntry);
}
// Gives us access to the protected members that we need.
class DeclContextExt : public DeclContext {
public:
static bool removeIfLast(DeclContext* DC, Decl* D) {
if (!D->getNextDeclInContext()) {
// Either last (remove!), or invalid (nothing to remove)
if (((DeclContextExt*)DC)->LastDecl == D) {
// Valid. Thus remove.
DC->removeDecl(D);
return true;
}
}
else {
DC->removeDecl(D);
return true;
}
return false;
}
};
bool DeclReverter::VisitDecl(Decl* D) {
assert(D && "The Decl is null");
PreVisitDecl(D);
DeclContext* DC = D->getDeclContext();
bool ExistsInDC = false;
for (DeclContext::decl_iterator I = DC->decls_begin(), E = DC->decls_end();
E !=I; ++I) {
if (*I == D) {
ExistsInDC = true;
break;
}
}
bool Successful = DeclContextExt::removeIfLast(DC, D);
// ExistsInDC && Successful
// true false -> false // In the context but cannot delete
// false false -> true // Not in the context cannot delete
// true true -> true // In the context and can delete
// false true -> assert // Not in the context but can delete ?
assert(!(!ExistsInDC && Successful) && \
"Not in the context but can delete?!");
if (ExistsInDC && !Successful)
return false;
else { // in release we'd want the assert to fall into true
m_Sema->getDiagnostics().Reset();
return true;
}
}
bool DeclReverter::VisitNamedDecl(NamedDecl* ND) {
bool Successful = VisitDecl(ND);
DeclContext* DC = ND->getDeclContext();
// If the decl was removed make sure that we fix the lookup
if (Successful) {
Scope* S = m_Sema->getScopeForContext(DC);
if (S)
S->RemoveDecl(ND);
if (isOnScopeChains(ND))
m_Sema->IdResolver.RemoveDecl(ND);
}
// if it was successfully removed from the AST we have to check whether
// code was generated and remove it.
if (Successful && m_CurTransaction->getState() == Transaction::kCommitted) {
std::string mangledName = ND->getName();
if (m_Mangler->shouldMangleDeclName(ND)) {
mangledName = "";
llvm::raw_string_ostream RawStr(mangledName);
m_Mangler->mangleName(ND, RawStr);
RawStr.flush();
}
llvm::GlobalValue* GV
= m_CurTransaction->getModule()->getNamedValue(mangledName);
GV->replaceAllUsesWith(llvm::UndefValue::get(GV->getType()));
GV->eraseFromParent();
}
return Successful;
}
bool DeclReverter::VisitVarDecl(VarDecl* VD) {
bool Successful = VisitNamedDecl(VD);
DeclContext* DC = VD->getDeclContext();
Scope* S = m_Sema->getScopeForContext(DC);
// Find other decls that the old one has replaced
StoredDeclsMap *Map = DC->getPrimaryContext()->getLookupPtr();
if (!Map)
return false;
StoredDeclsMap::iterator Pos = Map->find(VD->getDeclName());
assert(Pos != Map->end() && "no lookup entry for decl");
if (Pos->second.isNull())
// We need to rewire the list of the redeclarations in order to exclude
// the reverted one, because it gets found for example by
// Sema::MergeVarDecl and ends up in the lookup
//
if (VarDecl* MostRecentVD = RemoveFromRedeclChain(VD)) {
Pos->second.setOnlyValue(MostRecentVD);
if (S)
S->AddDecl(MostRecentVD);
m_Sema->IdResolver.AddDecl(MostRecentVD);
}
return Successful;
}
bool DeclReverter::VisitFunctionDecl(FunctionDecl* FD) {
bool Successful = true;
DeclContext* DC = FD->getDeclContext();
Scope* S = m_Sema->getScopeForContext(DC);
// Template instantiation of templated function first creates a canonical
// declaration and after the actual template specialization. For example:
// template<typename T> T TemplatedF(T t);
// template<> int TemplatedF(int i) { return i + 1; } creates:
// 1. Canonical decl: int TemplatedF(int i);
// 2. int TemplatedF(int i){ return i + 1; }
//
// The template specialization is attached to the list of specialization of
// the templated function.
// When TemplatedF is looked up it finds the templated function and the
// lookup is extended by the templated function with its specializations.
// In the end we don't need to remove the canonical decl because, it
// doesn't end up in the lookup table.
//
class FunctionTemplateDeclExt : public FunctionTemplateDecl {
public:
static void removeSpecialization(FunctionTemplateDecl* self,
const FunctionTemplateSpecializationInfo* info) {
assert(self && "Cannot be null!");
typedef llvm::SmallVector<FunctionTemplateSpecializationInfo*, 4> FTSI;
FunctionTemplateDeclExt* This = (FunctionTemplateDeclExt*) self;
// We can't just copy because in the end of the scope we will call the
// dtor of the elements.
FunctionTemplateSpecializationInfo* specInfos
= &(*This->getSpecializations().begin());
size_t specInfoSize = This->getSpecializations().size();
This->getSpecializations().clear();
for (size_t i = 0; i < specInfoSize; ++i)
if (&specInfos[i] != info) {
This->addSpecialization(&specInfos[i], /*InsertPos*/(void*)0);
}
}
};
if (FD->isFunctionTemplateSpecialization()) {
// 1. Remove the canonical decl.
// TODO: Can the canonical have another DeclContext and Scope, different
// from the specialization's implementation?
FunctionDecl* CanFD = FD->getCanonicalDecl();
FunctionTemplateDecl* FTD
= FD->getTemplateSpecializationInfo()->getTemplate();
FunctionTemplateDeclExt::removeSpecialization(FTD,
CanFD->getTemplateSpecializationInfo());
}
// Find other decls that the old one has replaced
StoredDeclsMap *Map = DC->getPrimaryContext()->getLookupPtr();
if (!Map)
return false;
StoredDeclsMap::iterator Pos = Map->find(FD->getDeclName());
assert(Pos != Map->end() && "no lookup entry for decl");
if (Pos->second.getAsDecl()) {
Successful = VisitNamedDecl(FD) && Successful;
Pos = Map->find(FD->getDeclName());
assert(Pos != Map->end() && "no lookup entry for decl");
if (Pos->second.isNull()) {
// When we have template specialization we have to clean up
if (FD->isFunctionTemplateSpecialization()) {
while ((FD = FD->getPreviousDecl())) {
Successful = VisitNamedDecl(FD) && Successful;
}
return true;
}
// We need to rewire the list of the redeclarations in order to exclude
// the reverted one, because it gets found for example by
// Sema::MergeVarDecl and ends up in the lookup
//
if (FunctionDecl* MostRecentFD = RemoveFromRedeclChain(FD)) {
Pos->second.setOnlyValue(MostRecentFD);
if (S)
S->AddDecl(MostRecentFD);
m_Sema->IdResolver.AddDecl(MostRecentFD);
}
}
}
else if (llvm::SmallVector<NamedDecl*, 4>* Decls
= Pos->second.getAsVector()) {
for(llvm::SmallVector<NamedDecl*, 4>::iterator I = Decls->begin();
I != Decls->end(); ++I) {
if ((*I) == FD) {
if (FunctionDecl* MostRecentFD = RemoveFromRedeclChain(FD)) {
Successful = VisitNamedDecl(*I) && Successful;
Decls->insert(I, MostRecentFD);
}
else
Decls->erase(I);
}
}
}
return Successful;
}
bool DeclReverter::VisitEnumDecl(EnumDecl* ED) {
bool Successful = true;
for (EnumDecl::enumerator_iterator I = ED->enumerator_begin(),
E = ED->enumerator_end(); I != E; ++I) {
assert(I->getDeclName() && "EnumConstantDecl with no name?");
Successful = VisitNamedDecl(*I) && Successful;
}
Successful = VisitNamedDecl(ED) && Successful;
return Successful;
}
bool DeclReverter::VisitNamespaceDecl(NamespaceDecl* NSD) {
bool Successful = VisitNamedDecl(NSD);
//DeclContext* DC = NSD->getPrimaryContext();
DeclContext* DC = NSD->getDeclContext();
Scope* S = m_Sema->getScopeForContext(DC);
// Find other decls that the old one has replaced
StoredDeclsMap *Map = DC->getPrimaryContext()->getLookupPtr();
if (!Map)
return false;
StoredDeclsMap::iterator Pos = Map->find(NSD->getDeclName());
assert(Pos != Map->end() && "no lookup entry for decl");
if (Pos->second.isNull())
if (NSD != NSD->getOriginalNamespace()) {
NamespaceDecl* NewNSD = NSD->getOriginalNamespace();
Pos->second.setOnlyValue(NewNSD);
if (S)
S->AddDecl(NewNSD);
m_Sema->IdResolver.AddDecl(NewNSD);
}
return Successful;
}
// See Sema::PushOnScopeChains
bool DeclReverter::isOnScopeChains(NamedDecl* ND) {
// Named decls without name shouldn't be in. Eg: struct {int a};
if (!ND->getDeclName())
return false;
// Out-of-line definitions shouldn't be pushed into scope in C++.
// Out-of-line variable and function definitions shouldn't even in C.
if ((isa<VarDecl>(ND) || isa<FunctionDecl>(ND)) && ND->isOutOfLine() &&
!ND->getDeclContext()->getRedeclContext()->Equals(
ND->getLexicalDeclContext()->getRedeclContext()))
return false;
// Template instantiations should also not be pushed into scope.
if (isa<FunctionDecl>(ND) &&
cast<FunctionDecl>(ND)->isFunctionTemplateSpecialization())
return false;
IdentifierResolver::iterator
IDRi = m_Sema->IdResolver.begin(ND->getDeclName()),
IDRiEnd = m_Sema->IdResolver.end();
for (; IDRi != IDRiEnd; ++IDRi) {
if (ND == *IDRi)
return true;
}
// Check if the declaration is template instantiation, which is not in
// any DeclContext yet, because it came from
// Sema::PerformPendingInstantiations
// if (isa<FunctionDecl>(D) &&
// cast<FunctionDecl>(D)->getTemplateInstantiationPattern())
// return false;ye
return false;
}
ASTNodeEraser::ASTNodeEraser(Sema* S) : m_Sema(S) {
}
ASTNodeEraser::~ASTNodeEraser() {
}
bool ASTNodeEraser::RevertTransaction(const Transaction* T) {
DeclReverter DeclRev(m_Sema, T);
bool Successful = true;
for (Transaction::const_reverse_iterator I = T->rdecls_begin(),
E = T->rdecls_end(); I != E; ++I) {
const DeclGroupRef& DGR = (*I);
for (DeclGroupRef::const_iterator
Di = DGR.end() - 1, E = DGR.begin() - 1; Di != E; --Di) {
// Get rid of the declaration. If the declaration has name we should
// heal the lookup tables as well
Successful = DeclRev.RevertDecl(*Di) && Successful;
assert(Successful && "Cannot handle that yet!");
}
}
return Successful;
}
} // end namespace cling

View File

@ -0,0 +1,44 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vvasilev@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_AST_NODE_ERASER
#define CLING_AST_NODE_ERASER
namespace clang {
class Sema;
}
namespace cling {
class Transaction;
///\brief A simple eraser class that removes already created AST Nodes.
///
class ASTNodeEraser {
private:
clang::Sema* m_Sema;
public:
ASTNodeEraser(clang::Sema* S);
~ASTNodeEraser();
///\brief Rolls back given transaction from the AST.
///
/// Removing includes reseting various internal stuctures in the compiler to
/// their previous states. For example it resets the lookup tables if the
/// declaration has name and can be looked up; Reverts the redeclaration
/// chain if the declaration was redeclarable and so on.
/// Note1: that the code generated for the declaration is not removed yet.
/// Note2: does not do dependency analysis.
///
///\param[in] T - The transaction to be removed.
///\returns true on success.
///
bool RevertTransaction(const Transaction* T);
};
} // end namespace cling
#endif // CLING_AST_NODE_ERASER

View File

@ -0,0 +1,274 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------
#include "cling/Interpreter/CIFactory.h"
#include "DeclCollector.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/Version.h"
#include "clang/Driver/ArgList.h"
#include "clang/Driver/Compilation.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/Job.h"
#include "clang/Driver/Tool.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/LLVMContext.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
using namespace clang;
namespace cling {
//
// Dummy function so we can use dladdr to find the executable path.
//
void locate_cling_executable()
{
}
/// \brief Retrieves the clang CC1 specific flags out of the compilation's
/// jobs. Returns NULL on error.
static const clang::driver::ArgStringList
*GetCC1Arguments(clang::DiagnosticsEngine *Diagnostics,
clang::driver::Compilation *Compilation) {
// We expect to get back exactly one Command job, if we didn't something
// failed. Extract that job from the Compilation.
const clang::driver::JobList &Jobs = Compilation->getJobs();
if (!Jobs.size() || !isa<clang::driver::Command>(*Jobs.begin())) {
// diagnose this...
return NULL;
}
// The one job we find should be to invoke clang again.
const clang::driver::Command *Cmd
= cast<clang::driver::Command>(*Jobs.begin());
if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") {
// diagnose this...
return NULL;
}
return &Cmd->getArguments();
}
CompilerInstance* CIFactory::createCI(llvm::StringRef code,
int argc,
const char* const *argv,
const char* llvmdir) {
return createCI(llvm::MemoryBuffer::getMemBuffer(code), argc, argv,
llvmdir);
}
CompilerInstance* CIFactory::createCI(llvm::MemoryBuffer* buffer,
int argc,
const char* const *argv,
const char* llvmdir) {
// Create an instance builder, passing the llvmdir and arguments.
//
// Initialize the llvm library.
llvm::InitializeNativeTarget();
llvm::InitializeAllAsmPrinters();
llvm::sys::Path resource_path;
if (llvmdir) {
resource_path = llvmdir;
resource_path.appendComponent("lib");
resource_path.appendComponent("clang");
resource_path.appendComponent(CLANG_VERSION_STRING);
} else {
// FIXME: The first arg really does need to be argv[0] on FreeBSD.
//
// Note: The second arg is not used for Apple, FreeBSD, Linux,
// or cygwin, and can only be used on systems which support
// the use of dladdr().
//
// Note: On linux and cygwin this uses /proc/self/exe to find the path.
//
// Note: On Apple it uses _NSGetExecutablePath().
//
// Note: On FreeBSD it uses getprogpath().
//
// Note: Otherwise it uses dladdr().
//
resource_path
= CompilerInvocation::GetResourcesPath("cling",
(void*)(intptr_t) locate_cling_executable
);
}
if (!resource_path.canRead()) {
llvm::errs()
<< "ERROR in cling::CIFactory::createCI():\n resource directory "
<< resource_path.str() << " not found!\n";
resource_path = "";
}
//______________________________________
DiagnosticOptions DefaultDiagnosticOptions;
DefaultDiagnosticOptions.ShowColors = 1;
TextDiagnosticPrinter* DiagnosticPrinter
= new TextDiagnosticPrinter(llvm::errs(), DefaultDiagnosticOptions);
llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs(new DiagnosticIDs());
DiagnosticsEngine* Diagnostics
= new DiagnosticsEngine(DiagIDs, DiagnosticPrinter,
/*Owns it*/ true); // LEAKS!
std::vector<const char*> argvCompile(argv, argv + argc);
// We do C++ by default; append right after argv[0] name
// Only insert it if there is no other "-x":
bool haveMinusX = false;
for (const char* const* iarg = argv; !haveMinusX && iarg < argv + argc;
++iarg) {
haveMinusX = !strcmp(*iarg, "-x");
}
if (!haveMinusX) {
argvCompile.insert(argvCompile.begin() + 1,"-x");
argvCompile.insert(argvCompile.begin() + 2, "c++");
}
argvCompile.push_back("-c");
argvCompile.push_back("-");
bool IsProduction = false;
assert(IsProduction = true && "set IsProduction if asserts are on.");
clang::driver::Driver Driver(argv[0], llvm::sys::getDefaultTargetTriple(),
"cling.out",
IsProduction,
*Diagnostics);
//Driver.setWarnMissingInput(false);
Driver.setCheckInputsExist(false); // think foo.C(12)
llvm::ArrayRef<const char*>RF(&(argvCompile[0]), argvCompile.size());
llvm::OwningPtr<clang::driver::Compilation>
Compilation(Driver.BuildCompilation(RF));
const clang::driver::ArgStringList* CC1Args
= GetCC1Arguments(Diagnostics, Compilation.get());
if (CC1Args == NULL) {
return 0;
}
clang::CompilerInvocation*
Invocation = new clang::CompilerInvocation; // LEAKS!
clang::CompilerInvocation::CreateFromArgs(*Invocation, CC1Args->data() + 1,
CC1Args->data() + CC1Args->size(),
*Diagnostics);
Invocation->getFrontendOpts().DisableFree = true;
if (Invocation->getHeaderSearchOpts().UseBuiltinIncludes &&
!resource_path.empty()) {
// Update ResourceDir
// header search opts' entry for resource_path/include isn't
// updated by providing a new resource path; update it manually.
clang::HeaderSearchOptions& Opts = Invocation->getHeaderSearchOpts();
llvm::sys::Path oldResInc(Opts.ResourceDir);
oldResInc.appendComponent("include");
llvm::sys::Path newResInc(resource_path);
newResInc.appendComponent("include");
bool foundOldResInc = false;
for (unsigned i = 0, e = Opts.UserEntries.size();
!foundOldResInc && i != e; ++i) {
HeaderSearchOptions::Entry &E = Opts.UserEntries[i];
if (!E.IsUserSupplied && !E.IsFramework
&& E.Group == clang::frontend::System && E.IgnoreSysRoot
&& E.IsInternal && !E.ImplicitExternC
&& oldResInc.str() == E.Path) {
E.Path = newResInc.str();
foundOldResInc = true;
}
}
Opts.ResourceDir = resource_path.str();
}
// Create and setup a compiler instance.
CompilerInstance* CI = new CompilerInstance();
CI->setInvocation(Invocation);
CI->createDiagnostics(CC1Args->size(), CC1Args->data() + 1);
{
//
// Buffer the error messages while we process
// the compiler options.
//
// Set the language options, which cling needs
SetClingCustomLangOpts(CI->getLangOpts());
CI->getInvocation().getPreprocessorOpts().addMacroDef("__CLING__");
if (CI->getDiagnostics().hasErrorOccurred()) {
delete CI;
CI = 0;
return 0;
}
}
CI->setTarget(TargetInfo::CreateTargetInfo(CI->getDiagnostics(),
Invocation->getTargetOpts()));
if (!CI->hasTarget()) {
delete CI;
CI = 0;
return 0;
}
CI->getTarget().setForcedLangOptions(CI->getLangOpts());
SetClingTargetLangOpts(CI->getLangOpts(), CI->getTarget());
// Set up source and file managers
CI->createFileManager();
CI->createSourceManager(CI->getFileManager());
// Set up the memory buffer
if (buffer)
CI->getSourceManager().createMainFileIDForMemBuffer(buffer);
// Set up the preprocessor
CI->createPreprocessor();
Preprocessor& PP = CI->getPreprocessor();
PP.getBuiltinInfo().InitializeBuiltins(PP.getIdentifierTable(),
PP.getLangOpts());
// Set up the ASTContext
ASTContext *Ctx = new ASTContext(CI->getLangOpts(),
PP.getSourceManager(), &CI->getTarget(),
PP.getIdentifierTable(),
PP.getSelectorTable(), PP.getBuiltinInfo(),
/*size_reserve*/0, /*DelayInit*/false);
CI->setASTContext(Ctx);
// Set up the ASTConsumers
CI->setASTConsumer(new DeclCollector());
// Set up Sema
CodeCompleteConsumer* CCC = 0;
CI->createSema(TU_Prefix, CCC);
// Set CodeGen options
// CI->getCodeGenOpts().DebugInfo = 1; // want debug info
// CI->getCodeGenOpts().EmitDeclMetadata = 1; // For unloading, for later
CI->getCodeGenOpts().OptimizationLevel = 0; // see pure SSA, that comes out
// When asserts are on, TURN ON not compare the VerifyModule
assert(CI->getCodeGenOpts().VerifyModule = 1);
return CI;
}
void CIFactory::SetClingCustomLangOpts(LangOptions& Opts) {
Opts.EmitAllDecls = 1;
Opts.Exceptions = 1;
Opts.CXXExceptions = 1;
Opts.Deprecated = 1;
}
void CIFactory::SetClingTargetLangOpts(LangOptions& Opts,
const TargetInfo& Target) {
if (Target.getTriple().getOS() == llvm::Triple::Win32) {
Opts.MicrosoftExt = 1;
Opts.MSCVersion = 1300;
// Should fix http://llvm.org/bugs/show_bug.cgi?id=10528
Opts.DelayedTemplateParsing = 1;
} else {
Opts.MicrosoftExt = 0;
}
}
} // end namespace

View File

@ -0,0 +1,43 @@
set( LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
asmparser
bitreader
bitwriter
codegen
linker
support
mc
executionengine
target
jit
x86info
x86asmprinter
)
#set(LLVM_USED_LIBS
# clingWhatever
#)
add_cling_library(clingInterpreter
ASTDumper.cpp
ASTNodeEraser.cpp
Callable.cpp
CIFactory.cpp
DeclCollector.cpp
DeclExtractor.cpp
DynamicLookup.cpp
DynamicExprInfo.cpp
ExecutionContext.cpp
IncrementalParser.cpp
Interpreter.cpp
InvocationOptions.cpp
LookupHelper.cpp
Transaction.cpp
ValuePrinter.cpp
ValuePrinterInfo.cpp
ValuePrinterSynthesizer.cpp
)
#add_dependencies(clangDriver ClangAttrList ClangDiagnosticDriver
# ClangDriverOptions ClangCC1Options ClangCC1AsOptions)

View File

@ -0,0 +1,50 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id: Interpreter.cpp 44226 2012-05-11 16:41:08Z vvassilev $
// author: Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------
#include "cling/Interpreter/Callable.h"
#include "llvm/Function.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/GenericValue.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Mangle.h"
#include "clang/Frontend/CompilerInstance.h"
#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/Value.h"
cling::Callable::Callable(const clang::FunctionDecl& Decl,
const cling::Interpreter& Interp):
decl(&Decl),
func(0),
exec(Interp.getExecutionEngine())
{
if (exec) {
std::string mangledName;
llvm::raw_string_ostream RawStr(mangledName);
llvm::OwningPtr<clang::MangleContext>
Mangle(Interp.getCI()->getASTContext().createMangleContext());
Mangle->mangleName(decl, RawStr);
RawStr.flush();
func = exec->FindFunctionNamed(mangledName.c_str());
}
}
bool cling::Callable::Invoke(const std::vector<llvm::GenericValue>& ArgValues,
Value* Result /*= 0*/) const
{
if (!isValid()) return false;
if (Result) {
*Result = Value(exec->runFunction(func, ArgValues),
decl->getCallResultType());
} else {
exec->runFunction(func, ArgValues);
}
return true;
}

View File

@ -0,0 +1,52 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id: IncrementalParser.h 42072 2011-11-16 19:27:35Z vvassilev $
// author: Vassil Vassilev <vvasilev@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_COMPILATION_OPTIONS
#define CLING_COMPILATION_OPTIONS
namespace cling {
///\brief Options controlling the incremental compilation. Describe the set of
/// custom AST consumers to be enabled/disabled.
///
class CompilationOptions {
public:
///\brief Whether or not to extract the declarations out from the processed
/// input.
///
unsigned DeclarationExtraction : 1;
///\brief Whether or not to print the result of the run input
///
/// 0 -> Disabled; 1 -> Enabled; 2 -> Auto;
///
unsigned ValuePrinting : 2;
enum ValuePrinting { VPDisabled, VPEnabled, VPAuto };
///\brief Whether or not to extend the static scope with new information
/// about the names available only at runtime
///
unsigned DynamicScoping : 1;
///\brief Whether or not to print debug information on the fly
///
unsigned Debug : 1;
///\brief Whether or not to generate executable (LLVM IR) code for the input
/// or to cache the incoming declarations in a queue
///
unsigned CodeGeneration : 1;
CompilationOptions() {
DeclarationExtraction = 1;
ValuePrinting = VPAuto;
DynamicScoping = 0;
Debug = 0;
CodeGeneration = 1;
}
};
} // end namespace cling
#endif // CLING_COMPILATION_OPTIONS

View File

@ -0,0 +1,47 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#include "DeclCollector.h"
#include "Transaction.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclGroup.h"
using namespace clang;
namespace cling {
// pin the vtable here.
DeclCollector::~DeclCollector() {
}
bool DeclCollector::HandleTopLevelDecl(DeclGroupRef DGR) {
m_CurTransaction->appendUnique(DGR);
return true;
}
void DeclCollector::HandleInterestingDecl(DeclGroupRef DGR) {
assert("Not implemented yet!");
}
void DeclCollector::HandleTagDeclDefinition(TagDecl* TD) {
m_CurTransaction->appendUnique(DeclGroupRef(TD));
}
void DeclCollector::HandleVTable(CXXRecordDecl* RD,
bool DefinitionRequired) {
assert("Not implemented yet!");
}
void DeclCollector::CompleteTentativeDefinition(VarDecl* VD) {
assert("Not implemented yet!");
}
void DeclCollector::HandleTranslationUnit(ASTContext& Ctx) {
assert("Not implemented yet!");
}
} // namespace cling

View File

@ -0,0 +1,62 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_DECL_COLLECTOR_H
#define CLING_DECL_COLLECTOR_H
#include "clang/AST/ASTConsumer.h"
namespace clang {
class ASTContext;
class DeclGroupRef;
}
namespace cling {
class Transaction;
///\brief Collects declarations and fills them in cling::Transaction.
///
/// cling::Transaction becomes is a main building block in the interpreter.
/// cling::DeclCollector is responsible for appending all the declarations seen
/// by clang.
///
class DeclCollector: public clang::ASTConsumer {
private:
Transaction* m_CurTransaction;
public:
DeclCollector() : m_CurTransaction(0) {}
virtual ~DeclCollector();
/// \{
/// \name ASTConsumer overrides
virtual bool HandleTopLevelDecl(clang::DeclGroupRef DGR);
virtual void HandleInterestingDecl(clang::DeclGroupRef DGR);
virtual void HandleTagDeclDefinition(clang::TagDecl* TD);
virtual void HandleVTable(clang::CXXRecordDecl* RD,
bool DefinitionRequired);
virtual void CompleteTentativeDefinition(clang::VarDecl* VD);
virtual void HandleTranslationUnit(clang::ASTContext& Ctx);
/// \}
/// \{
/// \name Transaction Support
Transaction* getTransaction() { return m_CurTransaction; }
const Transaction* getTransaction() const { return m_CurTransaction; }
void setTransaction(Transaction* curT) { m_CurTransaction = curT; }
void setTransaction(const Transaction* curT) {
m_CurTransaction = const_cast<Transaction*>(curT);
}
/// \}
};
} // namespace cling
#endif // CLING_DECL_COLLECTOR_H

View File

@ -0,0 +1,561 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#include "DeclExtractor.h"
#include "Transaction.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclGroup.h"
#include "clang/AST/Decl.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaDiagnostic.h"
using namespace clang;
namespace cling {
DeclExtractor::DeclExtractor(Sema* S)
: TransactionTransformer(S), m_Context(&S->getASTContext())
{ }
// pin the vtable here
DeclExtractor::~DeclExtractor()
{ }
void DeclExtractor::Transform() {
if (!getTransaction()->getCompilationOpts().DeclarationExtraction)
return;
for (Transaction::const_iterator I = getTransaction()->decls_begin(),
E = getTransaction()->decls_end(); I != E; ++I)
for (DeclGroupRef::const_iterator J = (*I).begin(),
JE = (*I).end(); J != JE; ++J)
if(!ExtractDecl(*J))
setTransaction(0); // On error set to NULL.
}
bool DeclExtractor::ExtractDecl(Decl* D) {
FunctionDecl* FD = dyn_cast<FunctionDecl>(D);
if (FD) {
if (FD->getNameAsString().find("__cling_Un1Qu3"))
return true;
llvm::SmallVector<NamedDecl*, 4> TouchedDecls;
CompoundStmt* CS = dyn_cast<CompoundStmt>(FD->getBody());
assert(CS && "Function body not a CompoundStmt?");
DeclContext* DC = FD->getTranslationUnitDecl();
Scope* TUScope = m_Sema->TUScope;
assert(TUScope == m_Sema->getScopeForContext(DC) && "TU scope from DC?");
llvm::SmallVector<Stmt*, 4> Stmts;
for (CompoundStmt::body_iterator I = CS->body_begin(), EI = CS->body_end();
I != EI; ++I) {
DeclStmt* DS = dyn_cast<DeclStmt>(*I);
if (!DS) {
Stmts.push_back(*I);
continue;
}
for (DeclStmt::decl_iterator J = DS->decl_begin();
J != DS->decl_end(); ++J) {
NamedDecl* ND = dyn_cast<NamedDecl>(*J);
if (ND) {
DeclContext* OldDC = ND->getDeclContext();
// Make sure the decl is not found at its old possition
OldDC->removeDecl(ND);
if (Scope* S = m_Sema->getScopeForContext(OldDC)) {
S->RemoveDecl(ND);
m_Sema->IdResolver.RemoveDecl(ND);
}
if (ND->getDeclContext() == ND->getLexicalDeclContext())
ND->setLexicalDeclContext(DC);
else
assert("Not implemented: Decl with different lexical context");
ND->setDeclContext(DC);
if (VarDecl* VD = dyn_cast<VarDecl>(ND)) {
VD->setStorageClass(SC_None);
VD->setStorageClassAsWritten(SC_None);
// if we want to print the result of the initializer of int i = 5
// or the default initializer int i
if (I+1 == EI || !isa<NullStmt>(*(I+1))) {
QualType VDTy = VD->getType().getNonReferenceType();
Expr* DRE = m_Sema->BuildDeclRefExpr(VD, VDTy,VK_LValue,
SourceLocation()
).take();
Stmts.push_back(DRE);
}
}
// force recalc of the linkage (to external)
ND->ClearLinkageCache();
TouchedDecls.push_back(ND);
}
}
}
bool hasNoErrors = !CheckForClashingNames(TouchedDecls, DC, TUScope);
if (hasNoErrors) {
for (size_t i = 0; i < TouchedDecls.size(); ++i) {
m_Sema->PushOnScopeChains(TouchedDecls[i],
m_Sema->getScopeForContext(DC),
/*AddCurContext*/!isa<UsingDirectiveDecl>(TouchedDecls[i]));
// The transparent DeclContexts (eg. scopeless enum) doesn't have
// scopes. While extracting their contents we need to update the
// lookup tables and telling them to pick up the new possitions
// in the AST.
if (DeclContext* InnerDC = dyn_cast<DeclContext>(TouchedDecls[i])) {
if (InnerDC->isTransparentContext()) {
// We can't PushDeclContext, because we don't have scope.
Sema::ContextRAII pushedDC(*m_Sema, InnerDC);
for(DeclContext::decl_iterator DI = InnerDC->decls_begin(),
DE = InnerDC->decls_end(); DI != DE ; ++DI) {
if (NamedDecl* ND = dyn_cast<NamedDecl>(*DI))
InnerDC->makeDeclVisibleInContext(ND);
}
}
}
// Append the new top level decl to the current transaction.
getTransaction()->appendUnique(DeclGroupRef(TouchedDecls[i]));
}
}
CS->setStmts(*m_Context, Stmts.data(), Stmts.size());
// Put the wrapper after its declarations. (Nice when AST dumping)
DC->removeDecl(FD);
DC->addDecl(FD);
return hasNoErrors;
}
return true;
}
///\brief Checks for clashing names when trying to extract a declaration.
///
///\returns true if there is another declaration with the same name
bool DeclExtractor::CheckForClashingNames(
const llvm::SmallVector<NamedDecl*, 4>& Decls,
DeclContext* DC, Scope* S) {
for (size_t i = 0; i < Decls.size(); ++i) {
NamedDecl* ND = Decls[i];
if (TagDecl* TD = dyn_cast<TagDecl>(ND)) {
LookupResult Previous(*m_Sema, ND->getDeclName(), ND->getLocation(),
Sema::LookupTagName, Sema::ForRedeclaration
);
m_Sema->LookupName(Previous, S);
// There is no function diagnosing the redeclaration of tags (eg. enums).
// So either we have to do it by hand or we can call the top-most
// function that does the check. Currently the top-most clang function
// doing the checks creates an AST node, which we don't want.
CheckTagDeclaration(TD, Previous);
}
else if (VarDecl* VD = dyn_cast<VarDecl>(ND)) {
LookupResult Previous(*m_Sema, ND->getDeclName(), ND->getLocation(),
Sema::LookupOrdinaryName, Sema::ForRedeclaration
);
m_Sema->LookupName(Previous, S);
m_Sema->CheckVariableDeclaration(VD, Previous);
}
if (ND->isInvalidDecl())
return true;
}
return false;
}
bool DeclExtractor::CheckTagDeclaration(TagDecl* NewTD,
LookupResult& Previous){
// If the decl is already known invalid, don't check it.
if (NewTD->isInvalidDecl())
return false;
IdentifierInfo* Name = NewTD->getIdentifier();
// If this is not a definition, it must have a name.
assert((Name != 0 || NewTD->isThisDeclarationADefinition()) &&
"Nameless record must be a definition!");
// Figure out the underlying type if this a enum declaration. We need to do
// this early, because it's needed to detect if this is an incompatible
// redeclaration.
TagDecl::TagKind Kind = NewTD->getTagKind();
bool Invalid = false;
assert(NewTD->getNumTemplateParameterLists() == 0
&& "Cannot handle that yet!");
bool isExplicitSpecialization = false;
if (Kind == TTK_Enum) {
EnumDecl* ED = cast<EnumDecl>(NewTD);
bool ScopedEnum = ED->isScoped();
const QualType QT = ED->getIntegerType();
if (QT.isNull() && ScopedEnum)
// No underlying type explicitly specified, or we failed to parse the
// type, default to int.
; //EnumUnderlying = m_Context->IntTy.getTypePtr();
else if (!QT.isNull()) {
// C++0x 7.2p2: The type-specifier-seq of an enum-base shall name an
// integral type; any cv-qualification is ignored.
SourceLocation UnderlyingLoc;
TypeSourceInfo* TI = 0;
if ((TI = ED->getIntegerTypeSourceInfo()))
UnderlyingLoc = TI->getTypeLoc().getBeginLoc();
if (!QT->isDependentType() && !QT->isIntegralType(*m_Context)) {
m_Sema->Diag(UnderlyingLoc, diag::err_enum_invalid_underlying)
<< QT;
}
if (TI)
m_Sema->DiagnoseUnexpandedParameterPack(UnderlyingLoc, TI,
Sema::UPPC_FixedUnderlyingType);
}
}
DeclContext *SearchDC = m_Sema->CurContext;
DeclContext *DC = m_Sema->CurContext;
//bool isStdBadAlloc = false;
SourceLocation NameLoc = NewTD->getLocation();
// if (Name && SS.isNotEmpty()) {
// // We have a nested-name tag ('struct foo::bar').
// // Check for invalid 'foo::'.
// if (SS.isInvalid()) {
// Name = 0;
// goto CreateNewDecl;
// }
// // If this is a friend or a reference to a class in a dependent
// // context, don't try to make a decl for it.
// if (TUK == TUK_Friend || TUK == TUK_Reference) {
// DC = computeDeclContext(SS, false);
// if (!DC) {
// IsDependent = true;
// return 0;
// }
// } else {
// DC = computeDeclContext(SS, true);
// if (!DC) {
// Diag(SS.getRange().getBegin(),
// diag::err_dependent_nested_name_spec)
// << SS.getRange();
// return 0;
// }
// }
// if (RequireCompleteDeclContext(SS, DC))
// return 0;
// SearchDC = DC;
// // Look-up name inside 'foo::'.
// LookupQualifiedName(Previous, DC);
// if (Previous.isAmbiguous())
// return 0;
// if (Previous.empty()) {
// // Name lookup did not find anything. However, if the
// // nested-name-specifier refers to the current instantiation,
// // and that current instantiation has any dependent base
// // classes, we might find something at instantiation time: treat
// // this as a dependent elaborated-type-specifier.
// // But this only makes any sense for reference-like lookups.
// if (Previous.wasNotFoundInCurrentInstantiation() &&
// (TUK == TUK_Reference || TUK == TUK_Friend)) {
// IsDependent = true;
// return 0;
// }
// // A tag 'foo::bar' must already exist.
// Diag(NameLoc, diag::err_not_tag_in_scope)
// << Kind << Name << DC << SS.getRange();
// Name = 0;
// Invalid = true;
// goto CreateNewDecl;
// }
//} else
if (Name) {
// If this is a named struct, check to see if there was a previous forward
// declaration or definition.
// FIXME: We're looking into outer scopes here, even when we
// shouldn't be. Doing so can result in ambiguities that we
// shouldn't be diagnosing.
//LookupName(Previous, S);
if (Previous.isAmbiguous()) {
LookupResult::Filter F = Previous.makeFilter();
while (F.hasNext()) {
NamedDecl *ND = F.next();
if (ND->getDeclContext()->getRedeclContext() != SearchDC)
F.erase();
}
F.done();
}
// Note: there used to be some attempt at recovery here.
if (Previous.isAmbiguous()) {
NewTD->setInvalidDecl();
return false;
}
if (!m_Sema->getLangOpts().CPlusPlus) {
// FIXME: This makes sure that we ignore the contexts associated
// with C structs, unions, and enums when looking for a matching
// tag declaration or definition. See the similar lookup tweak
// in Sema::LookupName; is there a better way to deal with this?
while (isa<RecordDecl>(SearchDC) || isa<EnumDecl>(SearchDC))
SearchDC = SearchDC->getParent();
}
} else if (m_Sema->getScopeForContext(m_Sema->CurContext)
->isFunctionPrototypeScope()) {
// If this is an enum declaration in function prototype scope, set its
// initial context to the translation unit.
SearchDC = m_Context->getTranslationUnitDecl();
}
if (Previous.isSingleResult() &&
Previous.getFoundDecl()->isTemplateParameter()) {
// Maybe we will complain about the shadowed template parameter.
m_Sema->DiagnoseTemplateParameterShadow(NameLoc, Previous.getFoundDecl());
// Just pretend that we didn't see the previous declaration.
Previous.clear();
}
if (m_Sema->getLangOpts().CPlusPlus && Name && DC && m_Sema->StdNamespace
&& DC->Equals(m_Sema->getStdNamespace()) && Name->isStr("bad_alloc")) {
// This is a declaration of or a reference to "std::bad_alloc".
//isStdBadAlloc = true;
if (Previous.empty() && m_Sema->StdBadAlloc) {
// std::bad_alloc has been implicitly declared (but made invisible to
// name lookup). Fill in this implicit declaration as the previous
// declaration, so that the declarations get chained appropriately.
Previous.addDecl(m_Sema->getStdBadAlloc());
}
}
if (!Previous.empty()) {
NamedDecl *PrevDecl = (*Previous.begin())->getUnderlyingDecl();
// It's okay to have a tag decl in the same scope as a typedef
// which hides a tag decl in the same scope. Finding this
// insanity with a redeclaration lookup can only actually happen
// in C++.
//
// This is also okay for elaborated-type-specifiers, which is
// technically forbidden by the current standard but which is
// okay according to the likely resolution of an open issue;
// see http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#407
if (m_Sema->getLangOpts().CPlusPlus) {
if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(PrevDecl)) {
if (const TagType *TT = TD->getUnderlyingType()->getAs<TagType>()) {
TagDecl *Tag = TT->getDecl();
if (Tag->getDeclName() == Name &&
Tag->getDeclContext()->getRedeclContext()
->Equals(TD->getDeclContext()->getRedeclContext())) {
PrevDecl = Tag;
Previous.clear();
Previous.addDecl(Tag);
Previous.resolveKind();
}
}
}
}
if (TagDecl *PrevTagDecl = dyn_cast<TagDecl>(PrevDecl)) {
// If this is a use of a previous tag, or if the tag is already declared
// in the same scope (so that the definition/declaration completes or
// rementions the tag), reuse the decl.
if (m_Sema->isDeclInScope(PrevDecl, SearchDC,
m_Sema->getScopeForContext(m_Sema->CurContext),
isExplicitSpecialization)) {
// Make sure that this wasn't declared as an enum and now used as a
// struct or something similar.
SourceLocation KWLoc = NewTD->getLocStart();
if (!m_Sema->isAcceptableTagRedeclaration(PrevTagDecl, Kind,
NewTD->isThisDeclarationADefinition(),
KWLoc, *Name)) {
bool SafeToContinue
= (PrevTagDecl->getTagKind() != TTK_Enum && Kind != TTK_Enum);
if (SafeToContinue)
m_Sema->Diag(KWLoc, diag::err_use_with_wrong_tag)
<< Name
<< FixItHint::CreateReplacement(SourceRange(KWLoc),
PrevTagDecl->getKindName());
else
m_Sema->Diag(KWLoc, diag::err_use_with_wrong_tag) << Name;
m_Sema->Diag(PrevTagDecl->getLocation(), diag::note_previous_use);
if (SafeToContinue)
Kind = PrevTagDecl->getTagKind();
else {
// Recover by making this an anonymous redefinition.
Name = 0;
Previous.clear();
Invalid = true;
}
}
if (Kind == TTK_Enum && PrevTagDecl->getTagKind() == TTK_Enum) {
const EnumDecl *NewEnum = cast<EnumDecl>(NewTD);
const EnumDecl *PrevEnum = cast<EnumDecl>(PrevTagDecl);
// All conflicts with previous declarations are recovered by
// returning the previous declaration.
if (NewEnum->isScoped() != PrevEnum->isScoped()) {
m_Sema->Diag(KWLoc, diag::err_enum_redeclare_scoped_mismatch)
<< PrevEnum->isScoped();
m_Sema->Diag(PrevTagDecl->getLocation(), diag::note_previous_use);
NewTD->setInvalidDecl();
return false;
}
else if (PrevEnum->isFixed()) {
QualType T = NewEnum->getIntegerType();
if (!m_Context->hasSameUnqualifiedType(T,
PrevEnum->getIntegerType())) {
m_Sema->Diag(NameLoc.isValid() ? NameLoc : KWLoc,
diag::err_enum_redeclare_type_mismatch)
<< T
<< PrevEnum->getIntegerType();
m_Sema->Diag(PrevTagDecl->getLocation(),
diag::note_previous_use);
NewTD->setInvalidDecl();
return false;
}
}
else if (NewEnum->isFixed() != PrevEnum->isFixed()) {
m_Sema->Diag(KWLoc, diag::err_enum_redeclare_fixed_mismatch)
<< PrevEnum->isFixed();
m_Sema->Diag(PrevTagDecl->getLocation(), diag::note_previous_use);
NewTD->setInvalidDecl();
return false;
}
}
if (!Invalid) {
// If this is a use, just return the declaration we found.
// Diagnose attempts to redefine a tag.
if (NewTD->isThisDeclarationADefinition()) {
if (TagDecl* Def = PrevTagDecl->getDefinition()) {
// If we're defining a specialization and the previous
// definition is from an implicit instantiation, don't emit an
// error here; we'll catch this in the general case below.
if (!isExplicitSpecialization ||
!isa<CXXRecordDecl>(Def) ||
cast<CXXRecordDecl>(Def)->getTemplateSpecializationKind()
== TSK_ExplicitSpecialization) {
m_Sema->Diag(NameLoc, diag::err_redefinition) << Name;
m_Sema->Diag(Def->getLocation(),
diag::note_previous_definition);
// If this is a redefinition, recover by making this
// struct be anonymous, which will make any later
// references get the previous definition.
Name = 0;
Previous.clear();
Invalid = true;
}
} else {
// If the type is currently being defined, complain
// about a nested redefinition.
const TagType *Tag
= cast<TagType>(m_Context->getTagDeclType(PrevTagDecl));
if (Tag->isBeingDefined()) {
m_Sema->Diag(NameLoc, diag::err_nested_redefinition) << Name;
m_Sema->Diag(PrevTagDecl->getLocation(),
diag::note_previous_definition);
Name = 0;
Previous.clear();
Invalid = true;
}
}
// Okay, this is definition of a previously declared or referenced
// tag PrevDecl. We're going to create a new Decl for it.
}
}
// If we get here we have (another) forward declaration or we
// have a definition. Just create a new decl.
} else {
// If we get here, this is a definition of a new tag type in a nested
// scope, e.g. "struct foo; void bar() { struct foo; }", just create a
// new decl/type. We set PrevDecl to NULL so that the entities
// have distinct types.
Previous.clear();
}
// If we get here, we're going to create a new Decl. If PrevDecl
// is non-NULL, it's a definition of the tag declared by
// PrevDecl. If it's NULL, we have a new definition.
// Otherwise, PrevDecl is not a tag, but was found with tag
// lookup. This is only actually possible in C++, where a few
// things like templates still live in the tag namespace.
} else {
assert(m_Sema->getLangOpts().CPlusPlus);
// Diagnose if the declaration is in scope.
if (!m_Sema->isDeclInScope(PrevDecl, SearchDC,
m_Sema->getScopeForContext(m_Sema->CurContext),
isExplicitSpecialization)) {
// do nothing
// Otherwise it's a declaration. Call out a particularly common
// case here.
} else if (TypedefNameDecl *TND = dyn_cast<TypedefNameDecl>(PrevDecl)) {
unsigned Kind = 0;
if (isa<TypeAliasDecl>(PrevDecl)) Kind = 1;
m_Sema->Diag(NameLoc, diag::err_tag_definition_of_typedef)
<< Name << Kind << TND->getUnderlyingType();
m_Sema->Diag(PrevDecl->getLocation(),
diag::note_previous_decl) << PrevDecl;
Invalid = true;
// Otherwise, diagnose.
} else {
// The tag name clashes with something else in the target scope,
// issue an error and recover by making this tag be anonymous.
m_Sema->Diag(NameLoc, diag::err_redefinition_different_kind) << Name;
m_Sema->Diag(PrevDecl->getLocation(), diag::note_previous_definition);
Name = 0;
Invalid = true;
}
// The existing declaration isn't relevant to us; we're in a
// new scope, so clear out the previous declaration.
Previous.clear();
}
}
if (Invalid) {
NewTD->setInvalidDecl();
return false;
}
return true;
}
} // namespace cling

View File

@ -0,0 +1,71 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_DECL_EXTRACTOR_H
#define CLING_DECL_EXTRACTOR_H
#include "TransactionTransformer.h"
#include "clang/Sema/Lookup.h"
namespace clang {
class ASTContext;
class Decl;
class DeclContext;
class NamedDecl;
class Scope;
}
namespace cling {
class DeclExtractor : public TransactionTransformer {
private:
clang::ASTContext* m_Context;
public:
DeclExtractor(clang::Sema* S);
virtual ~DeclExtractor();
///\brief Iterates over the transaction and finds cling specific wrappers.
/// Scans the wrappers for declarations and extracts them onto the global
/// scope.
///
virtual void Transform();
private:
///\brief Tries to extract the declaration on the global scope (translation
/// unit scope).
///
///\param D[in] - The declaration to be extracted.
///\returns true on success.
///
bool ExtractDecl(clang::Decl* D);
///\brief Checks for clashing names when trying to extract a declaration.
///
///\returns true if there is another declaration with the same name
///
bool CheckForClashingNames(
const llvm::SmallVector<clang::NamedDecl*, 4>& Decls,
clang::DeclContext* DC, clang::Scope* S);
///\brief Performs semantic checking on a newly-extracted tag declaration.
///
/// This routine performs all of the type-checking required for a tag
/// declaration once it has been built. It is used both to check tags before
/// they have been moved onto the global scope.
///
/// Sets NewTD->isInvalidDecl if an error was encountered.
///
///\returns true if the tag declaration is redeclaration.
///
bool CheckTagDeclaration(clang::TagDecl* NewTD,
clang::LookupResult& Previous);
};
} // namespace cling
#endif // CLING_DECL_EXTRACTOR_H

View File

@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id: Interpreter.cpp 45775 2012-08-31 14:54:11Z vvassilev $
// author: Vassil Vassilev <vvasilev@cern.ch>
//------------------------------------------------------------------------------
#include "cling/Interpreter/DynamicExprInfo.h"
#include <sstream>
namespace cling {
namespace runtime {
namespace internal {
const char* DynamicExprInfo::getExpr() {
int i = 0;
size_t found;
while ((found = m_Result.find("@")) && (found != std::string::npos)) {
std::stringstream address;
address << m_Addresses[i];
m_Result = m_Result.insert(found + 1, address.str());
m_Result = m_Result.erase(found, 1);
++i;
}
return m_Result.c_str();
}
} // end namespace internal
} // end namespace runtime
} // end namespace cling

View File

@ -0,0 +1,881 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#include "DynamicLookup.h"
#include "Transaction.h"
#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Utils/AST.h"
#include "clang/AST/ASTContext.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Template.h"
using namespace clang;
namespace cling {
// Constructor
DynamicIDHandler::DynamicIDHandler(Sema* Sema)
: Callbacks(0), m_Sema(Sema), m_Context(Sema->getASTContext())
{}
// pin the vtable to this file
DynamicIDHandler::~DynamicIDHandler(){
delete Callbacks;
Callbacks = 0;
}
bool DynamicIDHandler::LookupUnqualified(LookupResult& R, Scope* S) {
if (!IsDynamicLookup(R, S))
return false;
if (Callbacks && Callbacks->isEnabled()) {
return Callbacks->LookupObject(R, S);
}
DeclarationName Name = R.getLookupName();
IdentifierInfo* II = Name.getAsIdentifierInfo();
SourceLocation Loc = R.getNameLoc();
VarDecl* Result = VarDecl::Create(m_Context,
R.getSema().getFunctionLevelDeclContext(),
Loc,
Loc,
II,
m_Context.DependentTy,
/*TypeSourceInfo*/0,
SC_None,
SC_None);
if (Result) {
R.addDecl(Result);
// Say that we can handle the situation. Clang should try to recover
return true;
}
// We cannot handle the situation. Give up
return false;
}
bool DynamicIDHandler::IsDynamicLookup (LookupResult& R, Scope* S) {
if (R.getLookupKind() != Sema::LookupOrdinaryName) return false;
if (R.isForRedeclaration()) 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 (m_Sema->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())) {
return !Ctx->isDependentContext();
}
}
return true;
}
} // end namespace cling
namespace {
class StmtPrinterHelper : public PrinterHelper {
private:
PrintingPolicy m_Policy;
llvm::SmallVector<DeclRefExpr*, 4>& m_Addresses;
Sema* m_Sema;
public:
StmtPrinterHelper(const PrintingPolicy& Policy,
llvm::SmallVector<DeclRefExpr*, 4>& Addresses,
Sema* S) :
m_Policy(Policy), m_Addresses(Addresses), m_Sema(S) {}
virtual ~StmtPrinterHelper() {}
// Handle only DeclRefExprs since they are local and the call wrapper
// won't "see" them. Consequently we don't need to handle:
// * DependentScopeDeclRefExpr
// * CallExpr
// * MemberExpr
// * CXXDependentScopeMemberExpr
virtual bool handledStmt(Stmt* S, llvm::raw_ostream& OS) {
if (DeclRefExpr* Node = dyn_cast<DeclRefExpr>(S))
// Exclude the artificially dependent DeclRefExprs, created by the
// Lookup
if (!Node->isTypeDependent()) {
if (NestedNameSpecifier* Qualifier = Node->getQualifier())
Qualifier->print(OS, m_Policy);
m_Addresses.push_back(Node);
QualType T = Node->getType();
SplitQualType T_split = T.split();
if (!T->isArrayType())
OS << '*';
OS << '(';
ASTContext &Ctx = m_Sema->getASTContext();
OS << Ctx.getBaseElementType(T).getAsString();
// We need to handle the arrays differently
if (const ArrayType* AT = dyn_cast<ArrayType>(T.getTypePtr())) {
OS << "(*)";
T = AT->getElementType();
while ((AT = dyn_cast<ArrayType>(T))) {
// TODO: Fix other types of arrays
if (const ConstantArrayType* CAT
= dyn_cast<ConstantArrayType>(AT))
OS <<'[' << CAT->getSize().getZExtValue() << ']';
T = AT->getElementType();
}
}
else
OS << '*';
if (!Node->getType().isNull()) {
// If the type is sugared, also dump a (shallow) desugared type.
SplitQualType D_split = Node->getType().getSplitDesugaredType();
assert(T_split == D_split && "Trying to print shallow type!");
if (T_split != D_split)
OS << ":" << QualType::getAsString(D_split);
}
// end
OS <<")@";
if (Node->hasExplicitTemplateArgs())
OS << TemplateSpecializationType::PrintTemplateArgumentList(
Node->getTemplateArgs(),
Node->getNumTemplateArgs(),
m_Policy);
if (Node->hasExplicitTemplateArgs())
assert((Node->getTemplateArgs() || Node->getNumTemplateArgs()) && \
"There shouldn't be template paramlist");
return true;
}
return false;
}
};
} // end anonymous namespace
namespace cling {
// Constructors
EvaluateTSynthesizer::EvaluateTSynthesizer(Interpreter* interp, Sema* S)
: TransactionTransformer(S), m_EvalDecl(0), m_LifetimeHandlerDecl(0),
m_LHgetMemoryDecl(0), m_DynamicExprInfoDecl(0), m_DeclContextDecl(0),
m_CurDeclContext(0), m_Interpreter(interp), m_Context(&S->getASTContext())
{ }
// pin the vtable here.
EvaluateTSynthesizer::~EvaluateTSynthesizer()
{ }
void EvaluateTSynthesizer::Initialize() {
// Most of the declaration we are looking up are in cling::runtime::internal
NamespaceDecl* NSD = utils::Lookup::Namespace(m_Sema, "cling");
NSD = utils::Lookup::Namespace(m_Sema, "runtime", NSD);
NSD = utils::Lookup::Namespace(m_Sema, "internal", NSD);
// Find and set up EvaluateT
DeclarationName Name = &m_Context->Idents.get("EvaluateT");
LookupResult R(*m_Sema, Name, SourceLocation(), Sema::LookupOrdinaryName,
Sema::ForRedeclaration);
m_Sema->LookupQualifiedName(R, NSD);
// We have specialized EvaluateT but we don't care because the templated
// decl is needed.
TemplateDecl* TplD = dyn_cast_or_null<TemplateDecl>(*R.begin());
m_EvalDecl = dyn_cast<FunctionDecl>(TplD->getTemplatedDecl());
assert(m_EvalDecl && "The Eval function not found!");
// Find the LifetimeHandler declaration
R.clear();
Name = &m_Context->Idents.get("LifetimeHandler");
R.setLookupName(Name);
m_Sema->LookupQualifiedName(R, NSD);
m_LifetimeHandlerDecl = R.getAsSingle<CXXRecordDecl>();
assert(m_LifetimeHandlerDecl && "LifetimeHandler could not be found.");
// Find the LifetimeHandler::getMemory declaration
R.clear();
Name = &m_Context->Idents.get("getMemory");
R.setLookupName(Name);
m_Sema->LookupQualifiedName(R, m_LifetimeHandlerDecl);
m_LHgetMemoryDecl = R.getAsSingle<CXXMethodDecl>();
assert(m_LHgetMemoryDecl && "LifetimeHandler::getMemory couldn't be found.");
// Find the DynamicExprInfo declaration
R.clear();
Name = &m_Context->Idents.get("DynamicExprInfo");
R.setLookupName(Name);
m_Sema->LookupQualifiedName(R, NSD);
m_DynamicExprInfoDecl = R.getAsSingle<CXXRecordDecl>();
assert(m_DynamicExprInfoDecl && "DynExprInfo could not be found.");
// Find the DeclContext declaration
R.clear();
Name = &m_Context->Idents.get("DeclContext");
R.setLookupName(Name);
NamespaceDecl* clangNSD = utils::Lookup::Namespace(m_Sema, "clang");
m_Sema->LookupQualifiedName(R, clangNSD);
m_DeclContextDecl = R.getAsSingle<CXXRecordDecl>();
assert(m_DeclContextDecl && "clang::DeclContext decl could not be found.");
// Find and set the source locations to valid ones.
R.clear();
Name
= &m_Context->Idents.get(
"InterpreterGeneratedCodeDiagnosticsMaybeIncorrect");
R.setLookupName(Name);
m_Sema->LookupQualifiedName(R, NSD);
assert(!R.empty() && "Cannot find PrintValue(...)");
NamedDecl* ND = R.getFoundDecl();
m_NoRange = ND->getSourceRange();
m_NoSLoc = m_NoRange.getBegin();
m_NoELoc = m_NoRange.getEnd();
}
void EvaluateTSynthesizer::Transform() {
if (!getTransaction()->getCompilationOpts().DynamicScoping)
return;
// Find DynamicLookup specific builtins
if (!m_EvalDecl) {
Initialize();
}
for (Transaction::const_iterator I = getTransaction()->decls_begin(),
E = getTransaction()->decls_end(); I != E; ++I)
for (DeclGroupRef::const_iterator J = (*I).begin(),
JE = (*I).end(); J != JE; ++J)
if (ShouldVisit(*J) && (*J)->hasBody()) {
if (FunctionDecl* FD = dyn_cast<FunctionDecl>(*J)) {
// Set the decl context, which is needed by Evaluate.
m_CurDeclContext = FD->getDeclContext();
ASTNodeInfo NewBody = Visit((*J)->getBody());
FD->setBody(NewBody.getAsSingleNode());
}
assert ((!isa<BlockDecl>(*J) || !isa<ObjCMethodDecl>(*J))
&& "Not implemented yet!");
}
//TODO: Check for error before returning.
}
// StmtVisitor
ASTNodeInfo EvaluateTSynthesizer::VisitStmt(Stmt* Node) {
for (Stmt::child_iterator
I = Node->child_begin(), E = Node->child_end(); I != E; ++I) {
if (*I) {
ASTNodeInfo NewNode = Visit(*I);
assert(NewNode.hasSingleNode() &&
"Cannot have more than one stmt at that point");
if (NewNode.isForReplacement()) {
if (Expr* E = NewNode.getAs<Expr>())
// Assume void if still not escaped
*I = SubstituteUnknownSymbol(m_Context->VoidTy, E);
}
else {
*I = NewNode.getAsSingleNode();
}
}
}
return ASTNodeInfo(Node, 0);
}
// If the dynamic expression is in the conditional clause of the if
// assume that the return type is bool, because we know that
// everything in the condition of IfStmt is implicitly converted into bool
ASTNodeInfo EvaluateTSynthesizer::VisitIfStmt(IfStmt* Node) {
// See whether there is var defined. Eg: if (int i = f->call())
// It will fall into DeclStmt.
if (Node->getConditionVariableDeclStmt()) {
// Removing the const, which shouldn't be dangerous
VisitDeclStmt(const_cast<DeclStmt*>(
Node->getConditionVariableDeclStmt()));
}
// Handle the case where the dynamic expression is in the condition of the
// stmt.
ASTNodeInfo IfCondInfo = Visit(Node->getCond());
if (IfCondInfo.isForReplacement())
if (Expr* IfCondExpr = IfCondInfo.getAs<Expr>()) {
Node->setCond(SubstituteUnknownSymbol(m_Context->BoolTy, IfCondExpr));
return ASTNodeInfo(Node, /*needs eval*/false);
}
// Visit the other parts - they will fall naturally into Stmt or
// CompoundStmt where we know what to do.
Visit(Node->getThen());
if (Stmt* ElseExpr = Node->getElse())
Visit(ElseExpr);
return ASTNodeInfo(Node, false);
}
ASTNodeInfo EvaluateTSynthesizer::VisitCompoundStmt(CompoundStmt* Node) {
ASTNodes Children;
ASTNodes NewChildren;
if (GetChildren(Children, Node)) {
ASTNodes::iterator it;
for (it = Children.begin(); it != Children.end(); ++it) {
ASTNodeInfo NewNode = Visit(*it);
if (!NewNode.hasSingleNode()) {
ASTNodes& NewStmts(NewNode.getNodes());
for(unsigned i = 0; i < NewStmts.size(); ++i)
NewChildren.push_back(NewStmts[i]);
Node->setStmts(*m_Context, NewChildren.data(), NewChildren.size());
// Resolve all 1:n replacements
Visit(Node);
}
else {
if (NewNode.isForReplacement()) {
if (Expr* E = NewNode.getAs<Expr>()) {
// Check whether value printer has been requested
bool valuePrinterReq = false;
if ((it+1) == Children.end() || !isa<NullStmt>(*(it+1)))
valuePrinterReq = true;
// Assume void if still not escaped
NewChildren.push_back(SubstituteUnknownSymbol(m_Context->VoidTy,E,
valuePrinterReq));
}
}
else
NewChildren.push_back(*it);
}
}
}
Node->setStmts(*m_Context, NewChildren.data(), NewChildren.size());
return ASTNodeInfo(Node, 0);
}
ASTNodeInfo EvaluateTSynthesizer::VisitDeclStmt(DeclStmt* Node) {
// Visit all the children, which are the contents of the DeclGroupRef
for (Stmt::child_iterator
I = Node->child_begin(), E = Node->child_end(); I != E; ++I) {
if (*I) {
Expr* E = cast_or_null<Expr>(*I);
if (!E || !IsArtificiallyDependent(E))
continue;
//FIXME: don't assume there is only one decl.
assert(Node->isSingleDecl() && "There is more that one decl in stmt");
VarDecl* CuredDecl = cast_or_null<VarDecl>(Node->getSingleDecl());
assert(CuredDecl && "Not a variable declaration!");
QualType CuredDeclTy = CuredDecl->getType();
// check if the case is sometype * somevar = init;
// or some_builtin_type somevar = init;
if (CuredDecl->hasInit() && (CuredDeclTy->isAnyPointerType()
|| !CuredDeclTy->isRecordType())) {
*I = SubstituteUnknownSymbol(CuredDeclTy, CuredDecl->getInit());
continue;
}
// 1. Check whether this is the case of MyClass A(dep->symbol())
// 2. Insert the RuntimeUniverse's LifetimeHandler instance
// 3. Change the A's initializer to *(MyClass*)instance.getMemory()
// 4. Make A reference (&A)
// 5. Set the new initializer of A
if (CuredDeclTy->isLValueReferenceType())
continue;
// Set Sema's Current DeclContext to the one we need
DeclContext* OldDC = m_Sema->CurContext;
m_Sema->CurContext = CuredDecl->getDeclContext();
ASTNodeInfo NewNode;
// 2.1 Get unique name for the LifetimeHandler instance and
// initialize it
std::string UniqueName;
m_Interpreter->createUniqueName(UniqueName);
IdentifierInfo& II = m_Context->Idents.get(UniqueName);
// Prepare the initialization Exprs.
// We want to call LifetimeHandler(DynamicExprInfo* ExprInfo,
// DeclContext DC,
// const char* type)
llvm::SmallVector<Expr*, 4> Inits;
// Add MyClass in LifetimeHandler unique(DynamicExprInfo* ExprInfo
// DC,
// "MyClass")
// Build Arg0 DynamicExprInfo
Inits.push_back(BuildDynamicExprInfo(E));
// Build Arg1 DeclContext* DC
QualType DCTy = m_Context->getTypeDeclType(m_DeclContextDecl);
Inits.push_back(ConstructCStyleCasePtrExpr(DCTy,
(uint64_t)m_CurDeclContext)
);
// Build Arg2 llvm::StringRef
// Get the type of the type without specifiers
PrintingPolicy Policy(m_Context->getLangOpts());
Policy.SuppressTagKeyword = 1;
std::string Res;
CuredDeclTy.getAsStringInternal(Res, Policy);
Inits.push_back(ConstructConstCharPtrExpr(Res.c_str()));
// 2.3 Create a variable from LifetimeHandler.
QualType HandlerTy = m_Context->getTypeDeclType(m_LifetimeHandlerDecl);
VarDecl* HandlerInstance = VarDecl::Create(*m_Context,
CuredDecl->getDeclContext(),
m_NoSLoc,
m_NoSLoc,
&II,
HandlerTy,
/*TypeSourceInfo**/0,
SC_None,
SC_None);
// 2.4 Call the best-match constructor. The method does overload
// resolution of the constructors and then initializes the new
// variable with it
ExprResult InitExprResult
= m_Sema->ActOnParenListExpr(m_NoSLoc,
m_NoELoc,
Inits);
m_Sema->AddInitializerToDecl(HandlerInstance,
InitExprResult.take(),
/*DirectInit*/ true,
/*TypeMayContainAuto*/ false);
// 2.5 Register the instance in the enclosing context
CuredDecl->getDeclContext()->addDecl(HandlerInstance);
NewNode.addNode(new (m_Context)
DeclStmt(DeclGroupRef(HandlerInstance),
m_NoSLoc,
m_NoELoc)
);
// 3.1 Build a DeclRefExpr, which holds the object
DeclRefExpr* MemberExprBase
= m_Sema->BuildDeclRefExpr(HandlerInstance,
HandlerTy,
VK_LValue,
m_NoSLoc
).takeAs<DeclRefExpr>();
// 3.2 Create a MemberExpr to getMemory from its declaration.
CXXScopeSpec SS;
LookupResult MemberLookup(*m_Sema, m_LHgetMemoryDecl->getDeclName(),
m_NoSLoc, Sema::LookupMemberName);
// Add the declaration as if doesn't exist.
// TODO: Check whether this is the most appropriate variant
MemberLookup.addDecl(m_LHgetMemoryDecl, AS_public);
MemberLookup.resolveKind();
Expr* MemberExpr = m_Sema->BuildMemberReferenceExpr(MemberExprBase,
HandlerTy,
m_NoSLoc,
/*IsArrow=*/false,
SS,
m_NoSLoc,
/*FirstQualifierInScope=*/0,
MemberLookup,
/*TemplateArgs=*/0
).take();
// 3.3 Build the actual call
Scope* S = m_Sema->getScopeForContext(m_Sema->CurContext);
Expr* theCall = m_Sema->ActOnCallExpr(S,
MemberExpr,
m_NoSLoc,
MultiExprArg(),
m_NoELoc).take();
// Cast to the type LHS type
TypeSourceInfo* CuredDeclTSI
= m_Context->CreateTypeSourceInfo(m_Context->getPointerType(
CuredDeclTy));
Expr* Result = m_Sema->BuildCStyleCastExpr(m_NoSLoc,
CuredDeclTSI,
m_NoELoc,
theCall).take();
// Cast once more (dereference the cstyle cast)
Result = m_Sema->BuildUnaryOp(S, m_NoSLoc, UO_Deref, Result).take();
// 4.
CuredDecl->setType(m_Context->getLValueReferenceType(CuredDeclTy));
// 5.
CuredDecl->setInit(Result);
NewNode.addNode(Node);
// Restore Sema's original DeclContext
m_Sema->CurContext = OldDC;
return NewNode;
}
}
return ASTNodeInfo(Node, 0);
}
ASTNodeInfo EvaluateTSynthesizer::VisitExpr(Expr* Node) {
for (Stmt::child_iterator
I = Node->child_begin(), E = Node->child_end(); I != E; ++I) {
if (*I) {
ASTNodeInfo NewNode = Visit(*I);
assert(NewNode.hasSingleNode() &&
"Cannot have more than one stmt at that point");
if (NewNode.isForReplacement()) {
if (Expr *E = NewNode.getAs<Expr>())
// Assume void if still not escaped
*I = SubstituteUnknownSymbol(m_Context->VoidTy, E);
}
else {
*I = NewNode.getAsSingleNode();
}
}
}
return ASTNodeInfo(Node, 0);
}
ASTNodeInfo EvaluateTSynthesizer::VisitBinaryOperator(BinaryOperator* Node) {
ASTNodeInfo rhs = Visit(Node->getRHS());
ASTNodeInfo lhs = Visit(Node->getLHS());
assert((lhs.hasSingleNode() || rhs.hasSingleNode()) &&
"1:N replacements are not implemented yet!");
// Try find out the type of the left-hand-side of the operator
// and give the hint to the right-hand-side in order to replace the
// dependent symbol
if (Node->isAssignmentOp() &&
rhs.isForReplacement() &&
!lhs.isForReplacement()) {
if (Expr* LHSExpr = lhs.getAs<Expr>())
if (!IsArtificiallyDependent(LHSExpr)) {
const QualType LHSTy = LHSExpr->getType();
Node->setRHS(SubstituteUnknownSymbol(LHSTy, rhs.castTo<Expr>()));
Node->setTypeDependent(false);
Node->setValueDependent(false);
return ASTNodeInfo(Node, /*needs eval*/false);
}
}
return ASTNodeInfo(Node, IsArtificiallyDependent(Node));
}
ASTNodeInfo EvaluateTSynthesizer::VisitCallExpr(CallExpr* E) {
// FIXME: Maybe we need to handle the arguments
// ASTNodeInfo NewNode = Visit(E->getCallee());
return ASTNodeInfo (E, IsArtificiallyDependent(E));
}
ASTNodeInfo EvaluateTSynthesizer::VisitDeclRefExpr(DeclRefExpr* DRE) {
return ASTNodeInfo(DRE, IsArtificiallyDependent(DRE));
}
ASTNodeInfo EvaluateTSynthesizer::VisitDependentScopeDeclRefExpr(
DependentScopeDeclRefExpr* Node) {
return ASTNodeInfo(Node, IsArtificiallyDependent(Node));
}
// end StmtVisitor
// EvalBuilder
Expr* EvaluateTSynthesizer::SubstituteUnknownSymbol(const QualType InstTy,
Expr* SubTree,
bool ValuePrinterReq) {
assert(SubTree && "No subtree specified!");
//Build the arguments for the call
llvm::SmallVector<Expr*, 2> CallArgs;
// Build Arg0
Expr* Arg0 = BuildDynamicExprInfo(SubTree, ValuePrinterReq);
CallArgs.push_back(Arg0);
// Build Arg1
QualType DCTy = m_Context->getTypeDeclType(m_DeclContextDecl);
Expr* Arg1 = ConstructCStyleCasePtrExpr(DCTy, (uint64_t)m_CurDeclContext);
CallArgs.push_back(Arg1);
// Build the call
assert(Arg0 && Arg1 && "Arguments missing!");
CallExpr* EvalCall = BuildEvalCallExpr(InstTy, SubTree, CallArgs);
// Add substitution mapping
getSubstSymbolMap()[EvalCall] = SubTree;
return EvalCall;
}
Expr* EvaluateTSynthesizer::BuildDynamicExprInfo(Expr* SubTree,
bool ValuePrinterReq) {
// 1. Get the expression containing @-s and get the variable addresses
std::string Template;
llvm::SmallVector<DeclRefExpr*, 4> Addresses;
llvm::raw_string_ostream OS(Template);
const PrintingPolicy& Policy = m_Context->getPrintingPolicy();
StmtPrinterHelper helper(Policy, Addresses, m_Sema);
// In case when we print non paren inits like int i = h->Draw();
// not int i(h->Draw()). This simplifies the LifetimeHandler's
// constructor, there we don't need to add parenthesis while
// wrapping the expression.
if (!isa<ParenListExpr>(SubTree))
OS << '(';
SubTree->printPretty(OS, &helper, Policy);
if (!isa<ParenListExpr>(SubTree))
OS << ')';
OS.flush();
// 2. Build the template
Expr* ExprTemplate = ConstructConstCharPtrExpr(Template.c_str());
// 3. Build the array of addresses
QualType VarAddrTy = m_Sema->BuildArrayType(m_Context->VoidPtrTy,
ArrayType::Normal,
/*ArraySize*/0,
Qualifiers(),
m_NoRange,
DeclarationName() );
llvm::SmallVector<Expr*, 2> Inits;
Scope* S = m_Sema->getScopeForContext(m_Sema->CurContext);
for (unsigned int i = 0; i < Addresses.size(); ++i) {
Expr* UnOp
= m_Sema->BuildUnaryOp(S, m_NoSLoc, UO_AddrOf, Addresses[i]).take();
m_Sema->ImpCastExprToType(UnOp,
m_Context->getPointerType(m_Context->VoidPtrTy),
CK_BitCast);
Inits.push_back(UnOp);
}
// We need valid source locations to avoid assert(InitList.isExplicit()...)
InitListExpr* ILE = m_Sema->ActOnInitList(m_NoSLoc,
Inits,
m_NoELoc).takeAs<InitListExpr>();
Expr* ExprAddresses = m_Sema->BuildCompoundLiteralExpr(m_NoSLoc,
m_Context->CreateTypeSourceInfo(VarAddrTy),
m_NoELoc,
ILE).take();
assert (ExprAddresses && "Could not build the void* array");
m_Sema->ImpCastExprToType(ExprAddresses,
m_Context->getPointerType(m_Context->VoidPtrTy),
CK_ArrayToPointerDecay);
// Is the result of the expression to be printed or not
Expr* VPReq = 0;
if (ValuePrinterReq)
VPReq = m_Sema->ActOnCXXBoolLiteral(m_NoSLoc, tok::kw_true).take();
else
VPReq = m_Sema->ActOnCXXBoolLiteral(m_NoSLoc, tok::kw_false).take();
llvm::SmallVector<Expr*, 4> CtorArgs;
CtorArgs.push_back(ExprTemplate);
CtorArgs.push_back(ExprAddresses);
CtorArgs.push_back(VPReq);
// 4. Call the constructor
QualType ExprInfoTy = m_Context->getTypeDeclType(m_DynamicExprInfoDecl);
ExprResult Initializer = m_Sema->ActOnParenListExpr(m_NoSLoc, m_NoELoc,
CtorArgs);
Expr* Result = m_Sema->BuildCXXNew(m_NoSLoc,
/*UseGlobal=*/false,
m_NoSLoc,
/*PlacementArgs=*/MultiExprArg(),
m_NoELoc,
m_NoRange,
ExprInfoTy,
m_Context->CreateTypeSourceInfo(ExprInfoTy),
/*ArraySize=*/0,
//BuildCXXNew depends on the SLoc to be
//valid!
// TODO: Propose a patch in clang
m_NoRange,
Initializer.take(),
/*TypeMayContainAuto*/false
).take();
return Result;
}
Expr* EvaluateTSynthesizer::ConstructCStyleCasePtrExpr(QualType Ty,
uint64_t Ptr) {
if (!Ty->isPointerType())
Ty = m_Context->getPointerType(Ty);
TypeSourceInfo* TSI = m_Context->CreateTypeSourceInfo(Ty);
const llvm::APInt Addr(8 * sizeof(void *), Ptr);
Expr* Result = IntegerLiteral::Create(*m_Context,
Addr,
m_Context->UnsignedLongTy,
m_NoSLoc);
Result = m_Sema->BuildCStyleCastExpr(m_NoSLoc,
TSI,
m_NoELoc,
Result).take();
return Result;
}
Expr* EvaluateTSynthesizer::ConstructConstCharPtrExpr(const char* Val) {
const QualType CChar = m_Context->CharTy.withConst();
llvm::StringRef Value(Val);
unsigned bitSize = m_Context->getTypeSize(m_Context->VoidPtrTy);
llvm::APInt ArraySize(bitSize, Value.size() + 1);
const QualType CCArray = m_Context->getConstantArrayType(CChar,
ArraySize,
ArrayType::Normal,
/*IndexTypeQuals=*/0);
StringLiteral::StringKind Kind = StringLiteral::Ascii;
Expr* Result = StringLiteral::Create(*m_Context,
Value,
Kind,
/*Pascal=*/false,
CCArray,
m_NoSLoc);
m_Sema->ImpCastExprToType(Result,
m_Context->getPointerType(CChar),
CK_ArrayToPointerDecay);
return Result;
}
// Here is the test Eval function specialization. Here the CallExpr to the
// function is created.
CallExpr*
EvaluateTSynthesizer::BuildEvalCallExpr(const QualType InstTy,
Expr* SubTree,
llvm::SmallVector<Expr*, 2>& CallArgs) {
// Set up new context for the new FunctionDecl
DeclContext* PrevContext = m_Sema->CurContext;
m_Sema->CurContext = m_EvalDecl->getDeclContext();
// Create template arguments
Sema::InstantiatingTemplate Inst(*m_Sema, m_NoSLoc, m_EvalDecl);
TemplateArgument Arg(InstTy);
TemplateArgumentList TemplateArgs(TemplateArgumentList::OnStack, &Arg, 1U);
// Substitute the declaration of the templated function, with the
// specified template argument
Decl* D = m_Sema->SubstDecl(m_EvalDecl,
m_EvalDecl->getDeclContext(),
MultiLevelTemplateArgumentList(TemplateArgs));
FunctionDecl* Fn = dyn_cast<FunctionDecl>(D);
// Creates new body of the substituted declaration
m_Sema->InstantiateFunctionDefinition(Fn->getLocation(), Fn, true, true);
m_Sema->CurContext = PrevContext;
const FunctionProtoType* FPT = Fn->getType()->getAs<FunctionProtoType>();
FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo();
QualType FnTy = m_Context->getFunctionType(Fn->getResultType(),
FPT->arg_type_begin(),
FPT->getNumArgs(),
EPI);
DeclRefExpr* DRE = m_Sema->BuildDeclRefExpr(Fn,
FnTy,
VK_RValue,
m_NoSLoc
).takeAs<DeclRefExpr>();
// TODO: Figure out a way to avoid passing in wrong source locations
// of the symbol being replaced. This is important when we calculate the
// size of the memory buffers and may lead to creation of wrong wrappers.
Scope* S = m_Sema->getScopeForContext(m_Sema->CurContext);
CallExpr* EvalCall = m_Sema->ActOnCallExpr(S,
DRE,
SubTree->getLocStart(),
CallArgs,
SubTree->getLocEnd()
).takeAs<CallExpr>();
assert (EvalCall && "Cannot create call to Eval");
return EvalCall;
}
// end EvalBuilder
// Helpers
bool EvaluateTSynthesizer::ShouldVisit(Decl* D) {
while (true) {
if (isa<TemplateTemplateParmDecl>(D))
return false;
if (isa<ClassTemplateDecl>(D))
return false;
if (isa<FriendTemplateDecl>(D))
return false;
if (isa<ClassTemplatePartialSpecializationDecl>(D))
return false;
if (CXXRecordDecl* CXX = dyn_cast<CXXRecordDecl>(D)) {
if (CXX->getDescribedClassTemplate())
return false;
}
if (CXXMethodDecl* CXX = dyn_cast<CXXMethodDecl>(D)) {
if (CXX->getDescribedFunctionTemplate())
return false;
}
if (isa<TranslationUnitDecl>(D)) {
break;
}
if (DeclContext* DC = D->getDeclContext())
if (!(D = dyn_cast<Decl>(DC)))
break;
}
return true;
}
bool EvaluateTSynthesizer::IsArtificiallyDependent(Expr* Node) {
if (!Node->isValueDependent() || !Node->isTypeDependent())
return false;
DeclContext* DC = m_CurDeclContext;
while (DC) {
if (DC->isDependentContext())
return false;
DC = DC->getParent();
}
return true;
}
bool EvaluateTSynthesizer::GetChildren(ASTNodes& Children, Stmt* Node) {
if (std::distance(Node->child_begin(), Node->child_end()) < 1)
return false;
for (Stmt::child_iterator
I = Node->child_begin(), E = Node->child_end(); I != E; ++I) {
Children.push_back(*I);
}
return true;
}
// end Helpers
} // end namespace cling

View File

@ -0,0 +1,331 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_DYNAMIC_LOOKUP_H
#define CLING_DYNAMIC_LOOKUP_H
#include "TransactionTransformer.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Sema/ExternalSemaSource.h"
#include "clang/Sema/Ownership.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/Support/Casting.h"
namespace clang {
class Sema;
}
namespace cling {
class InterpreterCallbacks;
/// \brief Provides last chance of recovery for clang semantic analysis.
/// When the compiler doesn't find the symbol in its symbol table it asks
/// its ExternalSemaSource to look for the symbol.
///
/// In contrast to the compiler point of view, where these symbols must be
/// errors, the interpreter's point of view these symbols are to be
/// evaluated at runtime. For that reason the interpreter marks all unknown
/// by the compiler symbols to be with delayed lookup (evaluation).
/// One have to be carefull in the cases, in which the compiler expects that
/// the lookup will fail!
class DynamicIDHandler : public clang::ExternalSemaSource {
public:
InterpreterCallbacks* Callbacks;
DynamicIDHandler(clang::Sema* Sema);
~DynamicIDHandler();
/// \brief Provides last resort lookup for failed unqualified lookups
///
/// If there is failed lookup, tell sema to create an artificial declaration
/// which is of dependent type. So the lookup result is marked as dependent
/// and the diagnostics are suppressed. After that is's an interpreter's
/// responsibility to fix all these fake declarations and lookups.
/// It is done by the DynamicExprTransformer.
///
/// @param[out] R The recovered symbol.
/// @param[in] S The scope in which the lookup failed.
virtual bool LookupUnqualified(clang::LookupResult& R, clang::Scope* S);
private:
clang::Sema* m_Sema;
clang::ASTContext& m_Context;
/// \brief Checks whether the failed lookup is not expected from the
/// compiler to fail.
///
/// @param[out] R The symbol to be checked.
/// @param[in] S The scope, where the lookup failed.
bool IsDynamicLookup (clang::LookupResult& R, clang::Scope* S);
};
} // end namespace cling
namespace cling {
typedef llvm::SmallVector<clang::Stmt*, 2> ASTNodes;
/// \brief Helper structure that allows children of a node to delegate how
/// to be replaced from their parent. For example a child can return more than
/// one replacement node.
class ASTNodeInfo {
private:
ASTNodes Nodes;
bool forReplacement;
public:
ASTNodeInfo() : forReplacement(false){}
ASTNodeInfo(clang::Stmt* S, bool needed) : forReplacement(needed) {
Nodes.push_back(S);
}
bool isForReplacement() { return forReplacement; }
void setForReplacement(bool val = true) { forReplacement = val; }
bool hasSingleNode() { return Nodes.size() == 1; }
clang::Stmt* getAsSingleNode() {
assert(hasSingleNode() && "There is more than one node!");
return Nodes[0];
}
ASTNodes& getNodes() { return Nodes; }
void addNode(clang::Stmt* Node) { Nodes.push_back(Node); }
template <typename T> T* getAs() {
return llvm::dyn_cast<T>(getAsSingleNode());
}
template <typename T> T* castTo() {
T* Result = llvm::dyn_cast<T>(getAsSingleNode());
assert(Result && "Cannot cast to type!");
return Result;
}
};
} //end namespace cling
namespace cling {
class Interpreter;
class DynamicIDHandler;
typedef llvm::DenseMap<clang::Stmt*, clang::Stmt*> MapTy;
/// \brief In order to implement the runtime type binding and expression
/// evaluation we need to be able to compile code which contains unknown
/// symbols (undefined variables, types, functions, etc.). This cannot be done
/// by a compiler like clang, because it is not valid C++ code.
///
/// DynamicExprTransformer transforms these unknown symbols into valid C++
/// code at AST (abstract syntax tree) level. Thus it provides an opportunity
/// their evaluation to happen at runtime. Several steps are performed:
///
/// 1. Skip compiler's error diagnostics - if a compiler encounters unknown
/// symbol, by definition, it must output an error and it mustn't produce
/// machine code. Cling implements an extension to Clang semantic analyzer
/// that allows the compiler to recover even an unknown symbol is encountered.
/// For instance if the compiler sees a symbol it looks for its definition in
/// a internal structure (symbol table) and it is not found it asks whether
/// somebody else could provide the missing symbol. That is the place where
/// the DynamicIDHandler, which is controlled by DynamicExprTransformer comes
/// into play. It marks all unknown symbols as dependent as if they are
/// templates and are going to be resolved at first instantiation, with the
/// only difference that an instantiation never happens. The advantage is that
/// the unknown symbols are not diagnosed but the disadvantage is that
/// somebody needs to transform them into valid expressions with valid types.
///
/// 2. Replace all dependent symbols - all artificially dependent symbols need
/// to be replaced with appropriate valid symbols in order the compiler to
/// produce executable machine code. The DynamicExprTransformer walks up all
/// statements and declarations that might be possibly marked earlier as
/// dependent and replaces them with valid expression, which preserves the
/// meant behavior. Main implementation goal is to replace the as little
/// as possible part of the statement. The replacement is done immediately
/// after the expected type can be deduced.
///
/// 2.1. EvaluateT - this is a templated function, which is put at the
/// place of the dependent expression. It will be called at runtime and it
/// will use the runtime instance of the interpreter (cling interprets itself)
/// to evaluate the replaced expression. The template parameter of the
/// function carries the expected expression type. If unknown symbol is
/// encountered as a right-hand-side of an assignment one can claim that
/// the type of the unknown expression should be compatible with the type of
/// the left-hand-side.
///
/// 2.2 LifetimeHandler - in some more complex situation in order to preserve
/// the behavior the expression must be replaced with more complex structures.
///
/// 3. Evaluate interface - this is the core function in the interpreter,
/// which does the delayed evaluation. It uses a callback function, which
/// should be reimplemented in the subsystem that provides the runtime types
/// and addresses of the expressions.
class EvaluateTSynthesizer : public TransactionTransformer,
public clang::StmtVisitor<EvaluateTSynthesizer,
ASTNodeInfo> {
private:
/// \brief Stores the declaration of the EvaluateT function.
clang::FunctionDecl* m_EvalDecl;
/// \brief Stores helper structure dealing with static initializers.
clang::CXXRecordDecl* m_LifetimeHandlerDecl;
/// \brief Stores member function defined in the LifetimeHanlder.
clang::CXXMethodDecl* m_LHgetMemoryDecl;
/// \brief Stores helper class used in EvaluateT call.
clang::CXXRecordDecl* m_DynamicExprInfoDecl;
/// \brief Stores the clang::DeclContext declaration, used in as an parameter
/// in EvaluateT call.
clang::CXXRecordDecl* m_DeclContextDecl;
/// \brief Sema's external source, which provides last resort lookup.
llvm::OwningPtr<DynamicIDHandler> m_DynIDHandler;
/// \brief Keeps track of the replacements being made. If an AST node is
/// changed with another it should be added to the map (newNode->oldNode).
MapTy m_SubstSymbolMap;
/// \brief Stores the actual declaration context, in which declarations are
/// being visited.
clang::DeclContext* m_CurDeclContext;
/// \brief Stores pointer to cling, mainly used for declaration lookup.
Interpreter* m_Interpreter;
/// \brief Use instead of clang::SourceRange().
clang::SourceRange m_NoRange;
/// \brief Use instead of clang::SourceLocation() as start location.
clang::SourceLocation m_NoSLoc;
/// \brief Use instead of clang::SourceLocation() as end location.
clang::SourceLocation m_NoELoc;
/// \brief Needed for the AST transformations, owned by Sema
clang::ASTContext* m_Context;
public:
typedef clang::StmtVisitor<EvaluateTSynthesizer, ASTNodeInfo>
BaseStmtVisitor;
using BaseStmtVisitor::Visit;
EvaluateTSynthesizer(Interpreter* interp, clang::Sema* S);
~EvaluateTSynthesizer();
virtual void Transform();
MapTy& getSubstSymbolMap() { return m_SubstSymbolMap; }
ASTNodeInfo VisitStmt(clang::Stmt* Node);
ASTNodeInfo VisitCompoundStmt(clang::CompoundStmt* Node);
ASTNodeInfo VisitIfStmt(clang::IfStmt* Node);
/// \brief Transforms a declaration with initializer of dependent type.
/// If an object on the free store is being initialized we use the
/// EvaluateT
/// If an object on the stack is being initialized it is transformed into
/// reference and an object on the free store is created in order to
/// avoid the copy constructors, which might be private
///
/// For example:
/// @code
/// int i = 5;
/// MyClass my(dep->Symbol(i))
/// @endcode
/// where dep->Symbol() is of artificially dependent type it is being
/// transformed into:
/// @code
/// cling::runtime::internal::LifetimeHandler
/// __unique("dep->Sybmol(*(int*)@)",(void*[]){&i}, DC, "MyClass");
/// MyClass &my(*(MyClass*)__unique.getMemory());
/// @endcode
///
/// Note: here our main priority is to preserve equivalent behavior. We have
/// to clean the heap memory afterwords.
///
ASTNodeInfo VisitDeclStmt(clang::DeclStmt* Node);
ASTNodeInfo VisitExpr(clang::Expr* Node);
ASTNodeInfo VisitBinaryOperator(clang::BinaryOperator* Node);
ASTNodeInfo VisitCallExpr(clang::CallExpr* E);
ASTNodeInfo VisitDeclRefExpr(clang::DeclRefExpr* DRE);
ASTNodeInfo VisitDependentScopeDeclRefExpr(
clang::DependentScopeDeclRefExpr* Node);
///\brief Sets callbacks so that DynamicIDHandler can use them, when it sees
/// the unknown symbol again at runtime. This time the implementation of the
/// LookupObject callback should provide the actual definition of the object
/// and the lookup may succeed.
///
/// @param[in] C The concrete implementation of the callback interface.
///
void SetCallbacks(InterpreterCallbacks* C) {
m_DynIDHandler->Callbacks = C;
}
protected:
///\brief On first use it finds commonly used declarations.
///
/// For example: EvaluateT, clang::DeclContext and so on.
///
void Initialize();
/// @{
/// @name Helpers, which simplify node replacement
///\brief Replaces given dependent AST node with an instantiation of
/// EvaluateT with the deduced type.
///
/// @param[in] InstTy The deduced type used to create instantiation.
/// @param[in] SubTree The AST node or subtree, which is being replaced.
/// @param[in] ValuePrinterReq Whether to turn on the value printing or not
///
clang::Expr* SubstituteUnknownSymbol(const clang::QualType InstTy,
clang::Expr* SubTree,
bool ValuePrinterReq = false);
///\brief Builds the actual call expression, which is put in the place of
/// the dependent AST node.
///
/// @param[in] InstTy The deduced type used to create instantiation.
/// @param[in] SubTree The AST node or subtree, which is being replaced.
/// @param[in] CallArgs Proper arguments, which the call will use.
///
clang::CallExpr* BuildEvalCallExpr(clang::QualType InstTy,
clang::Expr* SubTree,
llvm::SmallVector<clang::Expr*, 2>& CallArgs);
///\brief Builds the DynamicExprInfo class with proper info.
///
clang::Expr* BuildDynamicExprInfo(clang::Expr* SubTree,
bool ValuePrinterReq = false);
///\brief Creates cstyle casts a pointer expression to a given qualified
/// type.
///
clang::Expr* ConstructCStyleCasePtrExpr(clang::QualType Ty, uint64_t Ptr);
///\brief Creates const char* expression from given value.
clang::Expr* ConstructConstCharPtrExpr(const char* Val);
///\brief Checks if the given node is marked as dependent by us.
///
bool IsArtificiallyDependent(clang::Expr* Node);
///\brief Checks if the given declaration should be examined. It checks
/// whether a declaration context marked as dependent contains the
/// declaration or the declaration type is not one of those we are looking
/// for.
///
bool ShouldVisit(clang::Decl* D);
/// \brief Gets all children of a given node.
///
bool GetChildren(ASTNodes& Children, clang::Stmt* Node);
/// @}
};
} // end namespace cling
#endif // CLING_DYNAMIC_LOOKUP_H

View File

@ -0,0 +1,274 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------
#include "ExecutionContext.h"
#include "cling/Interpreter/Value.h"
#include "llvm/Module.h"
#include "llvm/PassManager.h"
#include "llvm/Analysis/Verifier.h"
#include "llvm/Assembly/PrintModulePass.h"
#include "llvm/ExecutionEngine/JIT.h"
#include "llvm/ExecutionEngine/JITEventListener.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/DynamicLibrary.h"
using namespace cling;
namespace {
class JITtedFunctionCollector : public llvm::JITEventListener {
private:
llvm::SmallVector<llvm::Function*, 24> m_functions;
llvm::ExecutionEngine *m_engine;
public:
JITtedFunctionCollector(): m_functions(), m_engine(0) { }
virtual ~JITtedFunctionCollector() { }
virtual void NotifyFunctionEmitted(const llvm::Function& F, void *, size_t,
const JITEventListener::EmittedFunctionDetails&) {
m_functions.push_back(const_cast<llvm::Function *>(&F));
}
virtual void NotifyFreeingMachineCode(void* /*OldPtr*/) {}
void UnregisterFunctionMapping(llvm::ExecutionEngine&);
};
}
void JITtedFunctionCollector::UnregisterFunctionMapping(
llvm::ExecutionEngine &engine)
{
for (llvm::SmallVectorImpl<llvm::Function *>::reverse_iterator
it = m_functions.rbegin(), et = m_functions.rend();
it != et; ++it) {
llvm::Function *ff = *it;
engine.freeMachineCodeForFunction(ff);
engine.updateGlobalMapping(ff, 0);
}
m_functions.clear();
}
std::set<std::string> ExecutionContext::m_unresolvedSymbols;
std::vector<ExecutionContext::LazyFunctionCreatorFunc_t>
ExecutionContext::m_lazyFuncCreator;
ExecutionContext::ExecutionContext():
m_engine(0),
m_RunningStaticInits(false),
m_CxaAtExitRemapped(false)
{
}
void
ExecutionContext::InitializeBuilder(llvm::Module* m)
{
//
// Create an execution engine to use.
//
// Note: Engine takes ownership of the module.
assert(m && "Module cannot be null");
llvm::EngineBuilder builder(m);
builder.setOptLevel(llvm::CodeGenOpt::Less);
std::string errMsg;
builder.setErrorStr(&errMsg);
builder.setEngineKind(llvm::EngineKind::JIT);
builder.setAllocateGVsWithCode(false);
m_engine = builder.create();
assert(m_engine && "Cannot initialize builder without module!");
//m_engine->addModule(m); // Note: The engine takes ownership of the module.
// install lazy function
m_engine->InstallLazyFunctionCreator(NotifyLazyFunctionCreators);
}
ExecutionContext::~ExecutionContext()
{
}
void unresolvedSymbol()
{
// throw exception?
llvm::errs() << "ExecutionContext: calling unresolved symbol (should never happen)!\n";
}
void* ExecutionContext::HandleMissingFunction(const std::string& mangled_name)
{
// Not found in the map, add the symbol in the list of unresolved symbols
m_unresolvedSymbols.insert(mangled_name);
// Avoid "ISO C++ forbids casting between pointer-to-function and
// pointer-to-object":
return (void*)reinterpret_cast<size_t>(unresolvedSymbol);
}
void*
ExecutionContext::NotifyLazyFunctionCreators(const std::string& mangled_name)
{
for (std::vector<LazyFunctionCreatorFunc_t>::iterator it
= m_lazyFuncCreator.begin(), et = m_lazyFuncCreator.end();
it != et; ++it) {
void* ret = (void*)((LazyFunctionCreatorFunc_t)*it)(mangled_name);
if (ret) return ret;
}
return HandleMissingFunction(mangled_name);
}
void
ExecutionContext::executeFunction(llvm::StringRef funcname,
llvm::GenericValue* returnValue)
{
// Call a function without arguments, or with an SRet argument, see SRet below
if (!m_CxaAtExitRemapped) {
// Rewire atexit:
llvm::Function* atExit = m_engine->FindFunctionNamed("__cxa_atexit");
llvm::Function* clingAtExit = m_engine->FindFunctionNamed("cling_cxa_atexit");
if (atExit && clingAtExit) {
void* clingAtExitAddr = m_engine->getPointerToFunction(clingAtExit);
assert(clingAtExitAddr && "cannot find cling_cxa_atexit");
m_engine->updateGlobalMapping(atExit, clingAtExitAddr);
m_CxaAtExitRemapped = true;
}
}
// We don't care whether something was unresolved before.
m_unresolvedSymbols.clear();
llvm::Function* f = m_engine->FindFunctionNamed(funcname.data());
if (!f) {
llvm::errs() << "ExecutionContext::executeFunction: could not find function named " << funcname << '\n';
return;
}
JITtedFunctionCollector listener;
// register the listener
m_engine->RegisterJITEventListener(&listener);
m_engine->getPointerToFunction(f);
// check if there is any unresolved symbol in the list
if (!m_unresolvedSymbols.empty()) {
for (std::set<std::string>::const_iterator i = m_unresolvedSymbols.begin(),
e = m_unresolvedSymbols.end(); i != e; ++i) {
llvm::errs() << "ExecutionContext::executeFunction: symbol \'" << *i << "\' unresolved!\n";
llvm::Function *ff = m_engine->FindFunctionNamed(i->c_str());
assert(ff && "cannot find function to free");
m_engine->updateGlobalMapping(ff, 0);
m_engine->freeMachineCodeForFunction(ff);
}
m_unresolvedSymbols.clear();
// cleanup functions
listener.UnregisterFunctionMapping(*m_engine);
m_engine->UnregisterJITEventListener(&listener);
return;
}
// cleanup list and unregister our listener
m_engine->UnregisterJITEventListener(&listener);
std::vector<llvm::GenericValue> args;
bool wantReturn = (returnValue);
if (f->hasStructRetAttr()) {
// Function expects to receive the storage for the returned aggregate as
// first argument. Make sure returnValue is able to receive it, i.e.
// that it has the pointer set:
assert(returnValue && GVTOP(*returnValue) && \
"must call function returning aggregate with returnValue pointing " \
"to the storage space for return value!");
args.push_back(*returnValue);
// will get set as arg0, must not assign.
wantReturn = false;
}
if (wantReturn) {
*returnValue = m_engine->runFunction(f, args);
} else {
m_engine->runFunction(f, args);
}
m_engine->freeMachineCodeForFunction(f);
}
void
ExecutionContext::runStaticInitializersOnce(llvm::Module* m) {
assert(m && "Module must not be null");
if (!m_engine)
InitializeBuilder(m);
assert(m_engine && "Code generation did not create an engine!");
if (!m_RunningStaticInits) {
m_RunningStaticInits = true;
llvm::GlobalVariable* gctors
= m->getGlobalVariable("llvm.global_ctors", true);
if (gctors) {
m_engine->runStaticConstructorsDestructors(false);
gctors->eraseFromParent();
}
m_RunningStaticInits = false;
}
}
void
ExecutionContext::runStaticDestructorsOnce(llvm::Module* m) {
assert(m && "Module must not be null");
assert(m_engine && "Code generation did not create an engine!");
llvm::GlobalVariable* gdtors
= m->getGlobalVariable("llvm.global_dtors", true);
if (gdtors) {
m_engine->runStaticConstructorsDestructors(true);
}
}
int
ExecutionContext::verifyModule(llvm::Module* m)
{
//
// Verify generated module.
//
bool mod_has_errs = llvm::verifyModule(*m, llvm::PrintMessageAction);
if (mod_has_errs) {
return 1;
}
return 0;
}
void
ExecutionContext::printModule(llvm::Module* m)
{
//
// Print module LLVM code in human-readable form.
//
llvm::PassManager PM;
PM.add(llvm::createPrintModulePass(&llvm::outs()));
PM.run(*m);
}
void
ExecutionContext::installLazyFunctionCreator(LazyFunctionCreatorFunc_t fp)
{
m_lazyFuncCreator.push_back(fp);
}
bool ExecutionContext::addSymbol(const char* symbolName, void* symbolAddress){
void* actualAdress
= llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(symbolName);
if (actualAdress)
return false;
llvm::sys::DynamicLibrary::AddSymbol(symbolName, symbolAddress);
return true;
}

View File

@ -0,0 +1,83 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_EXECUTIONCONTEXT_H
#define CLING_EXECUTIONCONTEXT_H
#include "llvm/ADT/StringRef.h"
#include <vector>
#include <set>
namespace llvm {
class Module;
class ExecutionEngine;
struct GenericValue;
}
namespace clang {
class CompilerInstance;
class CodeGenerator;
}
namespace cling {
class Interpreter;
class Value;
class ExecutionContext {
public:
typedef void* (*LazyFunctionCreatorFunc_t)(const std::string&);
public:
ExecutionContext();
~ExecutionContext();
void installLazyFunctionCreator(LazyFunctionCreatorFunc_t fp);
void runStaticInitializersOnce(llvm::Module* m);
void runStaticDestructorsOnce(llvm::Module* m);
void executeFunction(llvm::StringRef function,
llvm::GenericValue* returnValue = 0);
///\brief Adds a symbol (function) to the execution engine.
///
/// Allows runtime declaration of a function passing its pointer for being
/// used by JIT generated code.
///
/// @param[in] symbolName - The name of the symbol as required by the
/// linker (mangled if needed)
/// @param[in] symbolAddress - The function pointer to register
/// @returns true if the symbol is successfully registered, false otherwise.
///
bool addSymbol(const char* symbolName, void* symbolAddress);
llvm::ExecutionEngine* getExecutionEngine() const {
return m_engine;
}
private:
static void* HandleMissingFunction(const std::string&);
static void* NotifyLazyFunctionCreators(const std::string&);
int verifyModule(llvm::Module* m);
void printModule(llvm::Module* m);
void InitializeBuilder(llvm::Module* m);
static std::set<std::string> m_unresolvedSymbols;
static std::vector<LazyFunctionCreatorFunc_t> m_lazyFuncCreator;
llvm::ExecutionEngine* m_engine; // Owned by JIT
/// \brief prevent the recursive run of the static inits
bool m_RunningStaticInits;
/// \brief Whether cxa_at_exit has been rewired to the Interpreter's version
bool m_CxaAtExitRemapped;
};
} // end cling
#endif // CLING_EXECUTIONCONTEXT_H

View File

@ -0,0 +1,350 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------
#include "IncrementalParser.h"
#include "ASTDumper.h"
#include "ASTNodeEraser.h"
#include "DeclCollector.h"
#include "DeclExtractor.h"
#include "DynamicLookup.h"
#include "ValuePrinterSynthesizer.h"
#include "cling/Interpreter/CIFactory.h"
#include "cling/Interpreter/Interpreter.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclGroup.h"
#include "clang/Basic/FileManager.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Parse/Parser.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Serialization/ASTWriter.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_os_ostream.h"
#include <ctime>
#include <iostream>
#include <stdio.h>
#include <sstream>
using namespace clang;
namespace cling {
IncrementalParser::IncrementalParser(Interpreter* interp,
int argc, const char* const *argv,
const char* llvmdir):
m_Interpreter(interp), m_Consumer(0) {
CompilerInstance* CI
= CIFactory::createCI(llvm::MemoryBuffer::getMemBuffer("", "CLING"),
argc, argv, llvmdir);
assert(CI && "CompilerInstance is (null)!");
m_Consumer = dyn_cast<DeclCollector>(&CI->getASTConsumer());
assert(m_Consumer && "Expected ChainedConsumer!");
m_CI.reset(CI);
if (CI->getFrontendOpts().ProgramAction != clang::frontend::ParseSyntaxOnly){
m_CodeGen.reset(CreateLLVMCodeGen(CI->getDiagnostics(), "cling input",
CI->getCodeGenOpts(),
*m_Interpreter->getLLVMContext()
));
}
CreateSLocOffsetGenerator();
// Add transformers to the IncrementalParser, which owns them
m_TTransformers.push_back(new EvaluateTSynthesizer(interp, &CI->getSema()));
m_TTransformers.push_back(new DeclExtractor(&getCI()->getSema()));
m_TTransformers.push_back(new ValuePrinterSynthesizer(interp, &CI->getSema()));
m_TTransformers.push_back(new ASTDumper());
m_Parser.reset(new Parser(CI->getPreprocessor(), CI->getSema(),
false /*skipFuncBodies*/));
CI->getPreprocessor().EnterMainSourceFile();
// Initialize the parser after we have entered the main source file.
m_Parser->Initialize();
// Perform initialization that occurs after the parser has been initialized
// but before it parses anything. Initializes the consumers too.
CI->getSema().Initialize();
}
IncrementalParser::~IncrementalParser() {
if (hasCodeGenerator()) {
getCodeGenerator()->ReleaseModule();
}
for (size_t i = 0; i < m_Transactions.size(); ++i)
delete m_Transactions[i];
for (size_t i = 0; i < m_TTransformers.size(); ++i)
delete m_TTransformers[i];
}
// pin the vtable here since there is no point to create dedicated to that
// cpp file.
TransactionTransformer::~TransactionTransformer() {}
void IncrementalParser::beginTransaction(const CompilationOptions& Opts) {
llvm::Module* M = 0;
if (hasCodeGenerator())
M = getCodeGenerator()->GetModule();
Transaction* NewCurT = new Transaction(Opts, M);
Transaction* OldCurT = m_Consumer->getTransaction();
m_Consumer->setTransaction(NewCurT);
// If we are in the middle of transaction and we see another begin
// transaction - it must be nested transaction.
if (OldCurT && !OldCurT->isCompleted()) {
OldCurT->addNestedTransaction(NewCurT); // takes the ownership
return;
}
m_Transactions.push_back(NewCurT);
}
void IncrementalParser::endTransaction() const {
Transaction* CurT = m_Consumer->getTransaction();
CurT->setCompleted();
const DiagnosticsEngine& Diags = getCI()->getSema().getDiagnostics();
//TODO: Make the enum orable.
if (Diags.getNumWarnings() > 0)
CurT->setIssuedDiags(Transaction::kWarnings);
if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
CurT->setIssuedDiags(Transaction::kErrors);
if (CurT->isNestedTransaction()) {
assert(!CurT->getParent()->isCompleted()
&& "Parent transaction completed!?");
CurT = m_Consumer->getTransaction()->getParent();
}
}
void IncrementalParser::commitCurrentTransaction() {
Transaction* CurT = m_Consumer->getTransaction();
assert(CurT->isCompleted() && "Transaction not ended!?");
// Check for errors...
if (CurT->getIssuedDiags() == Transaction::kErrors) {
rollbackTransaction(CurT);
return;
}
// We are sure it's safe to pipe it through the transformers
for (size_t i = 0; i < m_TTransformers.size(); ++i)
if (!m_TTransformers[i]->TransformTransaction(*CurT)) {
// Roll back on error in a transformer
rollbackTransaction(CurT);
return;
}
// Pull all template instantiations in that came from the consumers.
getCI()->getSema().PerformPendingInstantiations();
m_Consumer->HandleTranslationUnit(getCI()->getASTContext());
if (CurT->getCompilationOpts().CodeGeneration && hasCodeGenerator()) {
// Reset the module builder to clean up global initializers, c'tors, d'tors
getCodeGenerator()->Initialize(getCI()->getASTContext());
// codegen the transaction
for (Transaction::const_iterator I = CurT->decls_begin(),
E = CurT->decls_end(); I != E; ++I) {
getCodeGenerator()->HandleTopLevelDecl(*I);
}
getCodeGenerator()->HandleTranslationUnit(getCI()->getASTContext());
// run the static initializers that came from codegenning
m_Interpreter->runStaticInitializersOnce();
}
CurT->setState(Transaction::kCommitted);
}
void IncrementalParser::rollbackTransaction(Transaction* T) const {
ASTNodeEraser NodeEraser(&getCI()->getSema());
if (NodeEraser.RevertTransaction(T))
T->setState(Transaction::kRolledBack);
else
T->setState(Transaction::kRolledBackWithErrors);
}
// Each input line is contained in separate memory buffer. The SourceManager
// assigns sort-of invalid FileID for each buffer, i.e there is no FileEntry
// for the MemoryBuffer's FileID. That in turn is problem because invalid
// SourceLocations are given to the diagnostics. Thus the diagnostics cannot
// order the overloads, for example
//
// Our work-around is creating a virtual file, which doesn't exist on the disk
// with enormous size (no allocation is done). That file has valid FileEntry
// and so on... We use it for generating valid SourceLocations with valid
// offsets so that it doesn't cause any troubles to the diagnostics.
//
// +---------------------+
// | Main memory buffer |
// +---------------------+
// | Virtual file SLoc |
// | address space |<-----------------+
// | ... |<------------+ |
// | ... | | |
// | ... |<----+ | |
// | ... | | | |
// +~~~~~~~~~~~~~~~~~~~~~+ | | |
// | input_line_1 | ....+.......+..--+
// +---------------------+ | |
// | input_line_2 | ....+.....--+
// +---------------------+ |
// | ... | |
// +---------------------+ |
// | input_line_N | ..--+
// +---------------------+
//
void IncrementalParser::CreateSLocOffsetGenerator() {
SourceManager& SM = getCI()->getSourceManager();
FileManager& FM = SM.getFileManager();
const FileEntry* FE
= FM.getVirtualFile("Interactrive/InputLineIncluder", 1U << 15U, time(0));
m_VirtualFileID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User);
assert(!m_VirtualFileID.isInvalid() && "No VirtualFileID created?");
}
IncrementalParser::EParseResult
IncrementalParser::Compile(llvm::StringRef input,
const CompilationOptions& Opts) {
beginTransaction(Opts);
EParseResult Result = ParseInternal(input);
endTransaction();
// Check for errors coming from our custom consumers.
DiagnosticConsumer& DClient = m_CI->getDiagnosticClient();
DClient.BeginSourceFile(getCI()->getLangOpts(),
&getCI()->getPreprocessor());
commitCurrentTransaction();
DClient.EndSourceFile();
m_CI->getDiagnostics().Reset();
return Result;
}
Transaction* IncrementalParser::Parse(llvm::StringRef input) {
beginTransaction(CompilationOptions());
ParseInternal(input);
endTransaction();
return getLastTransaction();
}
// Add the input to the memory buffer, parse it, and add it to the AST.
IncrementalParser::EParseResult
IncrementalParser::ParseInternal(llvm::StringRef input) {
if (input.empty()) return IncrementalParser::kSuccess;
PrettyStackTraceParserEntry CrashInfo(*m_Parser.get());
// Recover resources if we crash before exiting this method.
llvm::CrashRecoveryContextCleanupRegistrar<Parser>
CleanupParser(m_Parser.get());
Sema& S = getCI()->getSema();
// Recover resources if we crash before exiting this method.
llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(&S);
Preprocessor& PP = m_CI->getPreprocessor();
DiagnosticConsumer& DClient = m_CI->getDiagnosticClient();
if (!PP.getCurrentLexer()) {
PP.EnterSourceFile(m_CI->getSourceManager().getMainFileID(),
0, SourceLocation());
}
PP.enableIncrementalProcessing();
DClient.BeginSourceFile(m_CI->getLangOpts(), &PP);
std::ostringstream source_name;
source_name << "input_line_" << (m_MemoryBuffers.size() + 1);
// Create an uninitialized memory buffer, copy code in and append "\n"
size_t InputSize = input.size(); // don't include trailing 0
// MemBuffer size should *not* include terminating zero
llvm::MemoryBuffer* MB
= llvm::MemoryBuffer::getNewUninitMemBuffer(InputSize + 1,
source_name.str());
char* MBStart = const_cast<char*>(MB->getBufferStart());
memcpy(MBStart, input.data(), InputSize);
memcpy(MBStart + InputSize, "\n", 2);
m_MemoryBuffers.push_back(MB);
SourceManager& SM = getCI()->getSourceManager();
// Create SourceLocation, which will allow clang to order the overload
// candidates for example
SourceLocation NewLoc = SM.getLocForStartOfFile(m_VirtualFileID);
NewLoc = NewLoc.getLocWithOffset(m_MemoryBuffers.size() + 1);
// Create FileID for the current buffer
FileID FID = SM.createFileIDForMemBuffer(m_MemoryBuffers.back(),
/*LoadedID*/0,
/*LoadedOffset*/0, NewLoc);
PP.EnterSourceFile(FID, /*DirLookup*/0, NewLoc);
Parser::DeclGroupPtrTy ADecl;
while (!m_Parser->ParseTopLevelDecl(ADecl)) {
// If we got a null return and something *was* parsed, ignore it. This
// is due to a top-level semicolon, an action override, or a parse error
// skipping something.
if (ADecl)
m_Consumer->HandleTopLevelDecl(ADecl.getAsVal<DeclGroupRef>());
};
// Process any TopLevelDecls generated by #pragma weak.
for (llvm::SmallVector<Decl*,2>::iterator I = S.WeakTopLevelDecls().begin(),
E = S.WeakTopLevelDecls().end(); I != E; ++I) {
m_Consumer->HandleTopLevelDecl(DeclGroupRef(*I));
}
S.PerformPendingInstantiations();
DClient.EndSourceFile();
PP.enableIncrementalProcessing(false);
DiagnosticsEngine& Diag = S.getDiagnostics();
if (Diag.hasErrorOccurred())
return IncrementalParser::kFailed;
else if (Diag.getNumWarnings())
return IncrementalParser::kSuccessWithWarnings;
return IncrementalParser::kSuccess;
}
void IncrementalParser::unloadTransaction(Transaction* T) {
if (!T)
T = getLastTransaction();
assert(T->getState() == Transaction::kCommitted &&
"Unloading not commited transaction?");
assert(T->getModule() &&
"Trying to uncodegen transaction taken in syntax only mode. ");
ASTNodeEraser NodeEraser(&getCI()->getSema());
NodeEraser.RevertTransaction(T);
}
} // namespace cling

View File

@ -0,0 +1,163 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_INCREMENTAL_PARSER_H
#define CLING_INCREMENTAL_PARSER_H
#include "DeclCollector.h"
#include "CompilationOptions.h"
#include "Transaction.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclGroup.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/StringRef.h"
#include <vector>
namespace llvm {
struct GenericValue;
class MemoryBuffer;
}
namespace clang {
class ASTConsumer;
class CodeGenerator;
class CompilerInstance;
class Decl;
class FileID;
class FunctionDecl;
class Parser;
class Sema;
class SourceLocation;
}
namespace cling {
class CIFactory;
class DeclCollector;
class ExecutionContext;
class Interpreter;
class TransactionTransformer;
///\brief Responsible for the incremental parsing and compilation of input.
///
/// The class manages the entire process of compilation line-by-line by
/// appending the compiled delta to clang'a AST. It provides basic operations
/// on the already compiled code. See cling::Transaction class.
///
class IncrementalParser {
private:
// our interpreter context
Interpreter* m_Interpreter;
// compiler instance.
llvm::OwningPtr<clang::CompilerInstance> m_CI;
// parser (incremental)
llvm::OwningPtr<clang::Parser> m_Parser;
// One buffer for each command line, owner by the source file manager
std::vector<llvm::MemoryBuffer*> m_MemoryBuffers;
// file ID of the memory buffer
clang::FileID m_VirtualFileID;
// CI owns it
DeclCollector* m_Consumer;
///\brief Holds information for the all transactions.
///
llvm::SmallVector<Transaction*, 64> m_Transactions;
///\brief Code generator
///
llvm::OwningPtr<clang::CodeGenerator> m_CodeGen;
///\brief Contains the transaction transformers.
///
llvm::SmallVector<TransactionTransformer*, 4> m_TTransformers;
public:
enum EParseResult {
kSuccess,
kSuccessWithWarnings,
kFailed
};
IncrementalParser(Interpreter* interp, int argc, const char* const *argv,
const char* llvmdir);
~IncrementalParser();
clang::CompilerInstance* getCI() const { return m_CI.get(); }
clang::Parser* getParser() const { return m_Parser.get(); }
clang::CodeGenerator* getCodeGenerator() const { return m_CodeGen.get(); }
bool hasCodeGenerator() const { return m_CodeGen.get(); }
/// \{
/// \name Transaction Support
///\brief Starts a transaction.
///
void beginTransaction(const CompilationOptions& Opts);
///\brief Finishes a transaction.
///
void endTransaction() const;
///\brief Commits the current transaction if it was compete. I.e pipes it
/// through the consumer chain, including codegen.
///
void commitCurrentTransaction();
///\brief Reverts the AST into its previous state.
///
/// If one of the declarations caused error in clang it is rolled back from
/// the AST. This is essential feature for the error recovery subsystem.
///
///\param[in] T - The transaction to be reverted from the AST
///
void rollbackTransaction(Transaction* T) const;
///\brief Returns the last transaction the incremental parser saw.
///
Transaction* getLastTransaction() {
return m_Transactions.back();
}
///\brief Returns the last transaction the incremental parser saw.
///
const Transaction* getLastTransaction() const {
return m_Transactions.back();
}
/// \}
///\brief Compiles the given input with the given compilation options.
///
EParseResult Compile(llvm::StringRef input, const CompilationOptions& Opts);
///\brief Parses the given input without calling the custom consumers and
/// code generation.
///
/// I.e changes to the decls in the transaction commiting it will cause
/// different executable code.
///
///\param[in] input - The code to parse.
///\returns The transaction coresponding to the input.
///
Transaction* Parse(llvm::StringRef input);
void unloadTransaction(Transaction* T);
private:
void CreateSLocOffsetGenerator();
EParseResult ParseInternal(llvm::StringRef input);
};
} // end namespace cling
#endif // CLING_INCREMENTAL_PARSER_H

View File

@ -0,0 +1,825 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Lukasz Janyst <ljanyst@cern.ch>
//------------------------------------------------------------------------------
#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/LookupHelper.h"
#include "CompilationOptions.h"
#include "DynamicLookup.h"
#include "ExecutionContext.h"
#include "IncrementalParser.h"
#include "cling/Interpreter/CIFactory.h"
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/Value.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Mangle.h"
#include "clang/AST/DeclarationName.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/Utils.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Parse/Parser.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Overload.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/TemplateDeduction.h"
#include "llvm/Linker.h"
#include "llvm/LLVMContext.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/raw_os_ostream.h"
#include <cstdio>
#include <iostream>
#include <sstream>
#include <vector>
using namespace clang;
namespace {
static bool tryLinker(const std::string& filename,
const cling::InvocationOptions& Opts,
llvm::Module* module) {
assert(module && "Module must exist for linking!");
llvm::Linker L("cling", module, llvm::Linker::QuietWarnings
| llvm::Linker::QuietErrors);
for (std::vector<llvm::sys::Path>::const_iterator I
= Opts.LibSearchPath.begin(), E = Opts.LibSearchPath.end(); I != E;
++I) {
L.addPath(*I);
}
L.addSystemPaths();
bool Native = true;
if (L.LinkInLibrary(filename, Native)) {
// that didn't work, try bitcode:
llvm::sys::Path FilePath(filename);
std::string Magic;
if (!FilePath.getMagicNumber(Magic, 64)) {
// filename doesn't exist...
L.releaseModule();
return false;
}
if (llvm::sys::IdentifyFileType(Magic.c_str(), 64)
== llvm::sys::Bitcode_FileType) {
// We are promised a bitcode file, complain if it fails
L.setFlags(0);
if (L.LinkInFile(llvm::sys::Path(filename), Native)) {
L.releaseModule();
return false;
}
} else {
// Nothing the linker can handle
L.releaseModule();
return false;
}
} else if (Native) {
// native shared library, load it!
llvm::sys::Path SoFile = L.FindLib(filename);
assert(!SoFile.isEmpty() && "The shared lib exists but can't find it!");
std::string errMsg;
bool hasError = llvm::sys::DynamicLibrary
::LoadLibraryPermanently(SoFile.str().c_str(), &errMsg);
if (hasError) {
llvm::errs() << "Could not load shared library!\n"
<< "\n"
<< errMsg.c_str();
L.releaseModule();
return false;
}
}
L.releaseModule();
return true;
}
static bool canWrapForCall(const std::string& input_line) {
// Whether input_line can be wrapped into a function.
// "1" can, "#include <vector>" can't.
if (input_line.length() > 1 && input_line[0] == '#') return false;
if (input_line.compare(0, strlen("extern "), "extern ") == 0) return false;
if (input_line.compare(0, strlen("using "), "using ") == 0) return false;
return true;
}
} // unnamed namespace
namespace cling {
// "Declared" to the JIT in RuntimeUniverse.h
namespace runtime {
namespace internal {
int local_cxa_atexit(void (*func) (void*), void* arg,
void* dso, Interpreter* interp) {
return interp->CXAAtExit(func, arg, dso);
}
struct __trigger__cxa_atexit {
~__trigger__cxa_atexit();
};
__trigger__cxa_atexit::~__trigger__cxa_atexit() {}
}
}
// This function isn't referenced outside its translation unit, but it
// can't use the "static" keyword because its address is used for
// GetMainExecutable (since some platforms don't support taking the
// address of main, and some platforms can't implement GetMainExecutable
// without being given the address of a function in the main executable).
llvm::sys::Path GetExecutablePath(const char *Argv0) {
// This just needs to be some symbol in the binary; C++ doesn't
// allow taking the address of ::main however.
void *MainAddr = (void*) (intptr_t) GetExecutablePath;
return llvm::sys::Path::GetMainExecutable(Argv0, MainAddr);
}
Interpreter::NamedDeclResult::NamedDeclResult(llvm::StringRef Decl,
Interpreter* interp,
const DeclContext* Within)
: m_Interpreter(interp),
m_Context(m_Interpreter->getCI()->getASTContext()),
m_CurDeclContext(Within),
m_Result(0)
{
LookupDecl(Decl);
}
Interpreter::NamedDeclResult&
Interpreter::NamedDeclResult::LookupDecl(llvm::StringRef Decl) {
DeclarationName Name(&m_Context.Idents.get(Decl));
DeclContext::lookup_const_result Lookup = m_CurDeclContext->lookup(Name);
// If more than one found return 0. Cannot handle ambiguities.
if (Lookup.second - Lookup.first == 1) {
if (DeclContext* DC = dyn_cast<DeclContext>(*Lookup.first))
m_CurDeclContext = DC;
else
m_CurDeclContext = (*Lookup.first)->getDeclContext();
m_Result = (*Lookup.first);
}
else {
m_Result = 0;
}
return *this;
}
NamedDecl* Interpreter::NamedDeclResult::getSingleDecl() const {
// TODO: Check whether it is only one decl if (end-begin == 1 )
return dyn_cast<NamedDecl>(m_Result);
}
void Interpreter::unload() {
m_IncrParser->unloadTransaction(0);
}
Interpreter::Interpreter(int argc, const char* const *argv,
const char* llvmdir /*= 0*/) :
m_UniqueCounter(0), m_PrintAST(false), m_DynamicLookupEnabled(false) {
m_LLVMContext.reset(new llvm::LLVMContext);
std::vector<unsigned> LeftoverArgsIdx;
m_Opts = InvocationOptions::CreateFromArgs(argc, argv, LeftoverArgsIdx);
std::vector<const char*> LeftoverArgs;
for (size_t I = 0, N = LeftoverArgsIdx.size(); I < N; ++I) {
LeftoverArgs.push_back(argv[LeftoverArgsIdx[I]]);
}
m_IncrParser.reset(new IncrementalParser(this, LeftoverArgs.size(),
&LeftoverArgs[0],
llvmdir));
m_LookupHelper.reset(new LookupHelper(m_IncrParser->getParser()));
m_ExecutionContext.reset(new ExecutionContext());
m_ValuePrintStream.reset(new llvm::raw_os_ostream(std::cout));
// Add path to interpreter's include files
// Try to find the headers in the src folder first
#ifdef CLING_SRCDIR_INCL
llvm::sys::Path SrcP(CLING_SRCDIR_INCL);
if (SrcP.canRead())
AddIncludePath(SrcP.str());
#endif
llvm::sys::Path P = GetExecutablePath(argv[0]);
if (!P.isEmpty()) {
P.eraseComponent(); // Remove /cling from foo/bin/clang
P.eraseComponent(); // Remove /bin from foo/bin
// Get foo/include
P.appendComponent("include");
if (P.canRead())
AddIncludePath(P.str());
else {
#ifdef CLING_INSTDIR_INCL
llvm::sys::Path InstP(CLING_INSTDIR_INCL);
if (InstP.canRead())
AddIncludePath(InstP.str());
#endif
}
}
m_ExecutionContext->addSymbol("local_cxa_atexit",
(void*)(intptr_t)&cling::runtime::internal::local_cxa_atexit);
if (getCI()->getLangOpts().CPlusPlus) {
// Set up common declarations which are going to be available
// only at runtime
// Make sure that the universe won't be included to compile time by using
// -D __CLING__ as CompilerInstance's arguments
#ifdef _WIN32
// We have to use the #defined __CLING__ on windows first.
//FIXME: Find proper fix.
declare("#ifdef __CLING__ \n#endif");
#endif
declare("#include \"cling/Interpreter/RuntimeUniverse.h\"");
declare("#include \"cling/Interpreter/ValuePrinter.h\"");
// Set up the gCling variable
std::stringstream initializer;
initializer << "gCling=(cling::Interpreter*)" << (uintptr_t)this << ";";
evaluate(initializer.str());
}
else {
declare("#include \"cling/Interpreter/CValuePrinter.h\"");
}
handleFrontendOptions();
}
Interpreter::~Interpreter() {
for (size_t I = 0, N = m_AtExitFuncs.size(); I < N; ++I) {
const CXAAtExitElement& AEE = m_AtExitFuncs[N - I - 1];
(*AEE.m_Func)(AEE.m_Arg);
}
}
const char* Interpreter::getVersion() const {
return "$Id$";
}
void Interpreter::handleFrontendOptions() {
if (m_Opts.ShowVersion) {
llvm::outs() << getVersion() << '\n';
}
if (m_Opts.Help) {
m_Opts.PrintHelp();
}
}
void Interpreter::AddIncludePath(llvm::StringRef incpath)
{
// Add the given path to the list of directories in which the interpreter
// looks for include files. Only one path item can be specified at a
// time, i.e. "path1:path2" is not supported.
CompilerInstance* CI = getCI();
HeaderSearchOptions& headerOpts = CI->getHeaderSearchOpts();
const bool IsUserSupplied = false;
const bool IsFramework = false;
const bool IsSysRootRelative = true;
headerOpts.AddPath(incpath, frontend::Angled, IsUserSupplied, IsFramework,
IsSysRootRelative);
Preprocessor& PP = CI->getPreprocessor();
ApplyHeaderSearchOptions(PP.getHeaderSearchInfo(), headerOpts,
PP.getLangOpts(),
PP.getTargetInfo().getTriple());
}
// Copied from clang/lib/Frontend/CompilerInvocation.cpp
void Interpreter::DumpIncludePath() {
const HeaderSearchOptions Opts(getCI()->getHeaderSearchOpts());
std::vector<std::string> Res;
if (Opts.Sysroot != "/") {
Res.push_back("-isysroot");
Res.push_back(Opts.Sysroot);
}
/// User specified include entries.
for (unsigned i = 0, e = Opts.UserEntries.size(); i != e; ++i) {
const HeaderSearchOptions::Entry &E = Opts.UserEntries[i];
if (E.IsFramework && (E.Group != frontend::Angled || !E.IsUserSupplied))
llvm::report_fatal_error("Invalid option set!");
if (E.IsUserSupplied) {
switch (E.Group) {
case frontend::After:
Res.push_back("-idirafter");
break;
case frontend::Quoted:
Res.push_back("-iquote");
break;
case frontend::System:
Res.push_back("-isystem");
break;
case frontend::IndexHeaderMap:
Res.push_back("-index-header-map");
Res.push_back(E.IsFramework? "-F" : "-I");
break;
case frontend::CSystem:
Res.push_back("-c-isystem");
break;
case frontend::CXXSystem:
Res.push_back("-cxx-isystem");
break;
case frontend::ObjCSystem:
Res.push_back("-objc-isystem");
break;
case frontend::ObjCXXSystem:
Res.push_back("-objcxx-isystem");
break;
case frontend::Angled:
Res.push_back(E.IsFramework ? "-F" : "-I");
break;
}
} else {
if (E.Group != frontend::Angled && E.Group != frontend::System)
llvm::report_fatal_error("Invalid option set!");
Res.push_back(E.Group == frontend::Angled ? "-iwithprefixbefore" :
"-iwithprefix");
}
Res.push_back(E.Path);
}
if (!Opts.ResourceDir.empty()) {
Res.push_back("-resource-dir");
Res.push_back(Opts.ResourceDir);
}
if (!Opts.ModuleCachePath.empty()) {
Res.push_back("-fmodule-cache-path");
Res.push_back(Opts.ModuleCachePath);
}
if (!Opts.UseStandardSystemIncludes)
Res.push_back("-nostdinc");
if (!Opts.UseStandardCXXIncludes)
Res.push_back("-nostdinc++");
if (Opts.UseLibcxx)
Res.push_back("-stdlib=libc++");
if (Opts.Verbose)
Res.push_back("-v");
// print'em all
for (unsigned i = 0; i < Res.size(); ++i) {
llvm::outs() << Res[i] <<"\n";
}
}
CompilerInstance* Interpreter::getCI() const {
return m_IncrParser->getCI();
}
llvm::ExecutionEngine* Interpreter::getExecutionEngine() const {
return m_ExecutionContext->getExecutionEngine();
}
llvm::Module* Interpreter::getModule() const {
return m_IncrParser->getCodeGenerator()->GetModule();
}
///\brief Maybe transform the input line to implement cint command line
/// semantics (declarations are global) and compile to produce a module.
///
Interpreter::CompilationResult
Interpreter::process(const std::string& input, Value* V /* = 0 */,
const Decl** D /* = 0 */) {
CompilationOptions CO;
CO.DeclarationExtraction = 1;
CO.ValuePrinting = CompilationOptions::VPAuto;
CO.DynamicScoping = isDynamicLookupEnabled();
CO.Debug = isPrintingAST();
if (!canWrapForCall(input))
return declare(input, D);
if (Evaluate(input, CO, V) == Interpreter::kFailure) {
if (D)
*D = 0;
return Interpreter::kFailure;
}
if (D)
*D = m_IncrParser->getLastTransaction()->getFirstDecl().getSingleDecl();
return Interpreter::kSuccess;
}
Interpreter::CompilationResult
Interpreter::parse(const std::string& input) {
CompilationOptions CO;
CO.CodeGeneration = 0;
CO.DeclarationExtraction = 0;
CO.ValuePrinting = 0;
CO.DynamicScoping = isDynamicLookupEnabled();
CO.Debug = isPrintingAST();
return Declare(input, CO);
}
Interpreter::CompilationResult
Interpreter::declare(const std::string& input, const Decl** D /* = 0 */) {
CompilationOptions CO;
CO.DeclarationExtraction = 0;
CO.ValuePrinting = 0;
CO.DynamicScoping = isDynamicLookupEnabled();
CO.Debug = isPrintingAST();
return Declare(input, CO, D);
}
Interpreter::CompilationResult
Interpreter::evaluate(const std::string& input, Value* V /* = 0 */) {
// Here we might want to enforce further restrictions like: Only one
// ExprStmt can be evaluated and etc. Such enforcement cannot happen in the
// worker, because it is used from various places, where there is no such
// rule
CompilationOptions CO;
CO.DeclarationExtraction = 0;
CO.ValuePrinting = 0;
return Evaluate(input, CO, V);
}
Interpreter::CompilationResult
Interpreter::echo(const std::string& input, Value* V /* = 0 */) {
CompilationOptions CO;
CO.DeclarationExtraction = 0;
CO.ValuePrinting = CompilationOptions::VPEnabled;
return Evaluate(input, CO, V);
}
void Interpreter::WrapInput(std::string& input, std::string& fname) {
fname = createUniqueWrapper();
input.insert(0, "void " + fname + "() {\n ");
input.append("\n;\n}");
}
llvm::StringRef Interpreter::createUniqueWrapper() {
const size_t size = sizeof("__cling_Un1Qu3") + sizeof(m_UniqueCounter);
llvm::SmallString<size> out("__cling_Un1Qu3");
llvm::raw_svector_ostream(out) << m_UniqueCounter++;
return (getCI()->getASTContext().Idents.getOwn(out)).getName();
}
bool Interpreter::RunFunction(llvm::StringRef fname,
llvm::GenericValue* res) {
if (getCI()->getDiagnostics().hasErrorOccurred())
return false;
if (!m_IncrParser->hasCodeGenerator()) {
return true;
}
std::string mangledNameIfNeeded;
FunctionDecl* FD = cast_or_null<FunctionDecl>(LookupDecl(fname).
getSingleDecl()
);
if (FD) {
if (!FD->isExternC()) {
llvm::raw_string_ostream RawStr(mangledNameIfNeeded);
llvm::OwningPtr<MangleContext>
Mangle(getCI()->getASTContext().createMangleContext());
Mangle->mangleName(FD, RawStr);
RawStr.flush();
fname = mangledNameIfNeeded;
}
m_ExecutionContext->executeFunction(fname, res);
return true;
}
return false;
}
void Interpreter::createUniqueName(std::string& out) {
out = "Un1Qu3";
llvm::raw_string_ostream(out) << m_UniqueCounter++;
}
Interpreter::CompilationResult
Interpreter::Declare(const std::string& input, const CompilationOptions& CO,
const clang::Decl** D /* = 0 */) {
if (m_IncrParser->Compile(input, CO) != IncrementalParser::kFailed) {
if (D)
*D = m_IncrParser->getLastTransaction()->getFirstDecl().getSingleDecl();
return Interpreter::kSuccess;
}
return Interpreter::kFailure;
}
Interpreter::CompilationResult
Interpreter::Evaluate(const std::string& input, const CompilationOptions& CO,
Value* V /* = 0 */) {
Sema& TheSema = getCI()->getSema();
DiagnosticsEngine& Diag = getCI()->getDiagnostics();
// Disable warnings which doesn't make sense when using the prompt
// This gets reset with the clang::Diagnostics().Reset()
Diag.setDiagnosticMapping(clang::diag::warn_unused_expr,
clang::diag::MAP_IGNORE, SourceLocation());
Diag.setDiagnosticMapping(clang::diag::warn_unused_call,
clang::diag::MAP_IGNORE, SourceLocation());
// Wrap the expression
std::string WrapperName;
std::string Wrapper = input;
WrapInput(Wrapper, WrapperName);
QualType RetTy = getCI()->getASTContext().VoidTy;
if (V) {
const Transaction* CurT = m_IncrParser->Parse(Wrapper);
assert(CurT->size() && "No decls created by Parse!");
// Find the wrapper function declaration.
//
// Note: The parse may have created a whole set of decls if a template
// instantiation happened. Our wrapper function should be the
// last decl in the set.
//
FunctionDecl* TopLevelFD
= dyn_cast<FunctionDecl>(CurT->getLastDecl().getSingleDecl());
assert(TopLevelFD && "No Decls Parsed?");
DeclContext* CurContext = TheSema.CurContext;
TheSema.CurContext = TopLevelFD;
ASTContext& Context(getCI()->getASTContext());
// We have to be able to mark the expression for printout. There are three
// scenarios:
// 0: Expression printing disabled - don't do anything just disable the
// consumer
// is our marker, even if there wasn't missing ';'.
// 1: Expression printing enabled - make sure we don't have NullStmt,
// which is used as a marker to suppress the print out.
// 2: Expression printing auto - do nothing - rely on the omitted ';' to
// not produce the suppress marker.
if (CompoundStmt* CS = dyn_cast<CompoundStmt>(TopLevelFD->getBody())) {
// Collect all Stmts, contained in the CompoundStmt
llvm::SmallVector<Stmt *, 4> Stmts;
for (CompoundStmt::body_iterator iStmt = CS->body_begin(),
eStmt = CS->body_end(); iStmt != eStmt; ++iStmt)
Stmts.push_back(*iStmt);
size_t indexOfLastExpr = Stmts.size();
while(indexOfLastExpr--) {
// find the trailing expression statement (skip e.g. null statements)
if (Expr* E = dyn_cast_or_null<Expr>(Stmts[indexOfLastExpr])) {
RetTy = E->getType();
if (!RetTy->isVoidType()) {
// Change the void function's return type
FunctionProtoType::ExtProtoInfo EPI;
QualType FuncTy = Context.getFunctionType(RetTy,/* ArgArray = */0,
/* NumArgs = */0, EPI);
TopLevelFD->setType(FuncTy);
// Strip the parenthesis if any
if (ParenExpr* PE = dyn_cast<ParenExpr>(E))
E = PE->getSubExpr();
// Change it with return stmt
Stmts[indexOfLastExpr]
= TheSema.ActOnReturnStmt(SourceLocation(), E).take();
}
// even if void: we found an expression
break;
}
}
// case 1:
if (CO.ValuePrinting == CompilationOptions::VPEnabled)
if (indexOfLastExpr < Stmts.size() - 1 &&
isa<NullStmt>(Stmts[indexOfLastExpr + 1]))
Stmts.erase(Stmts.begin() + indexOfLastExpr);
// Stmts.insert(Stmts.begin() + indexOfLastExpr + 1,
// TheSema.ActOnNullStmt(SourceLocation()).take());
// Update the CompoundStmt body
CS->setStmts(TheSema.getASTContext(), Stmts.data(), Stmts.size());
}
TheSema.CurContext = CurContext;
m_IncrParser->commitCurrentTransaction();
}
else
m_IncrParser->Compile(Wrapper, CO);
// get the result
if (!V) {
if (RunFunction(WrapperName)) {
return Interpreter::kSuccess;
}
} else if (RunFunction(WrapperName, &V->value)) {
V->type = RetTy;
return Interpreter::kSuccess;
} else {
*V = Value();
}
return Interpreter::kFailure;
}
bool Interpreter::loadFile(const std::string& filename,
bool allowSharedLib /*=true*/) {
if (allowSharedLib) {
llvm::Module* module = m_IncrParser->getCodeGenerator()->GetModule();
if (module) {
if (tryLinker(filename, getOptions(), module))
return true;
if (filename.compare(0, 3, "lib") == 0) {
// starts with "lib", try without (the llvm::Linker forces
// a "lib" in front, which makes it liblib...
if (tryLinker(filename.substr(3, std::string::npos),
getOptions(), module))
return true;
}
}
}
std::string code;
code += "#include \"" + filename + "\"";
return declare(code) == Interpreter::kSuccess;
}
class ParserExt : public Parser {
public:
static TypeResult ParseTypeNameFwd(Parser* P, SourceRange *Range = 0,
Declarator::TheContext Context
= Declarator::TypeNameContext,
AccessSpecifier AS = AS_none,
Decl **OwnedType = 0) {
return ((ParserExt*)P)->ParseTypeName(Range, Context, AS, OwnedType);
}
static const Token& NextTokenFwd(Parser* P) {
return ((ParserExt*)P)->NextToken();
}
static bool SkipUntilFwd(Parser* P, tok::TokenKind T, bool StopAtSemi = true,
bool DontConsume = false,
bool StopAtCodeCompletion = false) {
return ((ParserExt*)P)->SkipUntil(T, StopAtSemi, DontConsume,
StopAtCodeCompletion);
}
static bool TryAnnotateCXXScopeTokenFwd(Parser* P,
bool EnteringContext = false) {
return ((ParserExt*)P)->TryAnnotateCXXScopeToken(EnteringContext);
}
static bool TryAnnotateTypeOrScopeTokenFwd(Parser* P,
bool EnteringContext = false,
bool NeedType = false) {
return ((ParserExt*)P)->TryAnnotateTypeOrScopeToken(EnteringContext);
}
static ParsedType getTypeAnnotationFwd(Token &Tok) {
return ParserExt::getTypeAnnotation(Tok);
}
static SourceLocation ConsumeTokenFwd(Parser* P) {
return ((ParserExt*)P)->ConsumeToken();
}
static bool ParseUnqualifiedIdFwd(Parser* P,
CXXScopeSpec &SS, bool EnteringContext,
bool AllowDestructorName,
bool AllowConstructorName,
ParsedType ObjectType,
SourceLocation& TemplateKWLoc,
UnqualifiedId &Result) {
return ((ParserExt*)P)->ParseUnqualifiedId(SS, EnteringContext,
AllowDestructorName,
AllowConstructorName,
ObjectType,
TemplateKWLoc,
Result);
}
static ExprResult ParseAssignmentExpressionFwd(Parser* P,
TypeCastState isTypeCast
= NotTypeCast) {
return ((ParserExt*)P)->ParseAssignmentExpression(isTypeCast);
}
static void EnterScopeFwd(Parser* P, unsigned ScopeFlags) {
((ParserExt*)P)->EnterScope(ScopeFlags);
}
static void ExitScopeFwd(Parser* P) {
((ParserExt*)P)->ExitScope();
}
};
Interpreter::NamedDeclResult Interpreter::LookupDecl(llvm::StringRef Decl,
const DeclContext* Within) {
if (!Within)
Within = getCI()->getASTContext().getTranslationUnitDecl();
return Interpreter::NamedDeclResult(Decl, this, Within);
}
void Interpreter::installLazyFunctionCreator(
void* (*fp)(const std::string&)) {
m_ExecutionContext->installLazyFunctionCreator(fp);
}
Value Interpreter::Evaluate(const char* expr, DeclContext* DC,
bool ValuePrinterReq) {
Sema& TheSema = getCI()->getSema();
if (!DC)
DC = TheSema.getASTContext().getTranslationUnitDecl();
// Set up the declaration context
DeclContext* CurContext;
CurContext = TheSema.CurContext;
TheSema.CurContext = DC;
Value Result;
if (TheSema.ExternalSource) {
DynamicIDHandler* DIDH =
static_cast<DynamicIDHandler*>(TheSema.ExternalSource);
DIDH->Callbacks->setEnabled();
(ValuePrinterReq) ? echo(expr, &Result) : evaluate(expr, &Result);
DIDH->Callbacks->setEnabled(false);
}
else
(ValuePrinterReq) ? echo(expr, &Result) : evaluate(expr, &Result);
TheSema.CurContext = CurContext;
return Result;
}
void Interpreter::setCallbacks(InterpreterCallbacks* C) {
Sema& S = getCI()->getSema();
assert(S.ExternalSource && "No ExternalSource set!");
static_cast<DynamicIDHandler*>(S.ExternalSource)->Callbacks = C;
}
void Interpreter::enableDynamicLookup(bool value /*=true*/) {
m_DynamicLookupEnabled = value;
Sema& S = getCI()->getSema();
if (isDynamicLookupEnabled()) {
assert(!S.ExternalSource && "Already set Sema ExternalSource");
// Load the dynamic lookup specifics.
declare("#include \"cling/Interpreter/DynamicLookupRuntimeUniverse.h\"");
S.ExternalSource = new DynamicIDHandler(&S);
}
else {
delete S.ExternalSource;
S.ExternalSource = 0;
}
}
void Interpreter::runStaticInitializersOnce() const {
// Forward to ExecutionContext; should not be called by
// anyone except for IncrementalParser.
llvm::Module* module = m_IncrParser->getCodeGenerator()->GetModule();
m_ExecutionContext->runStaticInitializersOnce(module);
}
int Interpreter::CXAAtExit(void (*func) (void*), void* arg, void* dso) {
// Register a CXAAtExit function
Decl* LastTLD
= m_IncrParser->getLastTransaction()->getLastDecl().getSingleDecl();
m_AtExitFuncs.push_back(CXAAtExitElement(func, arg, dso, LastTLD));
return 0; // happiness
}
bool Interpreter::addSymbol(const char* symbolName, void* symbolAddress){
// Forward to ExecutionContext;
if (!symbolName || !symbolAddress )
return false;
return m_ExecutionContext->addSymbol(symbolName, symbolAddress);
}
} // namespace cling

View File

@ -0,0 +1,107 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------
#include "cling/Interpreter/InvocationOptions.h"
#include "cling/Interpreter/ClingOptions.h"
#include "clang/Driver/Arg.h"
#include "clang/Driver/ArgList.h"
#include "clang/Driver/Option.h"
#include "clang/Driver/Options.h"
#include "clang/Driver/OptTable.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
using namespace clang::driver;
using namespace cling::driver::clingoptions;
namespace {
static const OptTable::Info ClingInfoTable[] = {
#define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \
HELPTEXT, METAVAR) \
{ NAME, HELPTEXT, METAVAR, Option::KIND##Class, FLAGS, PARAM, \
OPT_##GROUP, OPT_##ALIAS },
#include "cling/Interpreter/ClingOptions.inc"
#undef OPTION
};
class ClingOptTable : public OptTable {
public:
ClingOptTable()
: OptTable(ClingInfoTable,
sizeof(ClingInfoTable) / sizeof(ClingInfoTable[0])) {}
};
static OptTable* CreateClingOptTable() {
return new ClingOptTable();
}
static void ParseStartupOpts(cling::InvocationOptions& Opts,
InputArgList& Args /* , Diags */) {
Opts.NoLogo = Args.hasArg(OPT__nologo);
Opts.ShowVersion = Args.hasArg(OPT_version);
Opts.Verbose = Args.hasArg(OPT_v);
Opts.Help = Args.hasArg(OPT_help);
if (Args.hasArg(OPT__metastr, OPT__metastr_EQ)) {
Arg* MetaStringArg = Args.getLastArg(OPT__metastr, OPT__metastr_EQ);
Opts.MetaString = MetaStringArg->getValue(Args);
if (Opts.MetaString.length() == 0) {
llvm::errs() << "ERROR: meta string must be non-empty! Defaulting to '.'.\n";
Opts.MetaString = ".";
}
}
}
static void ParseLinkerOpts(cling::InvocationOptions& Opts,
InputArgList& Args /* , Diags */) {
Opts.LibsToLoad = Args.getAllArgValues(OPT_l);
std::vector<std::string> LibPaths = Args.getAllArgValues(OPT_L);
for (size_t i = 0; i < LibPaths.size(); ++i)
Opts.LibSearchPath.push_back(llvm::sys::Path(LibPaths[i]));
}
}
cling::InvocationOptions
cling::InvocationOptions::CreateFromArgs(int argc, const char* const argv[],
std::vector<unsigned>& leftoverArgs
/* , Diagnostic &Diags */) {
InvocationOptions ClingOpts;
llvm::OwningPtr<OptTable> Opts(CreateClingOptTable());
unsigned MissingArgIndex, MissingArgCount;
llvm::OwningPtr<InputArgList> Args(
Opts->ParseArgs(argv, argv + argc, MissingArgIndex, MissingArgCount));
//if (MissingArgCount)
// Diags.Report(diag::err_drv_missing_argument)
// << Args->getArgString(MissingArgIndex) << MissingArgCount;
// Forward unknown arguments.
for (ArgList::const_iterator it = Args->begin(),
ie = Args->end(); it != ie; ++it) {
if ((*it)->getOption().getKind() == Option::UnknownClass
||(*it)->getOption().getKind() == Option::InputClass) {
leftoverArgs.push_back((*it)->getIndex());
}
}
ParseStartupOpts(ClingOpts, *Args /* , Diags */);
ParseLinkerOpts(ClingOpts, *Args /* , Diags */);
return ClingOpts;
}
void cling::InvocationOptions::PrintHelp() {
llvm::OwningPtr<OptTable> Opts(CreateClingOptTable());
Opts->PrintHelp(llvm::outs(), "cling",
"cling: LLVM/clang C++ Interpreter: http://cern.ch/cling");
llvm::OwningPtr<OptTable> OptsC1(createDriverOptTable());
OptsC1->PrintHelp(llvm::outs(), "clang -cc1",
"LLVM 'Clang' Compiler: http://clang.llvm.org");
}

File diff suppressed because it is too large Load Diff

28
lib/Interpreter/Makefile Normal file
View File

@ -0,0 +1,28 @@
##===- cling/lib/Interpreter/Makefile-----------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
#
# This implements the a C++ interpreter backend.
#
##===----------------------------------------------------------------------===##
CLING_LEVEL = ../..
LIBRARYNAME := clingInterpreter
BUILD_ARCHIVE = 1
CXXFLAGS = -fno-rtti
# Inform the sources about their install location
CPPFLAGS += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_SRC_DIR)/../../../clang/include \
-I$(PROJ_SRC_DIR)/../../../clang/lib -I$(PROJ_OBJ_DIR)/../../include \
-I$(PROJ_OBJ_DIR)/../../../clang/include \
'-DCLANG_SYS_HEADERS="-I$(PROJ_SRC_DIR)../../../clang/lib/Headers"' \
'-DCLING_INSTDIR_INCL="$(DESTDIR)$(PROJ_includedir)"' \
'-DCLING_SRCDIR_INCL="$(LLVM_SRC_ROOT)/tools/cling/include"'
include $(CLING_LEVEL)/Makefile

View File

@ -0,0 +1,47 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vvasielv@cern.ch>
//------------------------------------------------------------------------------
#include "Transaction.h"
#include "clang/AST/DeclBase.h"
using namespace clang;
namespace cling {
Transaction::~Transaction() {
for (size_t i = 0; i < m_NestedTransactions.size(); ++i)
delete m_NestedTransactions[i];
}
void Transaction::appendUnique(DeclGroupRef DGR) {
for (const_iterator I = decls_begin(), E = decls_end(); I != E; ++I) {
if (DGR.isNull() || (*I).getAsOpaquePtr() == DGR.getAsOpaquePtr())
return;
}
m_DeclQueue.push_back(DGR);
}
void Transaction::dump() const {
int nestedT = 0;
for (const_iterator I = decls_begin(), E = decls_end(); I != E; ++I) {
if (I->isNull()) {
assert(hasNestedTransactions() && "DGR is null even if no nesting?");
// print the nested decl
llvm::outs()<<"+====================================================+\n";
llvm::outs()<<"| Nested Transaction" << nestedT << " |\n";
llvm::outs()<<"+====================================================+\n";
m_NestedTransactions[nestedT++]->dump();
llvm::outs()<<"+====================================================+\n";
llvm::outs()<<"| End Transaction" << nestedT << " |\n";
llvm::outs()<<"+====================================================+\n";
}
for (DeclGroupRef::const_iterator J = I->begin(), L = I->end(); J != L;++J)
(*J)->dump();
}
}
} // end namespace cling

View File

@ -0,0 +1,222 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vvasilev@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_TRANSACTION_H
#define CLING_TRANSACTION_H
#include "CompilationOptions.h"
#include "clang/AST/DeclGroup.h"
#include "llvm/ADT/SmallVector.h"
namespace clang {
class Decl;
}
namespace llvm {
class Module;
}
namespace cling {
///\brief Contains information about the consumed input at once.
///
/// A transaction could be:
/// - transformed - some declarations in the transaction could be modified,
/// deleted or some new declarations could be added.
/// - rolled back - the declarations of the transactions could be reverted so
/// that they weren't seen at all.
/// - committed - code could be produced for the contents of the transaction.
///
class Transaction {
private:
typedef llvm::SmallVector<clang::DeclGroupRef, 64> DeclQueue;
typedef llvm::SmallVector<Transaction*, 2> NestedTransactions;
///\brief All seen declarations. If we collect the declarations by walking
/// the clang::DeclContext we will miss the injected onces (eg. template
/// instantiations).
///
DeclQueue m_DeclQueue;
///\brief Shows whether the transaction was commited or not.
///
bool m_Completed;
///\brief The enclosing transaction if nested.
///
Transaction* m_Parent;
///\brief List of nested transactions if any.
///
NestedTransactions m_NestedTransactions;
unsigned m_State : 2;
unsigned m_IssuedDiags : 2;
///\brief Options controlling the transformers and code generator.
///
CompilationOptions m_Opts;
///\brief The llvm Module containing the information that we will revert
///
llvm::Module* m_Module;
public:
Transaction(const CompilationOptions& Opts, llvm::Module* M)
: m_Completed(false), m_Parent(0), m_State(kUnknown), m_IssuedDiags(kNone),
m_Opts(Opts), m_Module(M)
{ }
~Transaction();
enum State {
kUnknown,
kRolledBack,
kRolledBackWithErrors,
kCommitted
};
enum IssuedDiags {
kErrors,
kWarnings,
kNone
};
/// \{
/// \name Iteration
typedef DeclQueue::const_iterator const_iterator;
typedef DeclQueue::const_reverse_iterator const_reverse_iterator;
const_iterator decls_begin() const { return m_DeclQueue.begin(); }
const_iterator decls_end() const { return m_DeclQueue.end(); }
const_reverse_iterator rdecls_begin() const { return m_DeclQueue.rbegin(); }
const_reverse_iterator rdecls_end() const { return m_DeclQueue.rend(); }
typedef NestedTransactions::const_iterator const_nested_iterator;
typedef NestedTransactions::const_reverse_iterator const_reverse_nested_iterator;
const_nested_iterator nested_decls_begin() const {
return m_NestedTransactions.begin();
}
const_nested_iterator nested_decls_end() const {
return m_NestedTransactions.end();
}
const_reverse_nested_iterator rnested_decls_begin() const {
return m_NestedTransactions.rbegin();
}
const_reverse_nested_iterator rnested_decls_end() const {
return m_NestedTransactions.rend();
}
/// \}
State getState() const { return static_cast<State>(m_State); }
void setState(State val) { m_State = val; }
IssuedDiags getIssuedDiags() const {
return static_cast<IssuedDiags>(m_IssuedDiags);
}
void setIssuedDiags(IssuedDiags val) { m_IssuedDiags = val; }
const CompilationOptions& getCompilationOpts() const { return m_Opts; }
///\brief Returns the first declaration of the transaction.
///
clang::DeclGroupRef getFirstDecl() const {
if (!empty())
return m_DeclQueue.front();
return clang::DeclGroupRef();
}
///\brief Returns the last declaration of a completed transaction.
///
clang::DeclGroupRef getLastDecl() const {
if (!empty() && isCompleted())
return m_DeclQueue.back();
return clang::DeclGroupRef();
}
///\brief Returns the current last transaction. Useful when the transaction
/// in still incomplete.
///
clang::DeclGroupRef getCurrentLastDecl() const {
if (!empty())
return m_DeclQueue.back();
return clang::DeclGroupRef();
}
///\ brief Returns whether the transaction is complete or not.
///
/// We assume that when the last declaration of the transaction is set,
/// the transaction is completed.
///
bool isCompleted() const { return m_Completed; }
void setCompleted(bool val = true) { m_Completed = val; }
///\brief If the transaction was nested into another transaction returns
/// the parent.
///
Transaction* getParent() { return m_Parent; }
///\brief If the transaction was nested into another transaction returns
/// the parent.
///
const Transaction* getParent() const { return m_Parent; }
///\brief Sets the nesting transaction of a nested transaction.
///
///\param[in] parent - The nesting transaction.
///
void setParent(Transaction* parent) { m_Parent = parent; }
bool isNestedTransaction() { return m_Parent; }
bool hasNestedTransactions() const { return !m_NestedTransactions.empty(); }
///\brief Adds nested transaction to the transaction.
///
///\param[in] nested - The transaction to be nested.
///
void addNestedTransaction(Transaction* nested) {
nested->setParent(nested);
// Leave a marker in the parent transaction, where the nested transaction
// started. Using empty DeclGroupRef is save because append() filters
// out possible empty DeclGroupRefs.
m_DeclQueue.push_back(clang::DeclGroupRef());
m_NestedTransactions.push_back(nested);
}
///\brief Returns the declaration count.
///
size_t size() const { return m_DeclQueue.size(); }
///\brief Returns whether there are declarations in the transaction.
///
bool empty() const { return m_DeclQueue.empty(); }
///\brief Appends a declaration group to the transaction if doesn't exist.
///
void appendUnique(clang::DeclGroupRef DGR);
///\brief Clears all declarations in the transaction.
///
void clear() {
m_DeclQueue.clear();
m_NestedTransactions.clear();
}
llvm::Module* getModule() const { return m_Module; }
///\brief Prints out all the declarations in the transaction.
///
void dump() const;
};
} // end namespace cling
#endif // CLING_TRANSACTION_H

View File

@ -0,0 +1,72 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id: Transaction.h 45164 2012-07-23 15:22:40Z vvassilev $
// author: Vassil Vassilev <vvasilev@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_TRANSACTION_TRANSFORMER_H
#define CLING_TRANSACTION_TRANSFORMER_H
namespace clang {
class Sema;
}
namespace cling {
class Transaction;
///\brief Inherit from that class if you want to change/analyse declarations
/// from the last input before code is generated.
///
class TransactionTransformer {
protected:
clang::Sema* m_Sema;
///\brief Transaction being transformed.
Transaction* m_Transaction;
public:
///\brief Initializes a new transaction transformer.
///
///\param[in] S - The semantic analysis object.
///
TransactionTransformer(clang::Sema* S): m_Sema(S), m_Transaction(0) {}
virtual ~TransactionTransformer();
///\brief Retrieves a pointer to the semantic analysis object used for this
/// transaction transform.
///
clang::Sema* getSemaPtr() const { return m_Sema; }
///\brief Retreives the transaction being currently transformed.
///
Transaction* getTransaction() { return m_Transaction; }
///\brief Retreives the transaction being currently transformed.
///
const Transaction* getTransaction() const { return m_Transaction; }
void setTransaction(Transaction* T) { m_Transaction = T; }
///\brief The method that does the transformation of a transaction into
/// another. If forwards to the protected virtual Transform method, which
/// does the actual transformation.
///
///\param[in] T - The transaction to be transformed.
///\returns The transformed transaction.
///
Transaction* TransformTransaction(Transaction& T) {
m_Transaction = &T;
Transform();
return m_Transaction;
}
protected:
///\brief Transforms the current transaction.
///
/// Subclasses may override it in order to provide the needed behavior.
///
virtual void Transform() = 0;
};
} // end namespace cling
#endif // CLING_TRANSACTION_TRANSFORMER_H

View File

@ -0,0 +1,173 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#include "cling/Interpreter/ValuePrinter.h"
#include "cling/Interpreter/CValuePrinter.h"
#include "cling/Interpreter/ValuePrinterInfo.h"
#include "cling/Interpreter/Value.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Type.h"
#include "llvm/Support/raw_ostream.h"
#include <string>
// Implements the CValuePrinter interface.
extern "C" void cling_PrintValue(void* /*clang::Expr**/ E,
void* /*clang::ASTContext**/ C,
const void* value) {
clang::Expr* Exp = (clang::Expr*)E;
clang::ASTContext* Context = (clang::ASTContext*)C;
cling::ValuePrinterInfo VPI(Exp, Context);
cling::printValue(llvm::outs(), value, value, VPI);
cling::flushOStream(llvm::outs());
}
static void StreamChar(llvm::raw_ostream& o, const char v) {
o << '"' << v << "\"\n";
}
static void StreamCharPtr(llvm::raw_ostream& o, const char* const v) {
o << '"';
const char* p = v;
for (;*p && p - v < 128; ++p) {
o << *p;
}
if (*p) o << "\"...\n";
else o << "\"\n";
}
static void StreamRef(llvm::raw_ostream& o, const void* v) {
o <<"&" << v << "\n";
}
static void StreamPtr(llvm::raw_ostream& o, const void* v) {
o << v << "\n";
}
static void StreamObj(llvm::raw_ostream& o, const void* v,
const cling::ValuePrinterInfo& VPI) {
const clang::Type* Ty = VPI.getExpr()->getType().getTypePtr();
if (clang::CXXRecordDecl* CXXRD = Ty->getAsCXXRecordDecl())
if (CXXRD->getQualifiedNameAsString().compare("cling::Value") == 0) {
cling::Value* V = (cling::Value*)v;
if (V->isValid()) {
o << "boxes [";
const clang::ASTContext& C = *VPI.getASTContext();
o <<
"(" <<
V->type.getAsString(C.getPrintingPolicy()) <<
") ";
clang::QualType valType = V->type.getDesugaredType(C);
if (valType->isPointerType())
o << V->value.PointerVal;
else if (valType->isFloatingType())
o << V->value.DoubleVal;
else if (valType->isIntegerType())
o << V->value.IntVal.getSExtValue();
else if (valType->isBooleanType())
o << V->value.IntVal.getBoolValue();
o << "]\n";
return;
} else
o << "<<<invalid>>> ";
}
// TODO: Print the object members.
o << "@" << v << "\n";
}
static void StreamValue(llvm::raw_ostream& o, const void* const p,
const cling::ValuePrinterInfo& VPI) {
clang::QualType Ty = VPI.getExpr()->getType();
const clang::ASTContext& C = *VPI.getASTContext();
Ty = Ty.getDesugaredType(C);
if (const clang::BuiltinType *BT
= llvm::dyn_cast<clang::BuiltinType>(Ty.getCanonicalType())) {
switch (BT->getKind()) {
case clang::BuiltinType::Bool:
if (*(bool*)p) o << "true\n";
else o << "false\n"; break;
case clang::BuiltinType::Char_U:
case clang::BuiltinType::UChar:
case clang::BuiltinType::Char_S:
case clang::BuiltinType::SChar: StreamChar(o, *(char*)p); break;
case clang::BuiltinType::Short: o << *(short*)p << "\n"; break;
case clang::BuiltinType::UShort: o << *(unsigned short*)p << "\n"; break;
case clang::BuiltinType::Int: o << *(int*)p << "\n"; break;
case clang::BuiltinType::UInt: o << *(unsigned int*)p << "\n"; break;
case clang::BuiltinType::Long: o << *(long*)p << "\n"; break;
case clang::BuiltinType::ULong: o << *(unsigned long*)p << "\n"; break;
case clang::BuiltinType::LongLong: o << *(long long*)p << "\n"; break;
case clang::BuiltinType::ULongLong: o << *(unsigned long long*)p << "\n";
break;
case clang::BuiltinType::Float: o << *(float*)p << "\n"; break;
case clang::BuiltinType::Double: o << *(double*)p << "\n"; break;
default:
StreamObj(o, p, VPI);
}
}
else if (Ty.getAsString().compare("class std::basic_string<char>") == 0) {
StreamObj(o, p, VPI);
o <<"c_str: ";
StreamCharPtr(o, ((const char*) (*(const std::string*)p).c_str()));
}
else if (Ty->isEnumeralType()) {
StreamObj(o, p, VPI);
clang::EnumDecl* ED = Ty->getAs<clang::EnumType>()->getDecl();
uint64_t value = *(uint64_t*)p;
bool IsFirst = true;
llvm::APSInt ValAsAPSInt = C.MakeIntValue(value, Ty);
for (clang::EnumDecl::enumerator_iterator I = ED->enumerator_begin(),
E = ED->enumerator_end(); I != E; ++I) {
if (I->getInitVal() == ValAsAPSInt) {
if (!IsFirst) {
o << " ? ";
}
o << "(" << I->getQualifiedNameAsString() << ")";
IsFirst = false;
}
}
o << " : (int) " << ValAsAPSInt.toString(/*Radix = */10) << "\n";
}
else if (Ty->isReferenceType())
StreamRef(o, p);
else if (Ty->isPointerType()) {
clang::QualType PointeeTy = Ty->getPointeeType();
if (PointeeTy->isCharType())
StreamCharPtr(o, (const char*)p);
else
StreamPtr(o, p);
}
else
StreamObj(o, p, VPI);
}
namespace cling {
void printValueDefault(llvm::raw_ostream& o, const void* const p,
const ValuePrinterInfo& VPI) {
const clang::Expr* E = VPI.getExpr();
o << "(";
o << E->getType().getAsString();
if (E->isRValue()) // show the user that the var cannot be changed
o << " const";
o << ") ";
StreamValue(o, p, VPI);
}
void flushOStream(llvm::raw_ostream& o) {
o.flush();
}
} // end namespace cling

View File

@ -0,0 +1,42 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#include "cling/Interpreter/ValuePrinterInfo.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Type.h"
using namespace clang;
namespace cling {
ValuePrinterInfo::ValuePrinterInfo(Expr* E, ASTContext* Ctx)
: m_Expr(E), m_Context(Ctx), m_Flags(0) {
assert(E && "Expression cannot be null!");
assert(Ctx && "ASTContext cannot be null!");
// 1. Get the flags
const QualType QT = m_Expr->getType();
if (E->isRValue() || QT.isLocalConstQualified() || QT.isConstant(*Ctx)){
m_Flags |= VPI_Const;
}
if (QT->isPointerType()) {
// treat arrary-to-pointer decay as array:
QualType PQT = QT->getPointeeType();
const Type* PTT = PQT.getTypePtr();
if (!PTT || !PTT->isArrayType()) {
m_Flags |= VPI_Ptr;
if (const RecordType* RT = dyn_cast<RecordType>(QT.getTypePtr()))
if (RecordDecl* RD = RT->getDecl()) {
CXXRecordDecl* CRD = dyn_cast<CXXRecordDecl>(RD);
if (CRD && CRD->isPolymorphic())
m_Flags |= VPI_Polymorphic;
}
}
}
}
} // end namespace cling

View File

@ -0,0 +1,252 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#include "ValuePrinterSynthesizer.h"
#include "Transaction.h"
#include "cling/Interpreter/Interpreter.h"
#include "cling/Utils/AST.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclGroup.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/Sema.h"
using namespace clang;
namespace cling {
ValuePrinterSynthesizer::ValuePrinterSynthesizer(Interpreter* Interp,
clang::Sema* S)
: TransactionTransformer(S), m_Interpreter(Interp),
m_Context(&S->getASTContext())
{ }
// pin the vtable here.
ValuePrinterSynthesizer::~ValuePrinterSynthesizer()
{ }
void ValuePrinterSynthesizer::Transform() {
if (!getTransaction()->getCompilationOpts().ValuePrinting)
return;
for (Transaction::const_iterator I = getTransaction()->decls_begin(),
E = getTransaction()->decls_end(); I != E; ++I)
if(!tryAttachVP(*I))
return setTransaction(0); // On error set the to NULL.
}
bool ValuePrinterSynthesizer::tryAttachVP(DeclGroupRef DGR) {
for (DeclGroupRef::iterator I = DGR.begin(), E = DGR.end(); I != E; ++I)
if (FunctionDecl* FD = dyn_cast<FunctionDecl>(*I)) {
if (FD->getNameAsString().find("__cling_Un1Qu3"))
return true;
if (CompoundStmt* CS = dyn_cast<CompoundStmt>(FD->getBody())) {
for (CompoundStmt::body_iterator
J = CS->body_begin(), E = CS->body_end(); J != E; ++J) {
if (J+1 == E || !isa<NullStmt>(*(J+1))) {
Expr* To = 0;
ReturnStmt* RS = dyn_cast<ReturnStmt>(*J);
if (RS)
To = RS->getRetValue();
else
To = dyn_cast<Expr>(*J);
if (To) {
Expr* Result = 0;
if (m_Sema->getLangOpts().CPlusPlus)
Result = SynthesizeCppVP(To);
else
Result = SynthesizeVP(To);
if (Result) {
if (RS)
RS->setRetValue(Result);
else
*J = Result;
}
}
}
}
// Clear the artificial NullStmt-s
if (!ClearNullStmts(CS)) {
// if no body remove the wrapper
DeclContext* DC = FD->getDeclContext();
Scope* S = m_Sema->getScopeForContext(DC);
if (S)
S->RemoveDecl(FD);
DC->removeDecl(FD);
}
}
}
return true;
}
// We need to artificially create:
// cling::valuePrinterInternal::PrintValue((void*) raw_ostream,
// (ASTContext)Ctx, (Expr*)E, &i);
Expr* ValuePrinterSynthesizer::SynthesizeCppVP(Expr* E) {
QualType QT = E->getType();
// For now we skip void and function pointer types.
if (!QT.isNull() && (QT->isVoidType() || QT->isFunctionType()))
return 0;
// 1. Call gCling->getValuePrinterStream()
// 1.1. Find gCling
SourceLocation NoSLoc = SourceLocation();
NamespaceDecl* NSD = utils::Lookup::Namespace(m_Sema, "cling");
NSD = utils::Lookup::Namespace(m_Sema, "valuePrinterInternal", NSD);
DeclarationName PVName = &m_Context->Idents.get("PrintValue");
LookupResult R(*m_Sema, PVName, NoSLoc, Sema::LookupOrdinaryName,
Sema::ForRedeclaration);
m_Sema->LookupQualifiedName(R, NSD);
assert(!R.empty() && "Cannot find PrintValue(...)");
CXXScopeSpec CSS;
Expr* UnresolvedLookup
= m_Sema->BuildDeclarationNameExpr(CSS, R, /*ADL*/ false).take();
// 2.4. Prepare the params
// 2.4.1 Lookup the llvm::raw_ostream
CXXRecordDecl* RawOStreamRD
= dyn_cast<CXXRecordDecl>(utils::Lookup::Named(m_Sema, "raw_ostream",
utils::Lookup::Namespace(m_Sema,
"llvm")));
assert(RawOStreamRD && "Declaration of the expr not found!");
QualType RawOStreamRDTy = m_Context->getTypeDeclType(RawOStreamRD);
// 2.4.2 Lookup the expr type
CXXRecordDecl* ExprRD
= dyn_cast<CXXRecordDecl>(utils::Lookup::Named(m_Sema, "Expr",
utils::Lookup::Namespace(m_Sema,
"clang")));
assert(ExprRD && "Declaration of the expr not found!");
QualType ExprRDTy = m_Context->getTypeDeclType(ExprRD);
// 2.4.3 Lookup ASTContext type
CXXRecordDecl* ASTContextRD
= dyn_cast<CXXRecordDecl>(utils::Lookup::Named(m_Sema, "ASTContext",
utils::Lookup::Namespace(m_Sema,
"clang")));
assert(ASTContextRD && "Declaration of the expr not found!");
QualType ASTContextRDTy = m_Context->getTypeDeclType(ASTContextRD);
Expr* RawOStreamTy
= utils::Synthesize::CStyleCastPtrExpr(m_Sema, RawOStreamRDTy,
(uint64_t)&m_Interpreter->getValuePrinterStream()
);
Expr* ExprTy = utils::Synthesize::CStyleCastPtrExpr(m_Sema, ExprRDTy,
(uint64_t)E);
Expr* ASTContextTy
= utils::Synthesize::CStyleCastPtrExpr(m_Sema, ASTContextRDTy,
(uint64_t)m_Context);
// E might contain temporaries. This means that the topmost expr is
// ExprWithCleanups. This contains the information about the temporaries and
// signals when they should be destroyed.
// Here we replace E with call to value printer and we must extend the life
// time of those temporaries to the end of the new CallExpr.
bool NeedsCleanup = false;
if (ExprWithCleanups* EWC = dyn_cast<ExprWithCleanups>(E)) {
E = EWC->getSubExpr();
NeedsCleanup = true;
}
llvm::SmallVector<Expr*, 4> CallArgs;
CallArgs.push_back(RawOStreamTy);
CallArgs.push_back(ExprTy);
CallArgs.push_back(ASTContextTy);
CallArgs.push_back(E);
Scope* S = m_Sema->getScopeForContext(m_Sema->CurContext);
Expr* Result = m_Sema->ActOnCallExpr(S, UnresolvedLookup, NoSLoc,
CallArgs, NoSLoc).take();
Result = m_Sema->ActOnFinishFullExpr(Result).take();
if (NeedsCleanup && !isa<ExprWithCleanups>(Result)) {
llvm::ArrayRef<ExprWithCleanups::CleanupObject> Cleanups;
ExprWithCleanups* EWC
= ExprWithCleanups::Create(*m_Context, Result, Cleanups);
Result = EWC;
}
assert(Result && "Cannot create value printer!");
return Result;
}
// We need to artificially create:
// cling_PrintValue(void* (ASTContext)C, void* (Expr)E, const void* (&i)
Expr* ValuePrinterSynthesizer::SynthesizeVP(Expr* E) {
QualType QT = E->getType();
// For now we skip void and function pointer types.
if (!QT.isNull() && (QT->isVoidType() || QT->isFunctionType()))
return 0;
// Find cling_PrintValue
SourceLocation NoSLoc = SourceLocation();
DeclarationName PVName = &m_Context->Idents.get("cling_PrintValue");
LookupResult R(*m_Sema, PVName, NoSLoc, Sema::LookupOrdinaryName,
Sema::ForRedeclaration);
Scope* S = m_Sema->getScopeForContext(m_Sema->CurContext);
m_Sema->LookupName(R, S);
assert(!R.empty() && "Cannot find PrintValue(...)");
CXXScopeSpec CSS;
Expr* UnresolvedLookup
= m_Sema->BuildDeclarationNameExpr(CSS, R, /*ADL*/ false).take();
Expr* VoidEArg = utils::Synthesize::CStyleCastPtrExpr(m_Sema,
m_Context->VoidPtrTy,
(uint64_t)E);
Expr* VoidCArg = utils::Synthesize::CStyleCastPtrExpr(m_Sema,
m_Context->VoidPtrTy,
(uint64_t)m_Context);
if (!QT->isPointerType()) {
while(ImplicitCastExpr* ICE = dyn_cast<ImplicitCastExpr>(E))
E = ICE->getSubExpr();
E = m_Sema->BuildUnaryOp(S, NoSLoc, UO_AddrOf, E).take();
}
llvm::SmallVector<Expr*, 4> CallArgs;
CallArgs.push_back(VoidEArg);
CallArgs.push_back(VoidCArg);
CallArgs.push_back(E);
Expr* Result = m_Sema->ActOnCallExpr(S, UnresolvedLookup, NoSLoc,
CallArgs, NoSLoc).take();
assert(Result && "Cannot create value printer!");
return Result;
}
unsigned ValuePrinterSynthesizer::ClearNullStmts(CompoundStmt* CS) {
llvm::SmallVector<Stmt*, 8> FBody;
for (StmtRange range = CS->children(); range; ++range)
if (!isa<NullStmt>(*range))
FBody.push_back(*range);
CS->setStmts(*m_Context, FBody.data(), FBody.size());
return FBody.size();
}
} // namespace cling

View File

@ -0,0 +1,46 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_VALUE_PRINTER_SYNTHESIZER_H
#define CLING_VALUE_PRINTER_SYNTHESIZER_H
#include "TransactionTransformer.h"
namespace clang {
class ASTContext;
class CompoundStmt;
class DeclGroupRef;
class Expr;
class Sema;
}
namespace cling {
class Interpreter;
class ValuePrinterSynthesizer : public TransactionTransformer {
private:
Interpreter* m_Interpreter;
///\brief Needed for the AST transformations, owned by Sema
clang::ASTContext* m_Context;
public:
ValuePrinterSynthesizer(Interpreter* Interp, clang::Sema* S);
virtual ~ValuePrinterSynthesizer();
virtual void Transform();
private:
bool tryAttachVP(clang::DeclGroupRef DGR);
clang::Expr* SynthesizeCppVP(clang::Expr* E);
clang::Expr* SynthesizeVP(clang::Expr* E);
unsigned ClearNullStmts(clang::CompoundStmt* CS);
};
} // namespace cling
#endif // CLING_DECL_EXTRACTOR_H

14
lib/Makefile Normal file
View File

@ -0,0 +1,14 @@
##===- lib/Makefile ----------------------------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
LEVEL = ../../..
PARALLEL_DIRS = Interpreter MetaProcessor UserInterface Utils
include $(LEVEL)/Makefile.common

View File

@ -0,0 +1,7 @@
set(LLVM_USED_LIBS
clangBasic
)
add_cling_library(clingMetaProcessor
MetaProcessor.cpp
InputValidator.cpp)

View File

@ -0,0 +1,56 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------
#include "InputValidator.h"
#include "clang/Lex/Preprocessor.h"
using namespace clang;
namespace cling {
InputValidator::ValidationResult
InputValidator::validate(llvm::StringRef line, LangOptions& LO) {
if (!m_Input.empty())
m_Input.append("\n");
else
m_Input = "";
m_Input.append(line);
llvm::MemoryBuffer* MB = llvm::MemoryBuffer::getMemBuffer(line);
Lexer RawLexer(SourceLocation(), LO, MB->getBufferStart(),
MB->getBufferStart(), MB->getBufferEnd());
Token Tok;
do {
RawLexer.LexFromRawLexer(Tok);
int kind = (int)Tok.getKind();
if (kind >= (int)tok::l_square
&& kind <= (int)tok::r_brace) {
kind -= (int)tok::l_square;
if (kind % 2) {
// closing the right one?
if (m_ParenStack.empty()) return kMismatch;
int prev = m_ParenStack.top();
if (prev != kind - 1) return kMismatch;
m_ParenStack.pop();
} else {
m_ParenStack.push(kind);
}
}
}
while (Tok.isNot(tok::eof));
if (!m_ParenStack.empty())
return kIncomplete;
return kComplete;
}
void InputValidator::reset() {
m_Input = "";
while (!m_ParenStack.empty())
m_ParenStack.pop();
}
} // end namespace cling

View File

@ -0,0 +1,69 @@
//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------
#ifndef CLING_INPUT_VALIDATOR_H
#define CLING_INPUT_VALIDATOR_H
#include "llvm/ADT/StringRef.h"
#include <stack>
namespace clang {
class LangOptions;
}
namespace cling {
///\brief Provides storage for the input and tracks down whether the (, [, {
/// are balanced.
///
class InputValidator {
private:
///\brief The input being collected.
///
std::string m_Input;
///\brief Stack used for checking the brace balance.
///
std::stack<int> m_ParenStack;
public:
InputValidator() {}
~InputValidator() {}
///\brief Brace balance validation could encounter.
///
enum ValidationResult {
kIncomplete, ///< There is dangling brace.
kComplete, ///< All braces are in balance.
kMismatch ///< Closing brace doesn't match to opening. Eg: void f(};
};
///\brief Checks whether the input contains balanced number of braces
///
///\param[in] line - Input line to validate.
///\param[in] LO - Langluage options to validate against.
///\returns Information about the outcome of the validation.
///
ValidationResult validate(llvm::StringRef line, clang::LangOptions& LO);
///\returns Reference to the collected input.
///
std::string& getInput() {
return m_Input;
}
///\brief Retrieves the number of spaces that the next input line should be
/// indented.
///
int getExpectedIndent() { return m_ParenStack.size(); }
///\brief Resets the collected input and its corresponding brace stack.
///
void reset();
};
}
#endif // CLING_INPUT_VALIDATOR_H

View File

@ -0,0 +1,22 @@
##===- cling/lib/MetaProcessor/Makefile---------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
#
# This implements the a C++ interpreter user frontend.
#
##===----------------------------------------------------------------------===##
CLING_LEVEL = ../..
LIBRARYNAME := clingMetaProcessor
BUILD_ARCHIVE = 1
CXXFLAGS = -fno-rtti
CPPFLAGS += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_SRC_DIR)/../../../clang/include \
-I$(PROJ_SRC_DIR)/../../../clang/lib -I$(PROJ_OBJ_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../../clang/include
include $(CLING_LEVEL)/Makefile

View File

@ -0,0 +1,365 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------
#include "cling/MetaProcessor/MetaProcessor.h"
#include "InputValidator.h"
#include "cling/Interpreter/Interpreter.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/Support/Path.h"
using namespace clang;
namespace cling {
MetaProcessor::MetaProcessor(Interpreter& interp) : m_Interp(interp) {
m_InputValidator.reset(new InputValidator());
}
MetaProcessor::~MetaProcessor() {}
int MetaProcessor::process(const char* input_text, Value* result /*=0*/) {
if (!input_text) { // null pointer, nothing to do.
return 0;
}
if (!input_text[0]) { // empty string, nothing to do.
return m_InputValidator->getExpectedIndent();
}
std::string input_line(input_text);
if (input_line == "\n") { // just a blank line, nothing to do.
return 0;
}
// Check for and handle meta commands.
bool was_meta = false;
std::string& metaString = m_Interp.getOptions().MetaString;
std::string::size_type lenMetaString = metaString.length();
if (input_line.length() > lenMetaString
&& !input_line.compare(0, lenMetaString, metaString)) {
was_meta = ProcessMeta(input_line.c_str() + lenMetaString, result);
}
if (was_meta) {
return 0;
}
// Check if the current statement is now complete. If not, return to
// prompt for more.
if (m_InputValidator->validate(input_line, m_Interp.getCI()->getLangOpts())
== InputValidator::kIncomplete) {
return m_InputValidator->getExpectedIndent();
}
// We have a complete statement, compile and execute it.
std::string input = m_InputValidator->getInput();
m_InputValidator->reset();
if (m_Options.RawInput)
m_Interp.declare(input);
else
m_Interp.process(input, result);
return 0;
}
MetaProcessorOpts& MetaProcessor::getMetaProcessorOpts() {
// Take interpreter's state
m_Options.PrintingAST = m_Interp.isPrintingAST();
return m_Options;
}
bool MetaProcessor::ProcessMeta(const std::string& input_line, Value* result){
llvm::MemoryBuffer* MB = llvm::MemoryBuffer::getMemBuffer(input_line);
LangOptions LO;
LO.C99 = 1;
// necessary for the @ symbol
LO.ObjC1 = 1;
Lexer RawLexer(SourceLocation(), LO, MB->getBufferStart(),
MB->getBufferStart(), MB->getBufferEnd());
Token Tok;
// Read the command
RawLexer.LexFromRawLexer(Tok);
if (!Tok.isAnyIdentifier() && Tok.isNot(tok::at))
return false;
const std::string Command = GetRawTokenName(Tok);
std::string Param;
// .q //Quits
if (Command == "q") {
m_Options.Quitting = true;
return true;
}
// .L <filename> // Load code fragment.
else if (Command == "L") {
// TODO: Additional checks on params
bool success
= m_Interp.loadFile(SanitizeArg(ReadToEndOfBuffer(RawLexer, MB)));
if (!success) {
llvm::errs() << "Load file failed.\n";
}
return true;
}
// .(x|X) <filename> // Execute function from file, function name is
// // filename without extension.
else if ((Command == "x") || (Command == "X")) {
// TODO: Additional checks on params
llvm::sys::Path path(SanitizeArg(ReadToEndOfBuffer(RawLexer, MB)));
if (!path.isValid())
return false;
bool success = executeFile(path.c_str(), result);
if (!success) {
llvm::errs()<< "Execute file failed.\n";
}
return true;
}
// .printAST [0|1] // Toggle the printing of the AST or if 1 or 0 is given
// // enable or disable it.
else if (Command == "printAST") {
// Check for params
RawLexer.LexFromRawLexer(Tok);
if (Tok.isNot(tok::numeric_constant) && Tok.isNot(tok::eof))
return false;
if (Tok.is(tok::eof)) {
// toggle:
bool print = !m_Interp.isPrintingAST();
m_Interp.enablePrintAST(print);
llvm::outs()<< (print?"P":"Not p") << "rinting AST\n";
} else {
Param = GetRawTokenName(Tok);
if (Param == "0")
m_Interp.enablePrintAST(false);
else
m_Interp.enablePrintAST(true);
}
m_Options.PrintingAST = m_Interp.isPrintingAST();
return true;
}
// .rawInput [0|1] // Toggle the raw input or if 1 or 0 is given enable
// // or disable it.
else if (Command == "rawInput") {
// Check for params
RawLexer.LexFromRawLexer(Tok);
if (Tok.isNot(tok::numeric_constant) && Tok.isNot(tok::eof))
return false;
if (Tok.is(tok::eof)) {
// toggle:
m_Options.RawInput = !m_Options.RawInput;
llvm::outs() << (m_Options.RawInput?"U":"Not u") << "sing raw input\n";
} else {
Param = GetRawTokenName(Tok);
if (Param == "0")
m_Options.RawInput = false;
else
m_Options.RawInput = true;
}
return true;
}
//
// .U <filename>
//
// Unload code fragment.
//
if (Command == "U") {
// llvm::sys::Path path(param);
// if (path.isDynamicLibrary()) {
// std::cerr << "[i] Failure: cannot unload shared libraries yet!"
// << std::endl;
// }
m_Interp.unload();
return true;
}
//
// Unrecognized command.
//
//fprintf(stderr, "Unrecognized command.\n");
else if (Command == "I") {
// Check for params
llvm::sys::Path path(SanitizeArg(ReadToEndOfBuffer(RawLexer, MB)));
if (path.isEmpty())
m_Interp.DumpIncludePath();
else {
// TODO: Additional checks on params
if (path.isValid())
m_Interp.AddIncludePath(path.c_str());
else
return false;
}
return true;
}
// Cancel the multiline input that has been requested
else if (Command == "@") {
m_InputValidator->reset();
return true;
}
// Enable/Disable DynamicExprTransformer
else if (Command == "dynamicExtensions") {
// Check for params
RawLexer.LexFromRawLexer(Tok);
if (Tok.isNot(tok::numeric_constant) && Tok.isNot(tok::eof))
return false;
if (Tok.is(tok::eof)) {
// toggle:
bool dynlookup = !m_Interp.isDynamicLookupEnabled();
m_Interp.enableDynamicLookup(dynlookup);
llvm::outs() << (dynlookup?"U":"Not u") <<"sing dynamic extensions\n";
} else {
Param = GetRawTokenName(Tok);
if (Param == "0")
m_Interp.enableDynamicLookup(false);
else
m_Interp.enableDynamicLookup(true);
}
return true;
}
// Print Help
else if (Command == "help") {
PrintCommandHelp();
return true;
}
// Print the loaded files
else if (Command == "file") {
PrintFileStats();
return true;
}
return false;
}
std::string MetaProcessor::GetRawTokenName(const Token& Tok) {
assert(!Tok.needsCleaning() && "Not implemented yet");
switch (Tok.getKind()) {
default:
assert("Unknown token");
return "";
case tok::at:
return "@";
case tok::l_paren:
return "(";
case tok::r_paren:
return ")";
case tok::period:
return ".";
case tok::slash:
return "/";
case tok::numeric_constant:
return StringRef(Tok.getLiteralData(), Tok.getLength()).str();
case tok::raw_identifier:
return StringRef(Tok.getRawIdentifierData(), Tok.getLength()).str();
}
}
llvm::StringRef MetaProcessor::ReadToEndOfBuffer(Lexer& RawLexer,
llvm::MemoryBuffer* MB) {
const char* CurPtr = RawLexer.getBufferLocation();
if (CurPtr == MB->getBufferEnd()) {
// Already at end of the buffer, return just the zero byte at the end.
return StringRef(CurPtr, 0);
}
Token TmpTok;
RawLexer.getAndAdvanceChar(CurPtr, TmpTok);
return StringRef(CurPtr, MB->getBufferSize()-(CurPtr-MB->getBufferStart()));
}
llvm::StringRef MetaProcessor::SanitizeArg(const std::string& Str) {
if(Str.empty())
return Str;
size_t begins = Str.find_first_not_of(" \t\n");
size_t ends = Str.find_last_not_of(" \t\n") + 1;
if (begins == std::string::npos)
ends = begins + 1;
return llvm::StringRef(Str.c_str() + begins, ends - begins);
}
void MetaProcessor::PrintCommandHelp() {
std::string& metaString = m_Interp.getOptions().MetaString;
llvm::outs() << "Cling meta commands usage\n";
llvm::outs() << "Syntax: .Command [arg0 arg1 ... argN]\n";
llvm::outs() << "\n";
llvm::outs() << metaString << "q\t\t\t\t- Exit the program\n";
llvm::outs() << metaString << "L <filename>\t\t\t - Load file or library\n";
llvm::outs() << metaString << "(x|X) <filename>[args]\t\t- Same as .L and runs a ";
llvm::outs() << "function with signature ";
llvm::outs() << "\t\t\t\tret_type filename(args)\n";
llvm::outs() << metaString << "I [path]\t\t\t- Shows the include path. If a path is ";
llvm::outs() << "given - \n\t\t\t\tadds the path to the include paths\n";
llvm::outs() << metaString << "@ \t\t\t\t- Cancels and ignores the multiline input\n";
llvm::outs() << metaString << "rawInput [0|1]\t\t\t- Toggle wrapping and printing ";
llvm::outs() << "the execution\n\t\t\t\tresults of the input\n";
llvm::outs() << metaString << "dynamicExtensions [0|1]\t- Toggles the use of the ";
llvm::outs() << "dynamic scopes and the \t\t\t\tlate binding\n";
llvm::outs() << metaString << "printAST [0|1]\t\t\t- Toggles the printing of input's ";
llvm::outs() << "corresponding \t\t\t\tAST nodes\n";
llvm::outs() << metaString << "help\t\t\t\t- Shows this information\n";
}
void MetaProcessor::PrintFileStats() {
const SourceManager& SM = m_Interp.getCI()->getSourceManager();
SM.getFileManager().PrintStats();
llvm::outs() << "\n***\n\n";
for (SourceManager::fileinfo_iterator I = SM.fileinfo_begin(),
E = SM.fileinfo_end(); I != E; ++I) {
llvm::outs() << (*I).first->getName();
llvm::outs() << "\n";
}
}
// Run a file: .x file[(args)]
bool MetaProcessor::executeFile(const std::string& fileWithArgs,
Value* result) {
// Look for start of parameters:
typedef std::pair<llvm::StringRef,llvm::StringRef> StringRefPair;
StringRefPair pairFileArgs = llvm::StringRef(fileWithArgs).split('(');
if (pairFileArgs.second.empty()) {
pairFileArgs.second = ")";
}
StringRefPair pairPathFile = pairFileArgs.first.rsplit('/');
if (pairPathFile.second.empty()) {
pairPathFile.second = pairPathFile.first;
}
StringRefPair pairFuncExt = pairPathFile.second.rsplit('.');
Interpreter::CompilationResult interpRes
= m_Interp.declare(std::string("#include \"")
+ pairFileArgs.first.str()
+ std::string("\""));
if (interpRes != Interpreter::kFailure) {
std::string expression = pairFuncExt.first.str()
+ "(" + pairFileArgs.second.str();
interpRes = m_Interp.evaluate(expression, result);
}
return (interpRes != Interpreter::kFailure);
}
} // end namespace cling

View File

@ -0,0 +1,53 @@
#set(LLVM_LINK_COMPONENTS mc)
#set(LLVM_USED_LIBS clangBasic clangAST clangParse)
add_cling_library(clingUserInterface
UserInterface.cpp
textinput/Editor.cpp
textinput/History.cpp
textinput/KeyBinding.cpp
textinput/Range.cpp
textinput/SignalHandler.cpp
textinput/StreamReader.cpp
textinput/StreamReaderUnix.cpp
textinput/StreamReaderWin.cpp
textinput/TerminalConfigUnix.cpp
textinput/TerminalDisplay.cpp
textinput/TerminalDisplayUnix.cpp
textinput/TerminalDisplayWin.cpp
textinput/TextInput.cpp
textinput/TextInputContext.cpp
)
#Find svn information
EXECUTE_PROCESS(COMMAND ${Subversion_SVN_EXECUTABLE} info "${CMAKE_CURRENT_LIST_DIR}" RESULT_VARIABLE svn_result OUTPUT_VARIABLE svn_ver)
IF (NOT ${svn_result} EQUAL 0)
MESSAGE(SEND_ERROR "Svn command returned error, is it installed?")
ELSE(NOT ${svn_result} EQUAL 0)
#Find and clean the url of the actual repository
STRING(REGEX MATCH "URL: ([^\n]+)" url "${svn_ver}")
STRING(REPLACE "URL: " "" url "${url}")
#Point the url to the rigt location
STRING(REPLACE "interpreter/cling/lib/UserInterface" "core/textinput/src/textinput" url "${url}")
#Checkout the source
EXECUTE_PROCESS(COMMAND ${Subversion_SVN_EXECUTABLE} co "${url}" "${CMAKE_CURRENT_LIST_DIR}/textinput" RESULT_VARIABLE scn_co_result)
IF (NOT ${scn_co_result} EQUAL 0)
MESSAGE(SEND_ERROR "Svn checkout for ${url} error")
ENDIF (NOT ${scn_co_result} EQUAL 0)
ENDIF(NOT ${svn_result} EQUAL 0)
if(MSVC)
# windows.h doesn't compile with /Za
get_target_property(NON_ANSI_COMPILE_FLAGS clingUserInterface COMPILE_FLAGS)
string(REPLACE "/Za" "" NON_ANSI_COMPILE_FLAGS ${NON_ANSI_COMPILE_FLAGS})
set_target_properties(clingUserInterface PROPERTIES
COMPILE_FLAGS ${NON_ANSI_COMPILE_FLAGS})
endif()
#add_subdirectory(textinput)
#add_dependencies(clangDriver ClangAttrList ClangDiagnosticDriver
# ClangDriverOptions ClangCC1Options ClangCC1AsOptions)

View File

@ -0,0 +1,31 @@
##===- cling/lib/UserInterface/Makefile---------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
#
# This implements the a C++ interpreter user frontend.
#
##===----------------------------------------------------------------------===##
CLING_LEVEL = ../..
DIRS = textinput
LIBRARYNAME := clingUserInterface
BUILD_ARCHIVE = 1
CXXFLAGS = -fno-rtti
CPPFLAGS += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_SRC_DIR)/../../../clang/include \
-I$(PROJ_SRC_DIR)/../../../clang/lib -I$(PROJ_OBJ_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../../clang/include
include $(CLING_LEVEL)/Makefile
$(PROJ_SRC_DIR)/textinput/Makefile.d:
svn co $(shell svn info $(PROJ_SRC_DIR) | grep ^URL: | sed 's,^URL: \(.*\)interpreter/cling/lib/UserInterface,\1/core/textinput/src/textinput,') $(PROJ_SRC_DIR)/textinput
touch $@
# trigger checkput before building
include $(PROJ_SRC_DIR)/textinput/Makefile.d

View File

@ -0,0 +1,88 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------
#include <cling/UserInterface/UserInterface.h>
#include <cling/MetaProcessor/MetaProcessor.h>
#include "textinput/TextInput.h"
#include "textinput/StreamReader.h"
#include "textinput/TerminalDisplay.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/PathV1.h"
//---------------------------------------------------------------------------
// Construct an interface for an interpreter
//---------------------------------------------------------------------------
cling::UserInterface::UserInterface(Interpreter& interp):
m_MetaProcessor(new MetaProcessor(interp))
{
}
//---------------------------------------------------------------------------
// Destruct the interface
//---------------------------------------------------------------------------
cling::UserInterface::~UserInterface()
{
delete m_MetaProcessor;
}
//---------------------------------------------------------------------------
// Interact with the user using a prompt
//---------------------------------------------------------------------------
void cling::UserInterface::runInteractively(bool nologo /* = false */)
{
if (!nologo) {
PrintLogo();
}
// History file is $HOME/.cling_history
static const char* histfile = ".cling_history";
llvm::sys::Path histfilePath = llvm::sys::Path::GetUserHomeDirectory();
histfilePath.appendComponent(histfile);
using namespace textinput;
StreamReader* R = StreamReader::Create();
TerminalDisplay* D = TerminalDisplay::Create();
TextInput TI(*R, *D, histfilePath.c_str());
TI.SetPrompt("[cling]$ ");
std::string line;
MetaProcessorOpts& MPOpts = m_MetaProcessor->getMetaProcessorOpts();
while (!MPOpts.Quitting) {
llvm::outs().flush();
TextInput::EReadResult RR = TI.ReadInput();
TI.TakeInput(line);
if (RR == TextInput::kRREOF) {
MPOpts.Quitting = true;
continue;
}
int indent = m_MetaProcessor->process(line.c_str());
std::string Prompt = "[cling]";
if (MPOpts.RawInput)
Prompt.append("! ");
else
Prompt.append("$ ");
if (indent > 0)
// Continuation requested.
Prompt.append('?' + std::string(indent * 3, ' '));
TI.SetPrompt(Prompt.c_str());
}
}
void cling::UserInterface::PrintLogo() {
llvm::outs() << "\n";
llvm::outs() << "****************** CLING ******************" << "\n";
llvm::outs() << "* Type C++ code and press enter to run it *" << "\n";
llvm::outs() << "* Type .q to exit *" << "\n";
llvm::outs() << "*******************************************" << "\n";
}

254
lib/Utils/AST.cpp Normal file
View File

@ -0,0 +1,254 @@
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author: Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------
#include "cling/Utils/AST.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclarationName.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/Lookup.h"
using namespace clang;
namespace cling {
namespace utils {
Expr* Synthesize::CStyleCastPtrExpr(Sema* S, QualType Ty, uint64_t Ptr) {
ASTContext& Ctx = S->getASTContext();
if (!Ty->isPointerType())
Ty = Ctx.getPointerType(Ty);
TypeSourceInfo* TSI = Ctx.CreateTypeSourceInfo(Ty);
const llvm::APInt Addr(8 * sizeof(void *), Ptr);
Expr* Result = IntegerLiteral::Create(Ctx, Addr, Ctx.UnsignedLongTy,
SourceLocation());
Result = S->BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(),
Result).take();
assert(Result && "Cannot create CStyleCastPtrExpr");
return Result;
}
static
NestedNameSpecifier* GetPartiallyDesugaredNNS(const ASTContext& Ctx,
NestedNameSpecifier* scope,
const llvm::SmallSet<const Type*, 4>& TypesToSkip){
// Desugar the scope qualifier if needed.
const Type* scope_type = scope->getAsType();
if (scope_type) {
// this is not a namespace, so we might need to desugar
NestedNameSpecifier* outer_scope = scope->getPrefix();
if (outer_scope) {
outer_scope = GetPartiallyDesugaredNNS(Ctx, outer_scope, TypesToSkip);
}
QualType desugared =
Transform::GetPartiallyDesugaredType(Ctx,
QualType(scope_type,0),
TypesToSkip,
/*fullyQualify=*/false);
// NOTE: Should check whether the type has changed or not.
return NestedNameSpecifier::Create(Ctx,outer_scope,
false /* template keyword wanted */,
desugared.getTypePtr());
}
return scope;
}
static
NestedNameSpecifier* CreateNestedNameSpecifier(const ASTContext& Ctx,
NamespaceDecl* cl) {
NamespaceDecl* outer
= llvm::dyn_cast_or_null<NamespaceDecl>(cl->getDeclContext());
if (outer && outer->getName().size()) {
NestedNameSpecifier* outerNNS = CreateNestedNameSpecifier(Ctx,outer);
return NestedNameSpecifier::Create(Ctx,outerNNS,
cl);
} else {
return NestedNameSpecifier::Create(Ctx,
0, /* no starting '::'*/
cl);
}
}
static
NestedNameSpecifier* CreateNestedNameSpecifier(const ASTContext& Ctx,
TagDecl *cl) {
NamedDecl* outer = llvm::dyn_cast_or_null<NamedDecl>(cl->getDeclContext());
if (outer && outer->getName().size()) {
NestedNameSpecifier *outerNNS;
if (cl->getDeclContext()->isNamespace()) {
outerNNS = CreateNestedNameSpecifier(Ctx,
llvm::dyn_cast<NamespaceDecl>(outer));
} else {
outerNNS = CreateNestedNameSpecifier(Ctx,
llvm::dyn_cast<TagDecl>(outer));
}
return NestedNameSpecifier::Create(Ctx,outerNNS,
false /* template keyword wanted */,
Ctx.getTypeDeclType(cl).getTypePtr());
} else {
return NestedNameSpecifier::Create(Ctx,
0, /* no starting '::'*/
false /* template keyword wanted */,
Ctx.getTypeDeclType(cl).getTypePtr());
}
}
QualType Transform::GetPartiallyDesugaredType(const ASTContext& Ctx,
QualType QT,
const llvm::SmallSet<const Type*, 4>& TypesToSkip,
bool fullyQualify /*=true*/){
// If there are no constains - use the standard desugaring.
if (!TypesToSkip.size())
return QT.getDesugaredType(Ctx);
// In case of Int_t* we need to strip the pointer first, desugar and attach
// the pointer once again.
if (QT->isPointerType()) {
// Get the qualifiers.
Qualifiers quals = QT.getQualifiers();
QT = GetPartiallyDesugaredType(Ctx, QT->getPointeeType(), TypesToSkip,
fullyQualify);
QT = Ctx.getPointerType(QT);
// Add back the qualifiers.
QT = Ctx.getQualifiedType(QT, quals);
}
// In case of Int_t& we need to strip the pointer first, desugar and attach
// the pointer once again.
if (QT->isReferenceType()) {
// Get the qualifiers.
bool isLValueRefTy = isa<LValueReferenceType>(QT.getTypePtr());
Qualifiers quals = QT.getQualifiers();
QT = GetPartiallyDesugaredType(Ctx, QT->getPointeeType(), TypesToSkip,
fullyQualify);
// Add the r- or l- value reference type back to the desugared one
if (isLValueRefTy)
QT = Ctx.getLValueReferenceType(QT);
else
QT = Ctx.getRValueReferenceType(QT);
// Add back the qualifiers.
QT = Ctx.getQualifiedType(QT, quals);
}
while(isa<TypedefType>(QT.getTypePtr())) {
if (!TypesToSkip.count(QT.getTypePtr()))
QT = QT.getSingleStepDesugaredType(Ctx);
else
return QT;
}
NestedNameSpecifier* prefix = 0;
const ElaboratedType* etype
= llvm::dyn_cast<ElaboratedType>(QT.getTypePtr());
if (etype) {
// We have to also desugar the prefix.
prefix = GetPartiallyDesugaredNNS(Ctx, etype->getQualifier(), TypesToSkip);
QT = QualType(etype->getNamedType().getTypePtr(),QT.getLocalFastQualifiers());
} else if (fullyQualify) {
// Let's check whether this type should have been an elaborated type.
// in which case we want to add it ... but we can't really preserve
// the typedef in this case ...
CXXRecordDecl* cl = QT->getAsCXXRecordDecl();
if (cl) {
NamedDecl* outer
= llvm::dyn_cast_or_null<NamedDecl>(cl->getDeclContext());
if (outer && outer->getName ().size()) {
if (cl->getDeclContext()->isNamespace()) {
prefix = CreateNestedNameSpecifier(Ctx,
llvm::dyn_cast<NamespaceDecl>(outer));
} else {
prefix = CreateNestedNameSpecifier(Ctx,
llvm::dyn_cast<TagDecl>(outer));
}
}
}
}
// In case of template specializations iterate over the arguments and
// desugar them as well.
if(const TemplateSpecializationType* TST
= dyn_cast<const TemplateSpecializationType>(QT.getTypePtr())) {
bool mightHaveChanged = false;
llvm::SmallVector<TemplateArgument, 4> desArgs;
for(TemplateSpecializationType::iterator I = TST->begin(), E = TST->end();
I != E; ++I) {
QualType SubTy = I->getAsType();
if (SubTy.isNull()) {
desArgs.push_back(*I);
continue;
}
// Check if the type needs more desugaring and recurse.
if (isa<TypedefType>(SubTy)
|| isa<TemplateSpecializationType>(SubTy)
|| fullyQualify) {
mightHaveChanged = true;
desArgs.push_back(TemplateArgument(GetPartiallyDesugaredType(Ctx,
SubTy,
TypesToSkip,
fullyQualify)));
} else
desArgs.push_back(*I);
}
// If desugaring happened allocate new type in the AST.
if (mightHaveChanged) {
QT = Ctx.getTemplateSpecializationType(TST->getTemplateName(),
desArgs.data(),
desArgs.size(),
TST->getCanonicalTypeInternal());
}
}
if (prefix) {
QT = Ctx.getElaboratedType(ETK_None,prefix,QT);
}
return QT;
}
NamespaceDecl* Lookup::Namespace(Sema* S, const char* Name,
DeclContext* Within) {
DeclarationName DName = &S->Context.Idents.get(Name);
LookupResult R(*S, DName, SourceLocation(),
Sema::LookupNestedNameSpecifierName);
if (!Within)
S->LookupName(R, S->TUScope);
else
S->LookupQualifiedName(R, Within);
if (R.empty())
return 0;
R.resolveKind();
return dyn_cast<NamespaceDecl>(R.getFoundDecl());
}
NamedDecl* Lookup::Named(Sema* S, const char* Name, DeclContext* Within) {
DeclarationName DName = &S->Context.Idents.get(Name);
LookupResult R(*S, DName, SourceLocation(), Sema::LookupOrdinaryName,
Sema::ForRedeclaration);
if (!Within)
S->LookupName(R, S->TUScope);
else
S->LookupQualifiedName(R, Within);
if (R.empty())
return 0;
R.resolveKind();
return R.getFoundDecl();
}
} // end namespace utils
} // end namespace cling

8
lib/Utils/CMakeLists.txt Normal file
View File

@ -0,0 +1,8 @@
set(LLVM_USED_LIBS
clangCodeGen
clangBasic
)
add_cling_library(clingUtils
AST.cpp
)

22
lib/Utils/Makefile Normal file
View File

@ -0,0 +1,22 @@
##===- cling/lib/MetaProcessor/Makefile---------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
#
# This implements the a C++ interpreter user frontend.
#
##===----------------------------------------------------------------------===##
CLING_LEVEL = ../..
LIBRARYNAME := clingUtils
BUILD_ARCHIVE = 1
CXXFLAGS = -fno-rtti
CPPFLAGS += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_SRC_DIR)/../../../clang/include \
-I$(PROJ_SRC_DIR)/../../../clang/lib -I$(PROJ_OBJ_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../../clang/include
include $(CLING_LEVEL)/Makefile

502
patches/clang-Modules.diff Normal file
View File

@ -0,0 +1,502 @@
Index: tools/clang/test/Modules/redecl-templates.mm
===================================================================
--- tools/clang/test/Modules/redecl-templates.mm (revision 0)
+++ tools/clang/test/Modules/redecl-templates.mm (revision 0)
@@ -0,0 +1,29 @@
+// template <typename T> int foo(T);
+// int usesfootoo() { return foo((float)1); }
+// template <typename T> int foo(T) { return 0; }
+
+// class Redecl;
+// class RedeclImp { Redecl* p; };
+// class Redecl { public: int M; };
+
+@__experimental_modules_import redecl_templates_left;
+@__experimental_modules_import redecl_templates_right;
+
+int call() {
+ // L::AB b;
+
+ // Redecl r;
+
+ //usesfwddeclThenDefinedInLeftRight();
+ //usesfwddeclThenDefinedInLeftRightToo();
+ definedInLeft((double)0.);
+// L::definedInLeftSpecializedInRight((float)0);
+// L::definedInLeftSpecializedInRight((char)0);
+ return 0;
+}
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fmodules -x objective-c++ -fmodule-cache-path %t -emit-module -fmodule-name=redecl_templates_left %S/Inputs/module.map
+// RUN: %clang_cc1 -fmodules -x objective-c++ -fmodule-cache-path %t -emit-module -fmodule-name=redecl_templates_right %S/Inputs/module.map
+// RUN: %clang_cc1 -fmodules -fmodule-cache-path %t -w %s -emit-obj -o %t.obj
+// RUN: echo 'int call(); int main(int,char*[]) { return call(); }' | %clang -x objective-c++ -fmodules -fmodule-cache-path %t - -c -o %t_main.obj
+// RUN: %clang -lstdc++ %t_main.obj %t.obj
Index: tools/clang/test/Modules/Inputs/module.map
===================================================================
--- tools/clang/test/Modules/Inputs/module.map (revision 162896)
+++ tools/clang/test/Modules/Inputs/module.map (working copy)
@@ -38,6 +38,8 @@
module redeclarations_right { header "redeclarations_right.h" }
module redecl_namespaces_left { header "redecl_namespaces_left.h" }
module redecl_namespaces_right { header "redecl_namespaces_right.h" }
+module redecl_templates_left { header "redecl_templates_left.h" }
+module redecl_templates_right { header "redecl_templates_right.h" }
module load_failure { header "load_failure.h" }
module decldef {
Index: tools/clang/test/Modules/Inputs/redecl_templates_right.h
===================================================================
--- tools/clang/test/Modules/Inputs/redecl_templates_right.h (revision 0)
+++ tools/clang/test/Modules/Inputs/redecl_templates_right.h (revision 0)
@@ -0,0 +1,23 @@
+//namespace L {
+ /*
+ template <typename T> class AA;
+ template <typename T> class AA {};
+ class AB: public virtual AA<char> {};
+ */
+
+ //template <typename T> int fwddeclThenDefinedInLeftRight(T);
+
+ // int usesfwddeclThenDefinedInLeftRightToo() {
+ // return fwddeclThenDefinedInLeftRight((float)1);
+ // }
+
+ // template <typename T> int fwddeclThenDefinedInLeftRight(T) { return 0; }
+
+ template <typename T> int definedInLeft(T);
+
+ // template <typename T> int definedInLeftSpecializedInRight(T);
+ // template <> int definedInLeftSpecializedInRight<char>(char) { return 1; }
+ // int usesdefinedInLeftSpecializedInRightChar() {
+ // return definedInLeftSpecializedInRight((char)0);
+ // }
+//}
Property changes on: tools/clang/test/Modules/Inputs/redecl_templates_right.h
___________________________________________________________________
Added: svn:eol-style
+ LF
Added: svn:keywords
+ Id
Index: tools/clang/test/Modules/Inputs/redecl_templates_left.h
===================================================================
--- tools/clang/test/Modules/Inputs/redecl_templates_left.h (revision 0)
+++ tools/clang/test/Modules/Inputs/redecl_templates_left.h (revision 0)
@@ -0,0 +1,31 @@
+//namespace L {
+ // template <typename T> class AA;
+ // template <typename T> class AA {};
+ // class AB: public virtual AA<char> {};
+
+ // class Redecl;
+ // class RedeclImp { Redecl* p; };
+
+ // class Redecl { public: int M; };
+
+ // class Redecl;
+
+ // class RedeclUse { Redecl* use; };
+
+ // class Redecl;
+
+ //template <typename T> int fwddeclThenDefinedInLeftRight(T);
+
+ //int usesfwddeclThenDefinedInLeftRight() {
+ // return fwddeclThenDefinedInLeftRight((char)1);
+ //}
+
+ // template <typename T> int fwddeclThenDefinedInLeftRight(T) { return 0; }
+
+ template <typename T> int definedInLeft(T) { return 1; }
+ //int usesdefinedInLeft() { return definedInLeft((int)0); }
+
+ // template <typename T> int definedInLeftSpecializedInRight(T) { return 2; }
+ // int usesdefinedInLeftSpecializedInRightInt() {
+ // return definedInLeftSpecializedInRight((int)0); }
+ //}
Property changes on: tools/clang/test/Modules/Inputs/redecl_templates_left.h
___________________________________________________________________
Added: svn:eol-style
+ LF
Added: svn:keywords
+ Id
Index: tools/clang/include/clang/Serialization/ASTReader.h
===================================================================
--- tools/clang/include/clang/Serialization/ASTReader.h (revision 162896)
+++ tools/clang/include/clang/Serialization/ASTReader.h (working copy)
@@ -686,7 +686,15 @@
/// need to be emitted, such as inline function definitions or
/// Objective-C protocols.
std::deque<Decl *> InterestingDecls;
+public:
+ /// \brief Currently deserializing Decls
+ ///
+ /// Decls that are currently read but have not been completed yet.
+ llvm::SmallPtrSet<Decl *, 16> DeclsInFlight;
+ llvm::SmallPtrSet<Decl *, 16> RedeclsAddedToAST;
+
+private:
/// \brief The set of redeclarable declaraations that have been deserialized
/// since the last time the declaration chains were linked.
llvm::SmallPtrSet<Decl *, 16> RedeclsDeserialized;
@@ -738,7 +746,7 @@
/// the given canonical declaration.
MergedDeclsMap::iterator
combineStoredMergedDecls(Decl *Canon, serialization::GlobalDeclID CanonID);
-
+
/// \brief Ready to load the previous declaration of the given Decl.
void loadAndAttachPreviousDecl(Decl *D, serialization::DeclID ID);
@@ -853,6 +861,8 @@
void finishPendingActions();
+ bool needPendingInstantiation(ValueDecl* D) const;
+
/// \brief Produce an error diagnostic and return true.
///
/// This routine should only be used for fatal errors that have to
Index: tools/clang/include/clang/Lex/HeaderSearch.h
===================================================================
--- tools/clang/include/clang/Lex/HeaderSearch.h (revision 162896)
+++ tools/clang/include/clang/Lex/HeaderSearch.h (working copy)
@@ -283,6 +283,11 @@
/// \brief Retrieve the path to the module cache.
StringRef getModuleCachePath() const { return ModuleCachePath; }
+
+ /// \brief Consider modules when including files from this directory.
+ void setDirectoryHasModuleMap(const DirectoryEntry* Dir) {
+ DirectoryHasModuleMap[Dir] = true;
+ }
/// \brief Forget everything we know about headers so far.
void ClearFileInfo() {
Index: tools/clang/lib/Serialization/ASTReaderDecl.cpp
===================================================================
--- tools/clang/lib/Serialization/ASTReaderDecl.cpp (revision 162896)
+++ tools/clang/lib/Serialization/ASTReaderDecl.cpp (working copy)
@@ -520,7 +520,9 @@
FD->IsConstexpr = Record[Idx++];
FD->EndRangeLoc = ReadSourceLocation(Record, Idx);
- switch ((FunctionDecl::TemplatedKind)Record[Idx++]) {
+ FunctionDecl::TemplatedKind TmpltKind
+ = (FunctionDecl::TemplatedKind) Record[Idx++];
+ switch (TmpltKind) {
case FunctionDecl::TK_NonTemplate:
mergeRedeclarable(FD, Redecl);
break;
@@ -623,6 +625,9 @@
for (unsigned I = 0; I != NumParams; ++I)
Params.push_back(ReadDeclAs<ParmVarDecl>(Record, Idx));
FD->setParams(Reader.getContext(), Params);
+
+ if (TmpltKind != FunctionDecl::TK_NonTemplate)
+ Reader.DeclsInFlight.erase(FD);
}
void ASTDeclReader::VisitObjCMethodDecl(ObjCMethodDecl *MD) {
@@ -901,6 +906,7 @@
// Only true variables (not parameters or implicit parameters) can be merged.
if (VD->getKind() == Decl::Var)
mergeRedeclarable(VD, Redecl);
+ else Reader.DeclsInFlight.erase(VD);
if (uint64_t Val = Record[Idx++]) {
VD->setInit(Reader.ReadExpr(F));
@@ -1311,7 +1317,7 @@
VisitTemplateDecl(D);
D->IdentifierNamespace = Record[Idx++];
-
+ mergeRedeclarable(D, Redecl);
return Redecl;
}
@@ -1515,6 +1521,7 @@
template <typename T>
ASTDeclReader::RedeclarableResult
ASTDeclReader::VisitRedeclarable(Redeclarable<T> *D) {
+ Reader.DeclsInFlight.insert(static_cast<T *>(D));
DeclID FirstDeclID = ReadDeclID(Record, Idx);
// 0 indicates that this declaration was the only declaration of its entity,
@@ -1545,6 +1552,7 @@
void ASTDeclReader::mergeRedeclarable(Redeclarable<T> *D,
RedeclarableResult &Redecl) {
// If modules are not available, there is no reason to perform this merge.
+ Reader.DeclsInFlight.erase(static_cast<T*>(D));
if (!Reader.getContext().getLangOpts().Modules)
return;
@@ -1731,7 +1739,7 @@
return (FuncX->getLinkage() == FuncY->getLinkage()) &&
FuncX->getASTContext().hasSameType(FuncX->getType(), FuncY->getType());
}
-
+
// Variables with the same type and linkage match.
if (VarDecl *VarX = dyn_cast<VarDecl>(X)) {
VarDecl *VarY = cast<VarDecl>(Y);
@@ -1744,20 +1752,26 @@
NamespaceDecl *NamespaceY = cast<NamespaceDecl>(Y);
return NamespaceX->isInline() == NamespaceY->isInline();
}
-
+
// FIXME: Many other cases to implement.
return false;
}
ASTDeclReader::FindExistingResult::~FindExistingResult() {
- if (!AddResult || Existing)
+ if (!AddResult || Existing) {
return;
+ }
DeclContext *DC = New->getDeclContext()->getRedeclContext();
if (DC->isTranslationUnit() && Reader.SemaObj) {
- Reader.SemaObj->IdResolver.tryAddTopLevelDecl(New, New->getDeclName());
+ if (Reader.SemaObj->IdResolver.tryAddTopLevelDecl(New, New->getDeclName()))
+ Reader.RedeclsAddedToAST.insert(New);
} else if (DC->isNamespace()) {
+ DeclContext* oldCtx = New->getLexicalDeclContext();
+ New->setLexicalDeclContext(DC);
DC->addDecl(New);
+ New->setLexicalDeclContext(oldCtx);
+ Reader.RedeclsAddedToAST.insert(New);
}
}
@@ -1779,7 +1793,7 @@
for (IdentifierResolver::iterator I = IdResolver.begin(Name),
IEnd = IdResolver.end();
I != IEnd; ++I) {
- if (isSameEntity(*I, D))
+ if (*I != D && isSameEntity(*I, D))
return FindExistingResult(Reader, D, *I);
}
}
@@ -1787,7 +1801,9 @@
if (DC->isNamespace()) {
for (DeclContext::lookup_result R = DC->lookup(Name);
R.first != R.second; ++R.first) {
- if (isSameEntity(*R.first, D))
+ if (*R.first != D
+ && !Reader.DeclsInFlight.count(*R.first)
+ && isSameEntity(*R.first, D))
return FindExistingResult(Reader, D, *R.first);
}
}
@@ -1798,22 +1814,41 @@
void ASTDeclReader::attachPreviousDecl(Decl *D, Decl *previous) {
assert(D && previous);
if (TagDecl *TD = dyn_cast<TagDecl>(D)) {
- TD->RedeclLink.setNext(cast<TagDecl>(previous));
+ //TD->RedeclLink.setNext(cast<TagDecl>(previous));
+ TD->RedeclLink
+ = Redeclarable<TagDecl>::PreviousDeclLink(cast<TagDecl>(previous));
} else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
- FD->RedeclLink.setNext(cast<FunctionDecl>(previous));
+ //FD->RedeclLink.setNext(cast<FunctionDecl>(previous));
+ FD->RedeclLink
+ = Redeclarable<FunctionDecl>::PreviousDeclLink(cast<FunctionDecl>(previous));
} else if (VarDecl *VD = dyn_cast<VarDecl>(D)) {
- VD->RedeclLink.setNext(cast<VarDecl>(previous));
+ //VD->RedeclLink.setNext(cast<VarDecl>(previous));
+ VD->RedeclLink
+ = Redeclarable<VarDecl>::PreviousDeclLink(cast<VarDecl>(previous));
} else if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(D)) {
- TD->RedeclLink.setNext(cast<TypedefNameDecl>(previous));
+ //TD->RedeclLink.setNext(cast<TypedefNameDecl>(previous));
+ TD->RedeclLink
+ = Redeclarable<TypedefNameDecl>::PreviousDeclLink(cast<TypedefNameDecl>(previous));
} else if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(D)) {
- ID->RedeclLink.setNext(cast<ObjCInterfaceDecl>(previous));
+ //ID->RedeclLink.setNext(cast<ObjCInterfaceDecl>(previous));
+ ID->RedeclLink
+ = Redeclarable<ObjCInterfaceDecl>::PreviousDeclLink(cast<ObjCInterfaceDecl>(previous));
} else if (ObjCProtocolDecl *PD = dyn_cast<ObjCProtocolDecl>(D)) {
- PD->RedeclLink.setNext(cast<ObjCProtocolDecl>(previous));
+ // PD->RedeclLink.setNext(cast<ObjCProtocolDecl>(previous));
+ PD->RedeclLink
+ = Redeclarable<ObjCProtocolDecl>::PreviousDeclLink(cast<ObjCProtocolDecl>(previous));
} else if (NamespaceDecl *ND = dyn_cast<NamespaceDecl>(D)) {
- ND->RedeclLink.setNext(cast<NamespaceDecl>(previous));
+ // ND->RedeclLink.setNext(cast<NamespaceDecl>(previous));
+ ND->RedeclLink
+ = Redeclarable<NamespaceDecl>::PreviousDeclLink(cast<NamespaceDecl>(previous));
} else {
RedeclarableTemplateDecl *TD = cast<RedeclarableTemplateDecl>(D);
- TD->RedeclLink.setNext(cast<RedeclarableTemplateDecl>(previous));
+ RedeclarableTemplateDecl *PrevTD = cast<RedeclarableTemplateDecl>(previous);
+ TD->RedeclLink
+ = Redeclarable<RedeclarableTemplateDecl>::PreviousDeclLink(PrevTD);
+ // We should attach the templated decl as well:
+ attachPreviousDecl(TD->getTemplatedDecl(), PrevTD->getTemplatedDecl());
+ //TD->RedeclLink.setNext(cast<RedeclarableTemplateDecl>(previous));
}
}
@@ -1846,9 +1881,11 @@
cast<NamespaceDecl>(Latest));
} else {
RedeclarableTemplateDecl *TD = cast<RedeclarableTemplateDecl>(D);
+ RedeclarableTemplateDecl *LatestTD = cast<RedeclarableTemplateDecl>(Latest);
TD->RedeclLink
- = Redeclarable<RedeclarableTemplateDecl>::LatestDeclLink(
- cast<RedeclarableTemplateDecl>(Latest));
+ = Redeclarable<RedeclarableTemplateDecl>::LatestDeclLink(LatestTD);
+ // We should attach the templated decl as well:
+ attachLatestDecl(TD->getTemplatedDecl(), LatestTD->getTemplatedDecl());
}
}
@@ -2125,9 +2162,16 @@
// AST consumer might need to know about, queue it.
// We don't pass it to the consumer immediately because we may be in recursive
// loading, and some declarations may still be initializing.
- if (isConsumerInterestedIn(D))
+ bool HaveModules = getContext().getLangOpts().Modules;
+ if (HaveModules) {
+ if (RedeclsAddedToAST.count(D)) {
+ RedeclsAddedToAST.erase(D);
+ if (isConsumerInterestedIn(D))
+ InterestingDecls.push_back(D);
+ }
+ } else if (isConsumerInterestedIn(D))
InterestingDecls.push_back(D);
-
+
return D;
}
@@ -2273,7 +2317,7 @@
}
MergedDeclsMap::iterator MergedPos = combineStoredMergedDecls(CanonDecl, ID);
if (MergedPos != MergedDecls.end())
- SearchDecls.append(MergedPos->second.begin(), MergedPos->second.end());
+ SearchDecls.append(MergedPos->second.begin(), MergedPos->second.end());
// Build up the list of redeclarations.
RedeclChainVisitor Visitor(*this, SearchDecls, RedeclsDeserialized, CanonID);
@@ -2475,3 +2519,60 @@
}
}
}
+
+/// \brief Return a template specialization of ND (should be a TemplateDecl)
+/// that matches FD or TD.
+static NamedDecl* findMatchingSpecialization(FunctionDecl* FD,
+ ClassTemplateSpecializationDecl*TD,
+ NamedDecl* ND) {
+ TemplateDecl* Templt = dyn_cast<TemplateDecl>(ND);
+ if (!Templt) return 0;
+ if (FD) {
+ FunctionTemplateDecl* FTD = dyn_cast<FunctionTemplateDecl>(Templt);
+ if (!FTD) return 0;
+ const TemplateArgumentList* TmpltArgs = FD->getTemplateSpecializationArgs();
+ assert(TmpltArgs || "Template without arguments");
+ void* InsertionPoint;
+ return FTD->findSpecialization(TmpltArgs->data(), TmpltArgs->size(),
+ InsertionPoint);
+ } else {
+ ClassTemplateDecl* CTD = dyn_cast<ClassTemplateDecl>(Templt);
+ if (!CTD) return 0;
+ const TemplateArgumentList& TmpltArgs = TD->getTemplateArgs();
+ void* InsertionPoint;
+ return CTD->findSpecialization(TmpltArgs.data(), TmpltArgs.size(),
+ InsertionPoint);
+ }
+ return 0;
+}
+
+/// \brief Find out whether an instantiation (outside the module) already exists
+bool ASTReader::needPendingInstantiation(ValueDecl* D) const {
+ DeclContext *DC = D->getDeclContext()->getRedeclContext();
+ DeclarationName Name = D->getDeclName();
+ assert(Name && "unnamed template");
+
+ FunctionDecl* FD = dyn_cast<FunctionDecl>(D);
+ ClassTemplateSpecializationDecl* CD
+ = FD ? 0 : dyn_cast<ClassTemplateSpecializationDecl>(D);
+
+ NamedDecl* FoundSpecialization = 0;
+ if (DC->isTranslationUnit() && SemaObj) {
+ IdentifierResolver &IdResolver = SemaObj->IdResolver;
+ for (IdentifierResolver::iterator I = IdResolver.begin(Name),
+ IEnd = IdResolver.end();
+ I != IEnd && !FoundSpecialization; ++I)
+ FoundSpecialization = findMatchingSpecialization(FD, CD, *I);
+ } else {
+ // templates are redeclarables, i.e. they must have been merged into
+ // the primary context. Use localUncachedLookup to not pick up template
+ // decls from modules again.
+ llvm::SmallVector<NamedDecl*, 6> Results;
+ DC->getPrimaryContext()->localUncachedLookup(Name, Results);
+ for (llvm::SmallVector<NamedDecl *, 6>::const_iterator
+ I = Results.begin(), E = Results.end();
+ I != E && FoundSpecialization; ++I)
+ FoundSpecialization = findMatchingSpecialization(FD, CD, *I);
+ }
+ return FoundSpecialization && isSameEntity(FoundSpecialization, D);
+}
Index: tools/clang/lib/Serialization/ASTReader.cpp
===================================================================
--- tools/clang/lib/Serialization/ASTReader.cpp (revision 162896)
+++ tools/clang/lib/Serialization/ASTReader.cpp (working copy)
@@ -4928,6 +4928,9 @@
if (!ND)
continue;
+ if (This->Reader.DeclsInFlight.count(ND))
+ continue;
+
if (ND->getDeclName() != This->Name) {
assert(!This->Name.getCXXNameType().isNull() &&
"Name mismatch without a type");
@@ -5569,7 +5572,11 @@
ValueDecl *D = cast<ValueDecl>(GetDecl(PendingInstantiations[Idx++]));
SourceLocation Loc
= SourceLocation::getFromRawEncoding(PendingInstantiations[Idx++]);
- Pending.push_back(std::make_pair(D, Loc));
+
+ // For modules, find out whether an instantiation already exists
+ if (!getContext().getLangOpts().Modules
+ || needPendingInstantiation(D))
+ Pending.push_back(std::make_pair(D, Loc));
}
PendingInstantiations.clear();
}
@@ -6466,6 +6473,9 @@
}
ASTReader::~ASTReader() {
+ assert(DeclsInFlight.empty() && "DeclsInFlight not empty!");
+ assert(RedeclsAddedToAST.empty() && "RedeclsAddedToAST not empty!");
+
for (DeclContextVisibleUpdatesPending::iterator
I = PendingVisibleUpdates.begin(),
E = PendingVisibleUpdates.end();
Index: tools/clang/lib/AST/Decl.cpp
===================================================================
--- tools/clang/lib/AST/Decl.cpp (revision 162896)
+++ tools/clang/lib/AST/Decl.cpp (working copy)
@@ -936,7 +936,8 @@
}
bool NamedDecl::declarationReplaces(NamedDecl *OldD) const {
- assert(getDeclName() == OldD->getDeclName() && "Declaration name mismatch");
+ assert((!getDeclName() || OldD->getDeclName() \
+ || getDeclName() == OldD->getDeclName()) && "Declaration name mismatch");
// UsingDirectiveDecl's are not really NamedDecl's, and all have same name.
// We want to keep it, unless it nominates same namespace.

View File

@ -0,0 +1,22 @@
Index: tools/clang/include/clang/Parse/Parser.h
===================================================================
--- tools/clang/include/clang/Parse/Parser.h (revision 162901)
+++ tools/clang/include/clang/Parse/Parser.h (working copy)
@@ -282,7 +282,7 @@
/// the EOF was encountered.
bool ParseTopLevelDecl(DeclGroupPtrTy &Result);
-private:
+protected:
//===--------------------------------------------------------------------===//
// Low-Level token peeking and consumption methods.
//
@@ -709,7 +709,7 @@
return Diag(Tok, DiagID);
}
-private:
+protected:
void SuggestParentheses(SourceLocation Loc, unsigned DK,
SourceRange ParenRange);
void CheckNestedObjCContexts(SourceLocation AtLoc);

View File

@ -0,0 +1,21 @@
Index: test/Makefile
===================================================================
--- test/Makefile (revision 161655)
+++ test/Makefile (working copy)
@@ -54,6 +54,16 @@
ifneq ($(strip $(filter check-local-all,$(MAKECMDGOALS))),)
ifndef TESTSUITE
+
+ifeq ($(NOCLING)$(shell test -f $(PROJ_OBJ_DIR)/../tools/cling/Makefile && echo OK), OK)
+LIT_ALL_TESTSUITES += $(PROJ_OBJ_DIR)/../tools/cling/test
+
+# Force creation of Cling's lit.site.cfg.
+cling-lit-site-cfg: FORCE
+ $(MAKE) -C $(PROJ_OBJ_DIR)/../tools/cling/test lit.site.cfg
+extra-lit-site-cfgs:: cling-lit-site-cfg
+endif
+
ifeq ($(shell test -f $(PROJ_OBJ_DIR)/../tools/clang/Makefile && echo OK), OK)
LIT_ALL_TESTSUITES += $(PROJ_OBJ_DIR)/../tools/clang/test

View File

@ -0,0 +1,13 @@
Index: tools/CMakeLists.txt
===================================================================
--- tools/CMakeLists.txt (revision 159991)
+++ tools/CMakeLists.txt (working copy)
@@ -52,4 +52,8 @@
add_llvm_external_project(lld)
add_llvm_external_project(polly)
+if( EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/cling/CMakeLists.txt )
+ add_subdirectory( ${CMAKE_CURRENT_SOURCE_DIR}/cling )
+endif( EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/cling/CMakeLists.txt )
+
set(LLVM_COMMON_DEPENDS ${LLVM_COMMON_DEPENDS} PARENT_SCOPE)

View File

@ -0,0 +1,14 @@
Index: tools/Makefile
===================================================================
--- tools/Makefile (revision 159991)
+++ tools/Makefile (working copy)
@@ -22,6 +22,9 @@
# Build LLDB if present. Note LLDB must be built last as it depends on the
# wider LLVM infrastructure (including Clang).
OPTIONAL_DIRS := lldb
+ifeq ($(NOCLING),)
+OPTIONAL_DIRS += cling
+endif
# NOTE: The tools are organized into five groups of four consisting of one
# large and three small executables. This is done to minimize memory load

95
test/CMakeLists.txt Normal file
View File

@ -0,0 +1,95 @@
set(CLING_TEST_DIRECTORIES
"ErrorRecovery"
"Extensions"
"LibraryCall"
"Prompt"
"Recursion"
"SourceCall")
set(LLVM_SOURCE_DIR "${LLVM_MAIN_SRC_DIR}")
set(LLVM_BINARY_DIR "${LLVM_BINARY_DIR}")
set(LLVM_BUILD_MODE "%(build_mode)s")
set(LLVM_TOOLS_DIR "${LLVM_TOOLS_BINARY_DIR}/%(build_config)s")
set(LLVM_LIBS_DIR "${LLVM_BINARY_DIR}/lib/%(build_config)s")
set(CLING_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..")
set(CLING_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/..")
if(BUILD_SHARED_LIBS)
set(ENABLE_SHARED 1)
else()
set(ENABLE_SHARED 0)
endif(BUILD_SHARED_LIBS)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg)
#configure_file(
# ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in
# ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg
# @ONLY)
include(FindPythonInterp)
if(PYTHONINTERP_FOUND)
if( LLVM_MAIN_SRC_DIR )
set(LIT "${LLVM_SOURCE_DIR}/utils/lit/lit.py")
else()
set(LIT "${PATH_TO_LLVM_BUILD}/bin/${CMAKE_CFG_INTDIR}/llvm-lit")
# Installed LLVM does not contain ${CMAKE_CFG_INTDIR} in paths.
if( NOT EXISTS ${LIT} )
set(LIT "${PATH_TO_LLVM_BUILD}/bin/llvm-lit")
endif()
endif()
if( PATH_TO_LLVM_BUILD )
set(CLING_TEST_EXTRA_ARGS "--path=${CLING_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}")
endif()
option(CLING_TEST_USE_VG "Run Cling tests under Valgrind" OFF)
if(CLING_TEST_USE_VG)
set(CLING_TEST_EXTRA_ARGS ${CLING_TEST_EXTRA_ARGS} "--vg")
endif ()
set(LIT_ARGS "${CLING_TEST_EXTRA_ARGS} ${LLVM_LIT_ARGS}")
separate_arguments(LIT_ARGS)
add_custom_target(cling-test.deps)
set_target_properties(cling-test.deps PROPERTIES FOLDER "Cling tests")
add_custom_target(cling-test
COMMAND ${PYTHON_EXECUTABLE}
${LIT}
--param claig_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
--param cling_unit_site_config=${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg
--param build_config=${CMAKE_CFG_INTDIR}
--param build_mode=${RUNTIME_BUILD_MODE}
${LIT_ARGS}
${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Running Cling regression tests")
# if( NOT CLING_BUILT_STANDALONE )
# add_custom_target(check-all
# COMMAND ${PYTHON_EXECUTABLE}
# ${LIT}
# --param build_config=${CMAKE_CFG_INTDIR}
# --param build_mode=${RUNTIME_BUILD_MODE}
# ${LIT_ARGS}
# ${LLVM_BINARY_DIR}/test
# ${CMAKE_CURRENT_BINARY_DIR}
# COMMENT "Running Cling and LLVM regression tests")
# add_dependencies(check-all cling-test.deps)
# if ( LLVM_INCLUDE_TESTS )
# add_dependencies(cling-test.deps ClangUnitTests)
# add_dependencies(check-all check.deps)
# endif ( LLVM_INCLUDE_TESTS )
# add_dependencies(cling-test.deps
# llvm-dis llc opt
# FileCheck count not
# )
# endif()
add_dependencies(cling-test cling-test.deps)
add_dependencies(cling-test.deps
cling cling-headers c-index-test diagtool arcmt-test c-arcmt-test
)
endif()

View File

@ -0,0 +1,14 @@
// RUN: cat %s | %cling -Xclang -verify -I%p | FileCheck %s
// Test the ability of unloading the last transaction. Here as a matter of fact
// we unload the wrapper as well and TODO: decrement the unique wrapper counter.
int f = 0;
.U
.rawInput 1
extern "C" int printf(const char* fmt, ...);
void f() {
printf("Now f is a function\n");
}
.rawInput 0
f()
//CHECK: Now f is a function

View File

@ -0,0 +1,3 @@
load_lib llvm.exp
RunLLVMTests [lsort [glob -nocomplain $srcdir/$subdir/*.{ll,c,C,cpp}]]

View File

@ -0,0 +1,54 @@
// RUN: cat %s | %cling -Xclang -verify
// Actually test clang::DeclContext::removeDecl(). This function in clang is
// the main method that is used for the error recovery. This means when there
// is an error in cling's input we need to revert all the declarations that came
// in from the same transaction. Even when we have anonymous declarations we
// need to be able to remove them from the declaration context. In a compiler's
// point of view there is no way that one can call removeDecl() and pass in anon
// decl, because the method is used when shadowing decls, which must have names.
// The issue is (and we patched it) is that removeDecl is trying to remove the
// anon decl (which doesn't have name) from the symbol (lookup) tables, which
// doesn't make sense.
// The current test checks if that codepath in removeDecl still exists because
// it is important for the stable error recovery in cling
class MyClass {
struct {
int a;
error_here; // expected-error {{C++ requires a type specifier for all declarations}}
};
};
struct X {
union {
float f3;
double d2;
} named;
union {
int i;
float f;
union {
float f2;
mutable double d;
};
};
void test_unqual_references();
struct {
int a;
float b;
};
void test_unqual_references_const() const;
mutable union { // expected-error{{anonymous union at class scope must not have a storage specifier}}
float c1;
double c2;
};
};
.q

View File

@ -0,0 +1,5 @@
extern "C" int printf(const char* fmt, ...);
class MyClass {
MyClass() { printf("MyClass ctor called!\n"); }
};

View File

@ -0,0 +1,5 @@
// RUN: cat %s | %cling -I%p | FileCheck %s
// XFAIL: *
#include "Overloads.h"
error_here;

View File

@ -0,0 +1,6 @@
extern "C" int printf(const char* fmt, ...);
class MyClass {
MyClass() { printf("MyClass ctor called!\n"); }
};

View File

@ -0,0 +1,11 @@
// RUN: cat %s | %cling -Xclang -verify -I%p
#define BEGIN_NAMESPACE namespace test_namespace {
#define END_NAMESPACE }
.rawInput 1
BEGIN_NAMESPACE int j; END_NAMESPACE
BEGIN_NAMESPACE int j; END_NAMESPACE // expected-error {{redefinition of 'j'}} expected-note {{previous definition is here}}
.rawInput 0

View File

@ -0,0 +1,27 @@
// RUN: cat %s | %cling -Xclang -verify -I%p
// XFAIL: vg
// We expect for now this to fail, because the access to the invalid memory comes
// from the fact that we invalidate the included file cache and when we are in
// verify mode the verifier notifies about errors at the end of the source file.
//
// Tests the ChainedConsumer's ability to recover from errors. .x produces
// #include \"CannotDotX.h\" \n void wrapper() {CannotDotX();}, which causes
// a TagDecl to be passed trough the consumers. This TagDecl is caught twice by
// the ChainedConsumer and cached is the queue of incoming declaration twice.
// If we encounter error the ChainedConsumer shouldn't try to remove the
// declaration twice and this test makes sure of that.
.x CannotDotX.h() // expected-error {{use of undeclared identifier 'CannotDotX'}}
.x CannotDotX.h() // expected-error {{use of undeclared identifier 'CannotDotX'}}
// Uses a bug in the implementation of .L, which must be fixed soon.
// Exposes another issue with the VerifyDiagnosticConsumer in the context of
// cling. The first .L shouldn't cause errors . However when using the
// preprocessor most probably we lose track of the errors source locations
// and files.
.L CannotDotX.h "// expected-error {{redefinition of 'MyClass'}} expected-error {{expected member name or ';' after declaration specifiers}} expected-node {{previous definition is here}}
.L CannotDotX.h
.q

View File

@ -0,0 +1,19 @@
// RUN: cat %s | %cling -Xclang -verify -I%p | FileCheck %s
// Tests the removal of nested decls
struct Outer { struct Inner { enum E{i = 1}; }; };error_here; // expected-error {{error: use of undeclared identifier 'error_here'}}
.rawInput
namespace Outer { struct Inner { enum E{i = 2}; }; };
.rawInput
Outer::Inner::i
// CHECK: (enum Outer::Inner::E const) @0x{{[0-9A-Fa-f]{7,12}.}}
// CHECK: (Outer::Inner::E::i) : (int) 2
enum A{a}; //
enum A{a}; // expected-error {{redefinition of 'A'}} expected-note {{previous definition is here}}
a // expected-error {{use of undeclared identifier 'a'}}
.q

View File

@ -0,0 +1,7 @@
#include <fstream>
#include <string>
void Overloads () {
std::string s;
std::ofstream("file.txt") << s << std::endl;
}

Some files were not shown because too many files have changed in this diff Show More