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:
commit
05ba8a3a07
167
CMakeLists.txt
Normal file
167
CMakeLists.txt
Normal 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()
|
1
LastKnownGoodLLVMSVNRevision.txt
Normal file
1
LastKnownGoodLLVMSVNRevision.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
162891
|
111
Makefile
Normal file
111
Makefile
Normal 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
83
Module.mk
Normal 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
70
README
Normal 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.
|
23
demo/ExpressiveDiagnostics/Ambiguities.C
Normal file
23
demo/ExpressiveDiagnostics/Ambiguities.C
Normal 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() {}
|
10
demo/ExpressiveDiagnostics/CaretDiagnostics.C
Normal file
10
demo/ExpressiveDiagnostics/CaretDiagnostics.C
Normal 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);
|
||||||
|
}
|
13
demo/ExpressiveDiagnostics/FixItHints.C
Normal file
13
demo/ExpressiveDiagnostics/FixItHints.C
Normal 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;
|
||||||
|
}
|
15
demo/ExpressiveDiagnostics/MacroExpansionInformation.C
Normal file
15
demo/ExpressiveDiagnostics/MacroExpansionInformation.C
Normal 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);
|
||||||
|
}
|
12
demo/ExpressiveDiagnostics/PreciseWording.C
Normal file
12
demo/ExpressiveDiagnostics/PreciseWording.C
Normal 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;
|
||||||
|
}
|
20
demo/ExpressiveDiagnostics/RangeHighlight.C
Normal file
20
demo/ExpressiveDiagnostics/RangeHighlight.C
Normal 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
7
demo/README
Normal 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
104
docs/Makefile
Normal 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
1562
docs/doxygen.cfg.in
Normal file
File diff suppressed because it is too large
Load Diff
408
docs/doxygen.css
Normal file
408
docs/doxygen.css
Normal 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
10
docs/doxygen.footer
Normal 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
9
docs/doxygen.header
Normal 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
15
docs/doxygen.intro
Normal 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
256
docs/manpage.css
Normal 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
4
include/Makefile
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
CLING_LEVEL := ..
|
||||||
|
DIRS := cling
|
||||||
|
|
||||||
|
include $(CLING_LEVEL)/Makefile
|
46
include/cling/Interpreter/CIFactory.h
Normal file
46
include/cling/Interpreter/CIFactory.h
Normal 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
|
16
include/cling/Interpreter/CValuePrinter.h
Normal file
16
include/cling/Interpreter/CValuePrinter.h
Normal 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
|
73
include/cling/Interpreter/Callable.h
Normal file
73
include/cling/Interpreter/Callable.h
Normal 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
|
26
include/cling/Interpreter/ClingOptions.h
Normal file
26
include/cling/Interpreter/ClingOptions.h
Normal 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
|
||||||
|
|
||||||
|
|
21
include/cling/Interpreter/ClingOptions.inc
Normal file
21
include/cling/Interpreter/ClingOptions.inc
Normal 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)
|
59
include/cling/Interpreter/DynamicExprInfo.h
Normal file
59
include/cling/Interpreter/DynamicExprInfo.h
Normal 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
|
134
include/cling/Interpreter/DynamicLookupRuntimeUniverse.h
Normal file
134
include/cling/Interpreter/DynamicLookupRuntimeUniverse.h
Normal 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
|
425
include/cling/Interpreter/Interpreter.h
Normal file
425
include/cling/Interpreter/Interpreter.h
Normal 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
|
49
include/cling/Interpreter/InterpreterCallbacks.h
Normal file
49
include/cling/Interpreter/InterpreterCallbacks.h
Normal 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
|
40
include/cling/Interpreter/InvocationOptions.h
Normal file
40
include/cling/Interpreter/InvocationOptions.h
Normal 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
|
84
include/cling/Interpreter/LookupHelper.h
Normal file
84
include/cling/Interpreter/LookupHelper.h
Normal 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
|
104
include/cling/Interpreter/RuntimeUniverse.h
Normal file
104
include/cling/Interpreter/RuntimeUniverse.h
Normal 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__
|
161
include/cling/Interpreter/Value.h
Normal file
161
include/cling/Interpreter/Value.h
Normal 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
|
78
include/cling/Interpreter/ValuePrinter.h
Normal file
78
include/cling/Interpreter/ValuePrinter.h
Normal 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
|
38
include/cling/Interpreter/ValuePrinterInfo.h
Normal file
38
include/cling/Interpreter/ValuePrinterInfo.h
Normal 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
30
include/cling/Makefile
Normal 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
|
151
include/cling/MetaProcessor/MetaProcessor.h
Normal file
151
include/cling/MetaProcessor/MetaProcessor.h
Normal 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
|
36
include/cling/UserInterface/UserInterface.h
Normal file
36
include/cling/UserInterface/UserInterface.h
Normal 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
74
include/cling/Utils/AST.h
Normal 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
|
5
include/cling/cint/multimap
Normal file
5
include/cling/cint/multimap
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#ifdef __CINT__
|
||||||
|
#include <map>
|
||||||
|
#else
|
||||||
|
#error "This header (multimap) is only expected to be used when emulating CINT"
|
||||||
|
#endif
|
5
include/cling/cint/multiset
Normal file
5
include/cling/cint/multiset
Normal 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
4
lib/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
add_subdirectory(Interpreter)
|
||||||
|
add_subdirectory(MetaProcessor)
|
||||||
|
add_subdirectory(UserInterface)
|
||||||
|
add_subdirectory(Utils)
|
52
lib/Interpreter/ASTDumper.cpp
Normal file
52
lib/Interpreter/ASTDumper.cpp
Normal 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
|
39
lib/Interpreter/ASTDumper.h
Normal file
39
lib/Interpreter/ASTDumper.h
Normal 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
|
522
lib/Interpreter/ASTNodeEraser.cpp
Normal file
522
lib/Interpreter/ASTNodeEraser.cpp
Normal 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
|
44
lib/Interpreter/ASTNodeEraser.h
Normal file
44
lib/Interpreter/ASTNodeEraser.h
Normal 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
|
274
lib/Interpreter/CIFactory.cpp
Normal file
274
lib/Interpreter/CIFactory.cpp
Normal 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
|
43
lib/Interpreter/CMakeLists.txt
Normal file
43
lib/Interpreter/CMakeLists.txt
Normal 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)
|
50
lib/Interpreter/Callable.cpp
Normal file
50
lib/Interpreter/Callable.cpp
Normal 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;
|
||||||
|
}
|
52
lib/Interpreter/CompilationOptions.h
Normal file
52
lib/Interpreter/CompilationOptions.h
Normal 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
|
47
lib/Interpreter/DeclCollector.cpp
Normal file
47
lib/Interpreter/DeclCollector.cpp
Normal 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
|
62
lib/Interpreter/DeclCollector.h
Normal file
62
lib/Interpreter/DeclCollector.h
Normal 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
|
561
lib/Interpreter/DeclExtractor.cpp
Normal file
561
lib/Interpreter/DeclExtractor.cpp
Normal 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
|
71
lib/Interpreter/DeclExtractor.h
Normal file
71
lib/Interpreter/DeclExtractor.h
Normal 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
|
30
lib/Interpreter/DynamicExprInfo.cpp
Normal file
30
lib/Interpreter/DynamicExprInfo.cpp
Normal 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
|
881
lib/Interpreter/DynamicLookup.cpp
Normal file
881
lib/Interpreter/DynamicLookup.cpp
Normal 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
|
331
lib/Interpreter/DynamicLookup.h
Normal file
331
lib/Interpreter/DynamicLookup.h
Normal 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
|
274
lib/Interpreter/ExecutionContext.cpp
Normal file
274
lib/Interpreter/ExecutionContext.cpp
Normal 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;
|
||||||
|
}
|
83
lib/Interpreter/ExecutionContext.h
Normal file
83
lib/Interpreter/ExecutionContext.h
Normal 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
|
350
lib/Interpreter/IncrementalParser.cpp
Normal file
350
lib/Interpreter/IncrementalParser.cpp
Normal 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
|
163
lib/Interpreter/IncrementalParser.h
Normal file
163
lib/Interpreter/IncrementalParser.h
Normal 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
|
825
lib/Interpreter/Interpreter.cpp
Normal file
825
lib/Interpreter/Interpreter.cpp
Normal 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
|
107
lib/Interpreter/InvocationOptions.cpp
Normal file
107
lib/Interpreter/InvocationOptions.cpp
Normal 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");
|
||||||
|
|
||||||
|
}
|
1219
lib/Interpreter/LookupHelper.cpp
Normal file
1219
lib/Interpreter/LookupHelper.cpp
Normal file
File diff suppressed because it is too large
Load Diff
28
lib/Interpreter/Makefile
Normal file
28
lib/Interpreter/Makefile
Normal 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
|
||||||
|
|
47
lib/Interpreter/Transaction.cpp
Normal file
47
lib/Interpreter/Transaction.cpp
Normal 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
|
222
lib/Interpreter/Transaction.h
Normal file
222
lib/Interpreter/Transaction.h
Normal 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
|
72
lib/Interpreter/TransactionTransformer.h
Normal file
72
lib/Interpreter/TransactionTransformer.h
Normal 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
|
173
lib/Interpreter/ValuePrinter.cpp
Normal file
173
lib/Interpreter/ValuePrinter.cpp
Normal 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
|
42
lib/Interpreter/ValuePrinterInfo.cpp
Normal file
42
lib/Interpreter/ValuePrinterInfo.cpp
Normal 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
|
252
lib/Interpreter/ValuePrinterSynthesizer.cpp
Normal file
252
lib/Interpreter/ValuePrinterSynthesizer.cpp
Normal 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
|
46
lib/Interpreter/ValuePrinterSynthesizer.h
Normal file
46
lib/Interpreter/ValuePrinterSynthesizer.h
Normal 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
14
lib/Makefile
Normal 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
|
||||||
|
|
7
lib/MetaProcessor/CMakeLists.txt
Normal file
7
lib/MetaProcessor/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
set(LLVM_USED_LIBS
|
||||||
|
clangBasic
|
||||||
|
)
|
||||||
|
|
||||||
|
add_cling_library(clingMetaProcessor
|
||||||
|
MetaProcessor.cpp
|
||||||
|
InputValidator.cpp)
|
56
lib/MetaProcessor/InputValidator.cpp
Normal file
56
lib/MetaProcessor/InputValidator.cpp
Normal 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
|
69
lib/MetaProcessor/InputValidator.h
Normal file
69
lib/MetaProcessor/InputValidator.h
Normal 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
|
22
lib/MetaProcessor/Makefile
Normal file
22
lib/MetaProcessor/Makefile
Normal 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
|
365
lib/MetaProcessor/MetaProcessor.cpp
Normal file
365
lib/MetaProcessor/MetaProcessor.cpp
Normal 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
|
||||||
|
|
53
lib/UserInterface/CMakeLists.txt
Normal file
53
lib/UserInterface/CMakeLists.txt
Normal 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)
|
31
lib/UserInterface/Makefile
Normal file
31
lib/UserInterface/Makefile
Normal 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
|
88
lib/UserInterface/UserInterface.cpp
Normal file
88
lib/UserInterface/UserInterface.cpp
Normal 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
254
lib/Utils/AST.cpp
Normal 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
8
lib/Utils/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
set(LLVM_USED_LIBS
|
||||||
|
clangCodeGen
|
||||||
|
clangBasic
|
||||||
|
)
|
||||||
|
|
||||||
|
add_cling_library(clingUtils
|
||||||
|
AST.cpp
|
||||||
|
)
|
22
lib/Utils/Makefile
Normal file
22
lib/Utils/Makefile
Normal 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
502
patches/clang-Modules.diff
Normal 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.
|
22
patches/clang-parser-private-to-protected.diff
Normal file
22
patches/clang-parser-private-to-protected.diff
Normal 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);
|
21
patches/llvm-test-Makefile.diff
Normal file
21
patches/llvm-test-Makefile.diff
Normal 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
|
||||||
|
|
13
patches/llvm-tools-CMakeLists.txt.diff
Normal file
13
patches/llvm-tools-CMakeLists.txt.diff
Normal 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)
|
14
patches/llvm-tools-Makefile.diff
Normal file
14
patches/llvm-tools-Makefile.diff
Normal 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
95
test/CMakeLists.txt
Normal 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()
|
14
test/CodeUnloading/Simple.C
Normal file
14
test/CodeUnloading/Simple.C
Normal 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
|
3
test/CodeUnloading/dg.exp
Normal file
3
test/CodeUnloading/dg.exp
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
load_lib llvm.exp
|
||||||
|
|
||||||
|
RunLLVMTests [lsort [glob -nocomplain $srcdir/$subdir/*.{ll,c,C,cpp}]]
|
54
test/ErrorRecovery/AnonymousDecls.C
Normal file
54
test/ErrorRecovery/AnonymousDecls.C
Normal 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
|
5
test/ErrorRecovery/CannotDotX.h
Normal file
5
test/ErrorRecovery/CannotDotX.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
extern "C" int printf(const char* fmt, ...);
|
||||||
|
|
||||||
|
class MyClass {
|
||||||
|
MyClass() { printf("MyClass ctor called!\n"); }
|
||||||
|
};
|
5
test/ErrorRecovery/CurrentFailures.C
Normal file
5
test/ErrorRecovery/CurrentFailures.C
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// RUN: cat %s | %cling -I%p | FileCheck %s
|
||||||
|
// XFAIL: *
|
||||||
|
|
||||||
|
#include "Overloads.h"
|
||||||
|
error_here;
|
6
test/ErrorRecovery/CurrentFailures.h
Normal file
6
test/ErrorRecovery/CurrentFailures.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
extern "C" int printf(const char* fmt, ...);
|
||||||
|
|
||||||
|
class MyClass {
|
||||||
|
MyClass() { printf("MyClass ctor called!\n"); }
|
||||||
|
};
|
11
test/ErrorRecovery/MacroExpansion.C
Normal file
11
test/ErrorRecovery/MacroExpansion.C
Normal 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
|
27
test/ErrorRecovery/MetaProcessor.C
Normal file
27
test/ErrorRecovery/MetaProcessor.C
Normal 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
|
19
test/ErrorRecovery/NestedTags.C
Normal file
19
test/ErrorRecovery/NestedTags.C
Normal 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
|
7
test/ErrorRecovery/Overloads.h
Normal file
7
test/ErrorRecovery/Overloads.h
Normal 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
Loading…
Reference in New Issue
Block a user