Browse Source

Merge branch 'develop' into p2p

cl-refactor
subtly 10 years ago
parent
commit
64360b8664
  1. 4
      .gitmodules
  2. 14
      CMakeLists.txt
  3. 4
      README.md
  4. 4
      alethzero/MainWin.cpp
  5. 50
      boost/process.hpp
  6. 200
      boost/process/child.hpp
  7. 41
      boost/process/config.hpp
  8. 209
      boost/process/context.hpp
  9. 406
      boost/process/detail/file_handle.hpp
  10. 187
      boost/process/detail/pipe.hpp
  11. 495
      boost/process/detail/posix_ops.hpp
  12. 176
      boost/process/detail/stream_info.hpp
  13. 231
      boost/process/detail/systembuf.hpp
  14. 355
      boost/process/detail/win32_ops.hpp
  15. 50
      boost/process/environment.hpp
  16. 583
      boost/process/operations.hpp
  17. 116
      boost/process/pistream.hpp
  18. 178
      boost/process/posix_child.hpp
  19. 118
      boost/process/posix_context.hpp
  20. 81
      boost/process/posix_operations.hpp
  21. 128
      boost/process/posix_status.hpp
  22. 117
      boost/process/postream.hpp
  23. 130
      boost/process/process.hpp
  24. 134
      boost/process/self.hpp
  25. 105
      boost/process/status.hpp
  26. 234
      boost/process/stream_behavior.hpp
  27. 128
      boost/process/win32_child.hpp
  28. 61
      boost/process/win32_context.hpp
  29. 77
      boost/process/win32_operations.hpp
  30. 2364
      doc/Doxyfile
  31. 1
      evmjit
  32. 2
      libdevcore/Common.cpp
  33. 2
      libdevcore/CommonIO.cpp
  34. 52
      libdevcore/Diff.h
  35. 3
      libethcore/BlockInfo.cpp
  36. 2
      libethcore/CommonEth.cpp
  37. 8
      libethcore/CommonEth.h
  38. 18
      libethereum/AccountDiff.cpp
  39. 55
      libethereum/AccountDiff.h
  40. 2
      libethereum/State.cpp
  41. 3
      libevm/CMakeLists.txt
  42. 10
      libevm/VMFactory.cpp
  43. 105
      libjsqrc/ethereum.js
  44. 2
      libserpent/compiler.cpp
  45. 1
      libserpent/rewriteutils.cpp
  46. 1
      libserpent/util.cpp
  47. 2
      libsolidity/ExpressionCompiler.cpp
  48. 47
      libsolidity/InterfaceHandler.cpp
  49. 3
      libsolidity/InterfaceHandler.h
  50. 132
      libsolidity/Scanner.cpp
  51. 7
      libsolidity/Scanner.h
  52. 22
      libsolidity/Token.h
  53. 5
      libweb3jsonrpc/WebThreeStubServer.cpp
  54. 1
      libweb3jsonrpc/WebThreeStubServer.h
  55. 16
      libweb3jsonrpc/abstractwebthreestubserver.h
  56. 2
      libweb3jsonrpc/spec.json
  57. 3
      neth/main.cpp
  58. 53
      test/SolidityABIJSON.cpp
  59. 35
      test/SolidityEndToEndTest.cpp
  60. 10
      test/SolidityNameAndTypeResolution.cpp
  61. 29
      test/SolidityNatspecJSON.cpp
  62. 14
      test/SolidityParser.cpp
  63. 54
      test/SolidityScanner.cpp
  64. 19
      test/TestHelper.cpp
  65. 1
      test/TestHelper.h
  66. 22
      test/createRandomTest.cpp
  67. 4
      test/jsonrpc.cpp
  68. 163
      test/stRefundTestFiller.json
  69. 2
      test/state.cpp
  70. 24
      test/vm.cpp

4
.gitmodules

@ -0,0 +1,4 @@
[submodule "evmjit"]
path = evmjit
url = https://github.com/ethereum/evmjit
branch = develop

14
CMakeLists.txt

@ -18,6 +18,7 @@ function(createDefaultCacheConfig)
set(VMTRACE OFF CACHE BOOL "VM tracing and run-time checks (useful for cross-implementation VM debugging)") set(VMTRACE OFF CACHE BOOL "VM tracing and run-time checks (useful for cross-implementation VM debugging)")
set(PARANOIA OFF CACHE BOOL "Additional run-time checks") set(PARANOIA OFF CACHE BOOL "Additional run-time checks")
set(JSONRPC ON CACHE BOOL "Build with jsonprc. default on") set(JSONRPC ON CACHE BOOL "Build with jsonprc. default on")
set(EVMJIT OFF CACHE BOOL "Build a just-in-time compiler for EVM code (requires LLVM)")
endfunction() endfunction()
@ -38,6 +39,10 @@ function(configureProject)
message(FATAL_ERROR "VM tracing requires debug.") message(FATAL_ERROR "VM tracing requires debug.")
endif () endif ()
endif () endif ()
if (EVMJIT)
add_definitions(-DETH_EVMJIT)
endif()
endfunction() endfunction()
@ -84,7 +89,7 @@ cmake_policy(SET CMP0015 NEW)
createDefaultCacheConfig() createDefaultCacheConfig()
configureProject() configureProject()
message("-- VMTRACE: ${VMTRACE}; PARANOIA: ${PARANOIA}; HEADLESS: ${HEADLESS}; JSONRPC: ${JSONRPC}") message("-- VMTRACE: ${VMTRACE}; PARANOIA: ${PARANOIA}; HEADLESS: ${HEADLESS}; JSONRPC: ${JSONRPC}; EVMJIT: ${EVMJIT}")
# Default TARGET_PLATFORM to "linux". # Default TARGET_PLATFORM to "linux".
@ -107,6 +112,13 @@ include(EthExecutableHelper)
createBuildInfo() createBuildInfo()
if (EVMJIT)
# Workaround for Ubuntu broken LLVM package
link_directories(/usr/lib/llvm-3.5/lib)
add_subdirectory(evmjit)
endif()
add_subdirectory(libdevcore) add_subdirectory(libdevcore)
add_subdirectory(libevmcore) add_subdirectory(libevmcore)
add_subdirectory(liblll) add_subdirectory(liblll)

4
README.md

@ -1,5 +1,9 @@
## Ethereum C++ Client. ## Ethereum C++ Client.
[![Build
Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20C%2B%2B%20master%20branch)](http://build.ethdev.com/builders/Linux%20C%2B%2B%20master%20branch/builds/-1) master [![Build
Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20C%2B%2B%20develop%20branch)](http://build.ethdev.com/builders/Linux%20C%2B%2B%20develop%20branch/builds/-1) develop
By Gav Wood, 2014. By Gav Wood, 2014.
[![Build [![Build

4
alethzero/MainWin.cpp

@ -1112,7 +1112,7 @@ string Main::renderDiff(dev::eth::StateDiff const& _d) const
s << "<hr/>"; s << "<hr/>";
dev::eth::AccountDiff const& ad = i.second; dev::eth::AccountDiff const& ad = i.second;
s << "<code style=\"white-space: pre; font-weight: bold\">" << ad.lead() << " </code>" << " <b>" << render(i.first).toStdString() << "</b>"; s << "<code style=\"white-space: pre; font-weight: bold\">" << lead(ad.changeType()) << " </code>" << " <b>" << render(i.first).toStdString() << "</b>";
if (!ad.exist.to()) if (!ad.exist.to())
continue; continue;
@ -1133,7 +1133,7 @@ string Main::renderDiff(dev::eth::StateDiff const& _d) const
s << " (" << ad.code.from().size() << " bytes)"; s << " (" << ad.code.from().size() << " bytes)";
} }
for (pair<u256, dev::eth::Diff<u256>> const& i: ad.storage) for (pair<u256, dev::Diff<u256>> const& i: ad.storage)
{ {
s << "<br/><code style=\"white-space: pre\">"; s << "<br/><code style=\"white-space: pre\">";
if (!i.second.from()) if (!i.second.from())

50
boost/process.hpp

@ -1,50 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process.hpp
*
* Convenience header that includes all other Boost.Process public header
* files. It is important to note that those headers that are specific to
* a given platform are only included if the library is being used in that
* same platform.
*/
#ifndef BOOST_PROCESS_HPP
#define BOOST_PROCESS_HPP
#include <boost/process/config.hpp>
#if defined(BOOST_POSIX_API)
# include <boost/process/posix_child.hpp>
# include <boost/process/posix_context.hpp>
# include <boost/process/posix_operations.hpp>
# include <boost/process/posix_status.hpp>
#elif defined(BOOST_WINDOWS_API)
# include <boost/process/win32_child.hpp>
# include <boost/process/win32_context.hpp>
# include <boost/process/win32_operations.hpp>
#else
# error "Unsupported platform."
#endif
#include <boost/process/child.hpp>
#include <boost/process/context.hpp>
#include <boost/process/environment.hpp>
#include <boost/process/operations.hpp>
#include <boost/process/pistream.hpp>
#include <boost/process/postream.hpp>
#include <boost/process/process.hpp>
#include <boost/process/self.hpp>
#include <boost/process/status.hpp>
#include <boost/process/stream_behavior.hpp>
#endif

200
boost/process/child.hpp

@ -1,200 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/child.hpp
*
* Includes the declaration of the child class.
*/
#ifndef BOOST_PROCESS_CHILD_HPP
#define BOOST_PROCESS_CHILD_HPP
#include <boost/process/config.hpp>
#if defined(BOOST_POSIX_API)
# include <sys/types.h>
# include <sys/wait.h>
# include <cerrno>
#elif defined(BOOST_WINDOWS_API)
# include <windows.h>
#else
# error "Unsupported platform."
#endif
#include <boost/process/process.hpp>
#include <boost/process/pistream.hpp>
#include <boost/process/postream.hpp>
#include <boost/process/status.hpp>
#include <boost/process/detail/file_handle.hpp>
#include <boost/system/system_error.hpp>
#include <boost/throw_exception.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/assert.hpp>
#include <vector>
namespace boost {
namespace process {
/**
* Generic implementation of the Child concept.
*
* The child class implements the Child concept in an operating system
* agnostic way.
*/
class child : public process
{
public:
/**
* Gets a reference to the child's standard input stream.
*
* Returns a reference to a postream object that represents the
* standard input communication channel with the child process.
*/
postream &get_stdin() const
{
BOOST_ASSERT(stdin_);
return *stdin_;
}
/**
* Gets a reference to the child's standard output stream.
*
* Returns a reference to a pistream object that represents the
* standard output communication channel with the child process.
*/
pistream &get_stdout() const
{
BOOST_ASSERT(stdout_);
return *stdout_;
}
/**
* Gets a reference to the child's standard error stream.
*
* Returns a reference to a pistream object that represents the
* standard error communication channel with the child process.
*/
pistream &get_stderr() const
{
BOOST_ASSERT(stderr_);
return *stderr_;
}
/**
* Blocks and waits for the child process to terminate.
*
* Returns a status object that represents the child process'
* finalization condition. The child process object ceases to be
* valid after this call.
*
* \remark Blocking remarks: This call blocks if the child
* process has not finalized execution and waits until
* it terminates.
*/
status wait()
{
#if defined(BOOST_POSIX_API)
int s;
if (::waitpid(get_id(), &s, 0) == -1)
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::child::wait: waitpid(2) failed"));
return status(s);
#elif defined(BOOST_WINDOWS_API)
::WaitForSingleObject(process_handle_.get(), INFINITE);
DWORD code;
if (!::GetExitCodeProcess(process_handle_.get(), &code))
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::child::wait: GetExitCodeProcess failed"));
return status(code);
#endif
}
/**
* Creates a new child object that represents the just spawned child
* process \a id.
*
* The \a fhstdin, \a fhstdout and \a fhstderr file handles represent
* the parent's handles used to communicate with the corresponding
* data streams. They needn't be valid but their availability must
* match the redirections configured by the launcher that spawned this
* process.
*
* The \a fhprocess handle represents a handle to the child process.
* It is only used on Windows as the implementation of wait() needs a
* process handle.
*/
child(id_type id, detail::file_handle fhstdin, detail::file_handle fhstdout, detail::file_handle fhstderr, detail::file_handle fhprocess = detail::file_handle())
: process(id)
#if defined(BOOST_WINDOWS_API)
, process_handle_(fhprocess.release(), ::CloseHandle)
#endif
{
if (fhstdin.valid())
stdin_.reset(new postream(fhstdin));
if (fhstdout.valid())
stdout_.reset(new pistream(fhstdout));
if (fhstderr.valid())
stderr_.reset(new pistream(fhstderr));
}
private:
/**
* The standard input stream attached to the child process.
*
* This postream object holds the communication channel with the
* child's process standard input. It is stored in a pointer because
* this field is only valid when the user requested to redirect this
* data stream.
*/
boost::shared_ptr<postream> stdin_;
/**
* The standard output stream attached to the child process.
*
* This postream object holds the communication channel with the
* child's process standard output. It is stored in a pointer because
* this field is only valid when the user requested to redirect this
* data stream.
*/
boost::shared_ptr<pistream> stdout_;
/**
* The standard error stream attached to the child process.
*
* This postream object holds the communication channel with the
* child's process standard error. It is stored in a pointer because
* this field is only valid when the user requested to redirect this
* data stream.
*/
boost::shared_ptr<pistream> stderr_;
#if defined(BOOST_WINDOWS_API)
/**
* Process handle owned by RAII object.
*/
boost::shared_ptr<void> process_handle_;
#endif
};
/**
* Collection of child objects.
*
* This convenience type represents a collection of child objects backed
* by a vector.
*/
typedef std::vector<child> children;
}
}
#endif

41
boost/process/config.hpp

@ -1,41 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/config.hpp
*
* Defines macros that are used by the library's code to determine the
* operating system it is running under and the features it supports.
*/
#ifndef BOOST_PROCESS_CONFIG_HPP
#define BOOST_PROCESS_CONFIG_HPP
#include <boost/config.hpp>
#include <boost/system/config.hpp>
#if defined(BOOST_POSIX_API) || defined(BOOST_PROCESS_DOXYGEN)
# if !defined(BOOST_PROCESS_POSIX_PATH_MAX)
/**
* The macro BOOST_PROCESS_POSIX_PATH_MAX is set to a positive integer
* value which specifies the system's maximal supported path length.
* By default it is set to 259. You should set the macro to PATH_MAX
* which should be defined in limits.h provided by your operating system
* if you experience problems when instantiating a context. The
* constructor of basic_work_directory_context tries to find out
* dynamically the maximal supported path length but uses
* BOOST_PROCESS_POSIX_PATH_MAX if it fails.
*/
# define BOOST_PROCESS_POSIX_PATH_MAX 259
# endif
#endif
#endif

209
boost/process/context.hpp

@ -1,209 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/context.hpp
*
* Includes the declaration of the context class and several accessory
* base classes.
*/
#ifndef BOOST_PROCESS_CONTEXT_HPP
#define BOOST_PROCESS_CONTEXT_HPP
#include <boost/process/config.hpp>
#if defined(BOOST_POSIX_API)
# include <boost/scoped_array.hpp>
# include <cerrno>
# include <unistd.h>
#elif defined(BOOST_WINDOWS_API)
# include <windows.h>
#else
# error "Unsupported platform."
#endif
#include <boost/process/environment.hpp>
#include <boost/process/stream_behavior.hpp>
#include <boost/system/system_error.hpp>
#include <boost/throw_exception.hpp>
#include <boost/assert.hpp>
#include <string>
#include <vector>
namespace boost {
namespace process {
/**
* Base context class that defines the child's work directory.
*
* Base context class that defines the necessary fields to configure a
* child's work directory. This class is useless on its own because no
* function in the library will accept it as a valid Context
* implementation.
*/
template <class Path>
class basic_work_directory_context
{
public:
/**
* Constructs a new work directory context.
*
* Constructs a new work directory context making the work directory
* described by the new object point to the caller's current working
* directory.
*/
basic_work_directory_context()
{
#if defined(BOOST_POSIX_API)
errno = 0;
long size = ::pathconf(".", _PC_PATH_MAX);
if (size == -1 && errno)
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::basic_work_directory_context::basic_work_directory_context: pathconf(2) failed"));
else if (size == -1)
size = BOOST_PROCESS_POSIX_PATH_MAX;
boost::scoped_array<char> cwd(new char[size]);
if (!::getcwd(cwd.get(), size))
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::basic_work_directory_context::basic_work_directory_context: getcwd(2) failed"));
work_directory = cwd.get();
#elif defined(BOOST_WINDOWS_API)
char cwd[MAX_PATH];
if (!::GetCurrentDirectoryA(sizeof(cwd), cwd))
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::basic_work_directory_context::basic_work_directory_context: GetCurrentDirectory failed"));
work_directory = cwd;
#endif
BOOST_ASSERT(!work_directory.empty());
}
/**
* The process' initial work directory.
*
* The work directory is the directory in which the process starts
* execution.
*/
Path work_directory;
};
/**
* Base context class that defines the child's environment.
*
* Base context class that defines the necessary fields to configure a
* child's environment variables. This class is useless on its own
* because no function in the library will accept it as a valid Context
* implementation.
*/
class environment_context
{
public:
/**
* The process' environment.
*
* Contains the list of environment variables, alongside with their
* values, that will be passed to the spawned child process.
*/
boost::process::environment environment;
};
/**
* Process startup execution context.
*
* The context class groups all the parameters needed to configure a
* process' environment during its creation.
*/
template <class Path>
class basic_context : public basic_work_directory_context<Path>, public environment_context
{
public:
/**
* Child's stdin behavior.
*/
stream_behavior stdin_behavior;
/**
* Child's stdout behavior.
*/
stream_behavior stdout_behavior;
/**
* Child's stderr behavior.
*/
stream_behavior stderr_behavior;
};
typedef basic_context<std::string> context;
/**
* Represents a child process in a pipeline.
*
* This convenience class is a triplet that holds all the data required
* to spawn a new child process in a pipeline.
*/
template <class Executable, class Arguments, class Context>
class basic_pipeline_entry
{
public:
/**
* The executable to launch.
*/
Executable executable;
/**
* The set of arguments to pass to the executable.
*/
Arguments arguments;
/**
* The child's execution context.
*/
Context context;
/**
* The type of the Executable concept used in this template
* instantiation.
*/
typedef Executable executable_type;
/**
* The type of the Arguments concept used in this template
* instantiation.
*/
typedef Arguments arguments_type;
/**
* The type of the Context concept used in this template
* instantiation.
*/
typedef Context context_type;
/**
* Constructs a new pipeline_entry object.
*
* Given the executable, set of arguments and execution triplet,
* constructs a new pipeline_entry object that holds the three
* values.
*/
basic_pipeline_entry(const Executable &exe, const Arguments &args, const Context &ctx)
: executable(exe),
arguments(args),
context(ctx)
{
}
};
/**
* Default instantiation of basic_pipeline_entry.
*/
typedef basic_pipeline_entry<std::string, std::vector<std::string>, context> pipeline_entry;
}
}
#endif

406
boost/process/detail/file_handle.hpp

@ -1,406 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/detail/file_handle.hpp
*
* Includes the declaration of the file_handle class. This file is for
* internal usage only and must not be included by the library user.
*/
#ifndef BOOST_PROCESS_DETAIL_FILE_HANDLE_HPP
#define BOOST_PROCESS_DETAIL_FILE_HANDLE_HPP
#include <boost/process/config.hpp>
#if defined(BOOST_POSIX_API)
# include <cerrno>
# include <unistd.h>
#elif defined(BOOST_WINDOWS_API)
# include <windows.h>
#else
# error "Unsupported platform."
#endif
#include <boost/assert.hpp>
#include <boost/system/system_error.hpp>
#include <boost/throw_exception.hpp>
namespace boost {
namespace process {
namespace detail {
/**
* Simple RAII model for system file handles.
*
* The \a file_handle class is a simple RAII model for native system file
* handles. This class wraps one of such handles grabbing its ownership,
* and automaticaly closes it upon destruction. It is basically used
* inside the library to avoid leaking open file handles, shall an
* unexpected execution trace occur.
*
* A \a file_handle object can be copied but doing so invalidates the
* source object. There can only be a single valid \a file_handle object
* for a given system file handle. This is similar to std::auto_ptr's
* semantics.
*
* This class also provides some convenience methods to issue special file
* operations under their respective platforms.
*/
class file_handle
{
public:
#if defined(BOOST_PROCESS_DOXYGEN)
/**
* Opaque name for the native handle type.
*
* Each operating system identifies file handles using a specific type.
* The \a handle_type type is used to transparently refer to file
* handles regarless of the operating system in which this class is
* used.
*
* If this class is used on a POSIX system, \a NativeSystemHandle is
* an integer type while it is a \a HANDLE on a Windows system.
*/
typedef NativeSystemHandle handle_type;
#elif defined(BOOST_POSIX_API)
typedef int handle_type;
#elif defined(BOOST_WINDOWS_API)
typedef HANDLE handle_type;
#endif
/**
* Constructs an invalid file handle.
*
* This constructor creates a new \a file_handle object that represents
* an invalid file handle. An invalid file handle can be copied but
* cannot be manipulated in any way (except checking for its validity).
*
* \see valid()
*/
file_handle()
: handle_(invalid_value())
{
}
/**
* Constructs a new file handle from a native file handle.
*
* This constructor creates a new \a file_handle object that takes
* ownership of the given \a h native file handle. The user must not
* close \a h on his own during the lifetime of the new object.
* Ownership can be reclaimed using release().
*
* \pre The native file handle must be valid; a close operation must
* succeed on it.
* \see release()
*/
file_handle(handle_type h)
: handle_(h)
{
BOOST_ASSERT(handle_ != invalid_value());
}
/**
* Copy constructor; invalidates the source handle.
*
* This copy constructor creates a new file handle from a given one.
* Ownership of the native file handle is transferred to the new
* object, effectively invalidating the source file handle. This
* avoids having two live \a file_handle objects referring to the
* same native file handle. The source file handle needs not be
* valid in the name of simplicity.
*
* \post The source file handle is invalid.
* \post The new file handle owns the source's native file handle.
*/
file_handle(const file_handle &fh)
: handle_(fh.handle_)
{
fh.handle_ = invalid_value();
}
/**
* Releases resources if the handle is valid.
*
* If the file handle is valid, the destructor closes it.
*
* \see valid()
*/
~file_handle()
{
if (valid())
close();
}
/**
* Assignment operator; invalidates the source handle.
*
* This assignment operator transfers ownership of the RHS file
* handle to the LHS one, effectively invalidating the source file
* handle. This avoids having two live \a file_handle objects
* referring to the same native file handle. The source file
* handle needs not be valid in the name of simplicity.
*
* \post The RHS file handle is invalid.
* \post The LHS file handle owns RHS' native file handle.
* \return A reference to the LHS file handle.
*/
file_handle &operator=(const file_handle &fh)
{
handle_ = fh.handle_;
fh.handle_ = invalid_value();
return *this;
}
/**
* Checks whether the file handle is valid or not.
*
* Returns a boolean indicating whether the file handle is valid or
* not. If the file handle is invalid, no other methods can be
* executed other than the destructor.
*
* \return true if the file handle is valid; false otherwise.
*/
bool valid() const
{
return handle_ != invalid_value();
}
/**
* Closes the file handle.
*
* Explicitly closes the file handle, which must be valid. Upon
* exit, the handle is not valid any more.
*
* \pre The file handle is valid.
* \post The file handle is invalid.
* \post The native file handle is closed.
*/
void close()
{
BOOST_ASSERT(valid());
#if defined(BOOST_POSIX_API)
::close(handle_);
#elif defined(BOOST_WINDOWS_API)
::CloseHandle(handle_);
#endif
handle_ = invalid_value();
}
/**
* Reclaims ownership of the native file handle.
*
* Explicitly reclaims ownership of the native file handle contained
* in the \a file_handle object, returning the native file handle.
* The caller is responsible of closing it later on.
*
* \pre The file handle is valid.
* \post The file handle is invalid.
* \return The native file handle.
*/
handle_type release()
{
BOOST_ASSERT(valid());
handle_type h = handle_;
handle_ = invalid_value();
return h;
}
/**
* Gets the native file handle.
*
* Returns the native file handle for the \a file_handle object.
* The caller can issue any operation on it except closing it.
* If closing is required, release() shall be used.
*
* \pre The file handle is valid.
* \post The file handle is valid.
* \return The native file handle.
*/
handle_type get() const
{
BOOST_ASSERT(valid());
return handle_;
}
#if defined(BOOST_POSIX_API) || defined(BOOST_PROCESS_DOXYGEN)
/**
* Changes the native file handle to the given one.
*
* Given a new native file handle \a h, this operation assigns this
* handle to the current object, closing its old native file handle.
* In other words, it first calls dup2() to remap the old handle to
* the new one and then closes the old handle.
*
* If \a h is open, it is automatically closed by dup2().
*
* This operation is only available in POSIX systems.
*
* \pre The file handle is valid.
* \pre The native file handle \a h is valid; i.e., it must be
* closeable.
* \post The file handle's native file handle is \a h.
* \throw boost::system::system_error If the internal remapping
* operation fails.
*/
void posix_remap(handle_type h)
{
BOOST_ASSERT(valid());
if (::dup2(handle_, h) == -1)
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::file_handle::posix_remap: dup2(2) failed"));
if (::close(handle_) == -1)
{
::close(h);
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::file_handle::posix_remap: close(2) failed"));
}
handle_ = h;
}
/**
* Duplicates an open native file handle.
*
* Given a native file handle \a h1, this routine duplicates it so
* that it ends up being identified by the native file handle \a h2
* and returns a new \a file_handle owning \a h2.
*
* This operation is only available in POSIX systems.
*
* \pre The native file handle \a h1 is open.
* \pre The native file handle \a h2 is valid (non-negative).
* \post The native file handle \a h1 is closed.
* \post The native file handle \a h2 is the same as the old \a h1
* from the operating system's point of view.
* \return A new \a file_handle object that owns \a h2.
* \throw boost::system::system_error If dup2() fails.
*/
static file_handle posix_dup(int h1, int h2)
{
if (::dup2(h1, h2) == -1)
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::file_handle::posix_dup: dup2(2) failed"));
return file_handle(h2);
}
#endif
#if defined(BOOST_WINDOWS_API) || defined(BOOST_PROCESS_DOXYGEN)
/**
* Duplicates the \a h native file handle.
*
* Given a native file handle \a h, this routine constructs a new
* \a file_handle object that owns a new duplicate of \a h. The
* duplicate's inheritable flag is set to the value of \a inheritable.
*
* This operation is only available in Windows systems.
*
* \pre The native file handle \a h is valid.
* \return A file handle owning a duplicate of \a h.
* \throw boost::system::system_error If DuplicateHandle() fails.
*/
static file_handle win32_dup(HANDLE h, bool inheritable)
{
HANDLE h2;
if (!::DuplicateHandle(::GetCurrentProcess(), h, ::GetCurrentProcess(), &h2, 0, inheritable ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::detail::file_handle::win32_dup: DuplicateHandle failed"));
return file_handle(h2);
}
/**
* Creates a new duplicate of a standard file handle.
*
* Constructs a new \a file_handle object that owns a duplicate of a
* standard file handle. The \a d parameter specifies which standard
* file handle to duplicate and can be one of \a STD_INPUT_HANDLE,
* \a STD_OUTPUT_HANDLE or \a STD_ERROR_HANDLE. The duplicate's
* inheritable flag is set to the value of \a inheritable.
*
* This operation is only available in Windows systems.
*
* \pre \a d refers to one of the standard handles as described above.
* \return A file handle owning a duplicate of the standard handle
* referred to by \a d.
* \throw boost::system::system_error If GetStdHandle() or
* DuplicateHandle() fails.
*/
static file_handle win32_std(DWORD d, bool inheritable)
{
BOOST_ASSERT(d == STD_INPUT_HANDLE || d == STD_OUTPUT_HANDLE || d == STD_ERROR_HANDLE);
HANDLE h = ::GetStdHandle(d);
if (h == INVALID_HANDLE_VALUE)
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::detail::file_handle::win32_std: GetStdHandle failed"));
return win32_dup(h, inheritable);
}
/**
* Changes the file handle's inheritable flag.
*
* Changes the file handle's inheritable flag to \a i. It is not
* necessary for the file handle's flag to be different than \a i.
*
* This operation is only available in Windows systems.
*
* \pre The file handle is valid.
* \post The native file handle's inheritable flag is set to \a i.
* \throw boost::system::system_error If the property change fails.
*/
void win32_set_inheritable(bool i)
{
BOOST_ASSERT(valid());
if (!::SetHandleInformation(handle_, HANDLE_FLAG_INHERIT, i ? HANDLE_FLAG_INHERIT : 0))
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::detail::file_handle::win32_set_inheritable: SetHandleInformation failed"));
}
#endif
private:
/**
* Internal handle value.
*
* This variable holds the native handle value for the file handle
* hold by this object. It is interesting to note that this needs
* to be mutable because the copy constructor and the assignment
* operator invalidate the source object.
*/
mutable handle_type handle_;
/**
* Constant function representing an invalid handle value.
*
* Returns the platform-specific handle value that represents an
* invalid handle. This is a constant function rather than a regular
* constant because, in the latter case, we cannot define it under
* Windows due to the value being of a complex type.
*/
static handle_type invalid_value()
{
#if defined(BOOST_POSIX_API)
return -1;
#elif defined(BOOST_WINDOWS_API)
return INVALID_HANDLE_VALUE;
#endif
}
};
}
}
}
#endif

187
boost/process/detail/pipe.hpp

@ -1,187 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/detail/pipe.hpp
*
* Includes the declaration of the pipe class. This file is for
* internal usage only and must not be included by the library user.
*/
#ifndef BOOST_PROCESS_DETAIL_PIPE_HPP
#define BOOST_PROCESS_DETAIL_PIPE_HPP
#include <boost/process/config.hpp>
#if defined(BOOST_POSIX_API)
# include <unistd.h>
# include <cerrno>
#elif defined(BOOST_WINDOWS_API)
# if defined(BOOST_PROCESS_WINDOWS_USE_NAMED_PIPE)
# include <boost/lexical_cast.hpp>
# include <string>
# endif
# include <windows.h>
#else
# error "Unsupported platform."
#endif
#include <boost/process/detail/file_handle.hpp>
#include <boost/system/system_error.hpp>
#include <boost/throw_exception.hpp>
namespace boost {
namespace process {
namespace detail {
/**
* Simple RAII model for anonymous pipes.
*
* The pipe class is a simple RAII model for anonymous pipes. It
* provides a portable constructor that allocates a new %pipe and creates
* a pipe object that owns the two file handles associated to it: the
* read end and the write end.
*
* These handles can be retrieved for modification according to
* file_handle semantics. Optionally, their ownership can be transferred
* to external \a file_handle objects which comes handy when the two
* ends need to be used in different places (i.e. after a POSIX fork()
* system call).
*
* Pipes can be copied following the same semantics as file handles.
* In other words, copying a %pipe object invalidates the source one.
*
* \see file_handle
*/
class pipe
{
public:
/**
* Creates a new %pipe.
*
* The default pipe constructor allocates a new anonymous %pipe
* and assigns its ownership to the created pipe object. On Windows
* when the macro BOOST_PROCESS_WINDOWS_USE_NAMED_PIPE is defined
* a named pipe is created. This is required if asynchronous I/O
* should be used as asynchronous I/O is only supported by named
* pipes on Windows.
*
* \throw boost::system::system_error If the anonymous %pipe
* creation fails.
*/
pipe()
{
file_handle::handle_type hs[2];
#if defined(BOOST_POSIX_API)
if (::pipe(hs) == -1)
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::pipe::pipe: pipe(2) failed"));
#elif defined(BOOST_WINDOWS_API)
SECURITY_ATTRIBUTES sa;
ZeroMemory(&sa, sizeof(sa));
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = FALSE;
# if defined(BOOST_PROCESS_WINDOWS_USE_NAMED_PIPE)
static unsigned int nextid = 0;
std::string pipe = "\\\\.\\pipe\\boost_process_" + boost::lexical_cast<std::string>(::GetCurrentProcessId()) + "_" + boost::lexical_cast<std::string>(nextid++);
hs[0] = ::CreateNamedPipeA(pipe.c_str(), PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, 0, 1, 8192, 8192, 0, &sa);
if (hs[0] == INVALID_HANDLE_VALUE)
boost::throw_exception(boost::system::system_error(::GetLastError(), boost::system::system_category(), "boost::process::detail::pipe::pipe: CreateNamedPipe failed"));
hs[1] = ::CreateFileA(pipe.c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (hs[1] == INVALID_HANDLE_VALUE)
boost::throw_exception(boost::system::system_error(::GetLastError(), boost::system::system_category(), "boost::process::detail::pipe::pipe: CreateFile failed"));
OVERLAPPED overlapped;
ZeroMemory(&overlapped, sizeof(overlapped));
overlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
if (!overlapped.hEvent)
boost::throw_exception(boost::system::system_error(::GetLastError(), boost::system::system_category(), "boost::process::detail::pipe::pipe: CreateEvent failed"));
BOOL b = ::ConnectNamedPipe(hs[0], &overlapped);
if (!b)
{
if (::GetLastError() == ERROR_IO_PENDING)
{
if (::WaitForSingleObject(overlapped.hEvent, INFINITE) == WAIT_FAILED)
{
::CloseHandle(overlapped.hEvent);
boost::throw_exception(boost::system::system_error(::GetLastError(), boost::system::system_category(), "boost::process::detail::pipe::pipe: WaitForSingleObject failed"));
}
}
else if (::GetLastError() != ERROR_PIPE_CONNECTED)
{
::CloseHandle(overlapped.hEvent);
boost::throw_exception(boost::system::system_error(::GetLastError(), boost::system::system_category(), "boost::process::detail::pipe::pipe: ConnectNamedPipe failed"));
}
}
::CloseHandle(overlapped.hEvent);
# else
if (!::CreatePipe(&hs[0], &hs[1], &sa, 0))
boost::throw_exception(boost::system::system_error(::GetLastError(), boost::system::system_category(), "boost::process::detail::pipe::pipe: CreatePipe failed"));
# endif
#endif
read_end_ = file_handle(hs[0]);
write_end_ = file_handle(hs[1]);
}
/**
* Returns the %pipe's read end file handle.
*
* Obtains a reference to the %pipe's read end file handle. Care
* should be taken to not duplicate the returned object if ownership
* shall remain to the %pipe.
*
* Duplicating the returned object invalidates its corresponding file
* handle in the %pipe.
*
* \return A reference to the %pipe's read end file handle.
*/
file_handle &rend()
{
return read_end_;
}
/**
* Returns the %pipe's write end file handle.
*
* Obtains a reference to the %pipe's write end file handle. Care
* should be taken to not duplicate the returned object if ownership
* shall remain to the %pipe.
*
* Duplicating the returned object invalidates its corresponding file
* handle in the %pipe.
*
* \return A reference to the %pipe's write end file handle.
*/
file_handle &wend()
{
return write_end_;
}
private:
/**
* The %pipe's read end file handle.
*/
file_handle read_end_;
/**
* The %pipe's write end file handle.
*/
file_handle write_end_;
};
}
}
}
#endif

495
boost/process/detail/posix_ops.hpp

@ -1,495 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/detail/posix_ops.hpp
*
* Provides some convenience functions to start processes under POSIX
* operating systems.
*/
#ifndef BOOST_PROCESS_DETAIL_POSIX_OPS_HPP
#define BOOST_PROCESS_DETAIL_POSIX_OPS_HPP
#include <boost/process/environment.hpp>
#include <boost/process/detail/file_handle.hpp>
#include <boost/process/detail/pipe.hpp>
#include <boost/process/detail/stream_info.hpp>
#include <boost/scoped_array.hpp>
#include <boost/assert.hpp>
#include <boost/system/system_error.hpp>
#include <boost/throw_exception.hpp>
#include <map>
#include <utility>
#include <string>
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>
namespace boost {
namespace process {
namespace detail {
/**
* Converts the command line to an array of C strings.
*
* Converts the command line's list of arguments to the format expected
* by the \a argv parameter in the POSIX execve() system call.
*
* This operation is only available on POSIX systems.
*
* \return The first argument of the pair is an integer that indicates
* how many strings are stored in the second argument. The
* second argument is a NULL-terminated, dynamically allocated
* array of dynamically allocated strings holding the arguments
* to the executable. The caller is responsible of freeing them.
*/
template <class Arguments>
inline std::pair<std::size_t, char**> collection_to_posix_argv(const Arguments &args)
{
std::size_t nargs = args.size();
BOOST_ASSERT(nargs > 0);
char **argv = new char*[nargs + 1];
typename Arguments::size_type i = 0;
for (typename Arguments::const_iterator it = args.begin(); it != args.end(); ++it)
{
argv[i] = new char[it->size() + 1];
std::strncpy(argv[i], it->c_str(), it->size() + 1);
++i;
}
argv[nargs] = 0;
return std::pair<std::size_t, char**>(nargs, argv);
}
/**
* Converts an environment to a char** table as used by execve().
*
* Converts the environment's contents to the format used by the
* execve() system call. The returned char** array is allocated
* in dynamic memory; the caller must free it when not used any
* more. Each entry is also allocated in dynamic memory and is a
* NULL-terminated string of the form var=value; these must also be
* released by the caller.
*
* \return A dynamically allocated char** array that represents
* the environment's content. Each array entry is a
* NULL-terminated string of the form var=value.
*/
inline char **environment_to_envp(const environment &env)
{
char **envp = new char*[env.size() + 1];
environment::size_type i = 0;
for (environment::const_iterator it = env.begin(); it != env.end(); ++it)
{
std::string s = it->first + "=" + it->second;
envp[i] = new char[s.size() + 1];
std::strncpy(envp[i], s.c_str(), s.size() + 1);
++i;
}
envp[i] = 0;
return envp;
}
/**
* Holds a mapping between native file descriptors and their corresponding
* pipes to set up communication between the parent and the %child process.
*/
typedef std::map<int, stream_info> info_map;
/**
* Helper class to configure a POSIX %child.
*
* This helper class is used to hold all the attributes that configure a
* new POSIX %child process and to centralize all the actions needed to
* make them effective.
*
* All its fields are public for simplicity. It is only intended for
* internal use and it is heavily coupled with the Context
* implementations.
*/
struct posix_setup
{
/**
* The work directory.
*
* This string specifies the directory in which the %child process
* starts execution. It cannot be empty.
*/
std::string work_directory;
/**
* The chroot directory, if any.
*
* Specifies the directory in which the %child process is chrooted
* before execution. Empty if this feature is not desired.
*/
std::string chroot;
/**
* The user credentials.
*
* UID that specifies the user credentials to use to run the %child
* process. Defaults to the current UID.
*/
uid_t uid;
/**
* The effective user credentials.
*
* EUID that specifies the effective user credentials to use to run
* the %child process. Defaults to the current EUID.
*/
uid_t euid;
/**
* The group credentials.
*
* GID that specifies the group credentials to use to run the %child
* process. Defaults to the current GID.
*/
gid_t gid;
/**
* The effective group credentials.
*
* EGID that specifies the effective group credentials to use to run
* the %child process. Defaults to the current EGID.
*/
gid_t egid;
/**
* Creates a new properties set.
*
* Creates a new object that has sensible default values for all the
* properties.
*/
posix_setup()
: uid(::getuid()),
euid(::geteuid()),
gid(::getgid()),
egid(::getegid())
{
}
/**
* Sets up the execution environment.
*
* Modifies the current execution environment (that of the %child) so
* that the properties become effective.
*
* \throw boost::system::system_error If any error ocurred during
* environment configuration. The child process should abort
* execution if this happens because its start conditions
* cannot be met.
*/
void operator()() const
{
if (!chroot.empty() && ::chroot(chroot.c_str()) == -1)
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_setup: chroot(2) failed"));
if (gid != ::getgid() && ::setgid(gid) == -1)
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_setup: setgid(2) failed"));
if (egid != ::getegid() && ::setegid(egid) == -1)
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_setup: setegid(2) failed"));
if (uid != ::getuid() && ::setuid(uid) == -1)
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_setup: setuid(2) failed"));
if (euid != ::geteuid() && ::seteuid(euid) == -1)
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_setup: seteuid(2) failed"));
BOOST_ASSERT(!work_directory.empty());
if (::chdir(work_directory.c_str()) == -1)
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_setup: chdir(2) failed"));
}
};
/**
* Configures child process' input streams.
*
* Sets up the current process' input streams to behave according to the
* information in the \a info map. \a closeflags is modified to reflect
* those descriptors that should not be closed because they where modified
* by the function.
*
* Modifies the current execution environment, so this should only be run
* on the child process after the fork(2) has happened.
*
* \throw boost::system::system_error If any error occurs during the
* configuration.
*/
inline void setup_input(info_map &info, bool *closeflags, int maxdescs)
{
for (info_map::iterator it = info.begin(); it != info.end(); ++it)
{
int d = it->first;
stream_info &si = it->second;
BOOST_ASSERT(d < maxdescs);
closeflags[d] = false;
switch (si.type_)
{
case stream_info::use_file:
{
int fd = ::open(si.file_.c_str(), O_RDONLY);
if (fd == -1)
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::setup_input: open(2) of " + si.file_ + " failed"));
if (fd != d)
{
file_handle h(fd);
h.posix_remap(d);
h.release();
}
break;
}
case stream_info::use_handle:
{
if (si.handle_.get() != d)
si.handle_.posix_remap(d);
break;
}
case stream_info::use_pipe:
{
si.pipe_->wend().close();
if (d != si.pipe_->rend().get())
si.pipe_->rend().posix_remap(d);
break;
}
default:
{
BOOST_ASSERT(si.type_ == stream_info::inherit);
break;
}
}
}
}
/**
* Configures child process' output streams.
*
* Sets up the current process' output streams to behave according to the
* information in the \a info map. \a closeflags is modified to reflect
* those descriptors that should not be closed because they where
* modified by the function.
*
* Modifies the current execution environment, so this should only be run
* on the child process after the fork(2) has happened.
*
* \throw boost::system::system_error If any error occurs during the
* configuration.
*/
inline void setup_output(info_map &info, bool *closeflags, int maxdescs)
{
for (info_map::iterator it = info.begin(); it != info.end(); ++it)
{
int d = it->first;
stream_info &si = it->second;
BOOST_ASSERT(d < maxdescs);
closeflags[d] = false;
switch (si.type_)
{
case stream_info::redirect:
{
break;
}
case stream_info::use_file:
{
int fd = ::open(si.file_.c_str(), O_WRONLY);
if (fd == -1)
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::setup_output: open(2) of " + si.file_ + " failed"));
if (fd != d)
{
file_handle h(fd);
h.posix_remap(d);
h.release();
}
break;
}
case stream_info::use_handle:
{
if (si.handle_.get() != d)
si.handle_.posix_remap(d);
break;
}
case stream_info::use_pipe:
{
si.pipe_->rend().close();
if (d != si.pipe_->wend().get())
si.pipe_->wend().posix_remap(d);
break;
}
default:
{
BOOST_ASSERT(si.type_ == stream_info::inherit);
break;
}
}
}
for (info_map::const_iterator it = info.begin(); it != info.end(); ++it)
{
int d = it->first;
const stream_info &si = it->second;
if (si.type_ == stream_info::redirect)
file_handle::posix_dup(si.desc_to_, d).release();
}
}
/**
* Starts a new child process in a POSIX operating system.
*
* This helper functions is provided to simplify the Context's task when
* it comes to starting up a new process in a POSIX operating system.
* The function hides all the details of the fork/exec pair of calls as
* well as all the setup of communication pipes and execution environment.
*
* \param exe The executable to spawn the child process.
* \param args The arguments for the executable.
* \param env The environment variables that the new child process
* receives.
* \param infoin A map describing all input file descriptors to be
* redirected.
* \param infoout A map describing all output file descriptors to be
* redirected.
* \param setup A helper object used to configure the child's execution
* environment.
* \return The new process' PID. The caller is responsible of creating
* an appropriate Child representation for it.
*/
template <class Executable, class Arguments>
inline pid_t posix_start(const Executable &exe, const Arguments &args, const environment &env, info_map &infoin, info_map &infoout, const posix_setup &setup)
{
pid_t pid = ::fork();
if (pid == -1)
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_start: fork(2) failed"));
else if (pid == 0)
{
#if defined(F_MAXFD)
int maxdescs = ::fcntl(-1, F_MAXFD, 0);
if (maxdescs == -1)
maxdescs = ::sysconf(_SC_OPEN_MAX);
#else
int maxdescs = ::sysconf(_SC_OPEN_MAX);
#endif
if (maxdescs == -1)
maxdescs = 1024;
try
{
boost::scoped_array<bool> closeflags(new bool[maxdescs]);
for (int i = 0; i < maxdescs; ++i)
closeflags[i] = true;
setup_input(infoin, closeflags.get(), maxdescs);
setup_output(infoout, closeflags.get(), maxdescs);
for (int i = 0; i < maxdescs; ++i)
{
if (closeflags[i])
::close(i);
}
setup();
}
catch (const boost::system::system_error &e)
{
::write(STDERR_FILENO, e.what(), std::strlen(e.what()));
::write(STDERR_FILENO, "\n", 1);
std::exit(EXIT_FAILURE);
}
std::pair<std::size_t, char**> argcv = collection_to_posix_argv(args);
char **envp = environment_to_envp(env);
::execve(exe.c_str(), argcv.second, envp);
boost::system::system_error e(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_start: execve(2) failed");
for (std::size_t i = 0; i < argcv.first; ++i)
delete[] argcv.second[i];
delete[] argcv.second;
for (std::size_t i = 0; i < env.size(); ++i)
delete[] envp[i];
delete[] envp;
::write(STDERR_FILENO, e.what(), std::strlen(e.what()));
::write(STDERR_FILENO, "\n", 1);
std::exit(EXIT_FAILURE);
}
BOOST_ASSERT(pid > 0);
for (info_map::iterator it = infoin.begin(); it != infoin.end(); ++it)
{
stream_info &si = it->second;
if (si.type_ == stream_info::use_pipe)
si.pipe_->rend().close();
}
for (info_map::iterator it = infoout.begin(); it != infoout.end(); ++it)
{
stream_info &si = it->second;
if (si.type_ == stream_info::use_pipe)
si.pipe_->wend().close();
}
return pid;
}
/**
* Locates a communication pipe and returns one of its endpoints.
*
* Given a \a info map, and a file descriptor \a desc, searches for its
* communicataion pipe in the map and returns one of its endpoints as
* indicated by the \a out flag. This is intended to be used by a
* parent process after a fork(2) call.
*
* \pre If the info map contains the given descriptor, it is configured
* to use a pipe.
* \post The info map does not contain the given descriptor.
* \return If the file descriptor is found in the map, returns the pipe's
* read end if out is true; otherwise its write end. If the
* descriptor is not found returns an invalid file handle.
*/
inline file_handle posix_info_locate_pipe(info_map &info, int desc, bool out)
{
file_handle fh;
info_map::iterator it = info.find(desc);
if (it != info.end())
{
stream_info &si = it->second;
if (si.type_ == stream_info::use_pipe)
{
fh = out ? si.pipe_->rend().release() : si.pipe_->wend().release();
BOOST_ASSERT(fh.valid());
}
info.erase(it);
}
return fh;
}
}
}
}
#endif

176
boost/process/detail/stream_info.hpp

@ -1,176 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/detail/stream_info.hpp
*
* Provides the definition of the stream_info structure.
*/
#ifndef BOOST_PROCESS_DETAIL_STREAM_INFO_HPP
#define BOOST_PROCESS_DETAIL_STREAM_INFO_HPP
#include <boost/process/config.hpp>
#if defined(BOOST_POSIX_API)
# include <unistd.h>
#elif defined(BOOST_WINDOWS_API)
#else
# error "Unsupported platform."
#endif
#include <boost/process/stream_behavior.hpp>
#include <boost/process/detail/file_handle.hpp>
#include <boost/process/detail/pipe.hpp>
#include <boost/optional.hpp>
#include <boost/assert.hpp>
#include <string>
namespace boost {
namespace process {
namespace detail {
/**
* Configuration data for a file descriptor.
*
* This convenience structure provides a compact way to pass information
* around on how to configure a file descriptor. It is a lower-level
* representation of stream_behavior, as it can hold the same information
* but in a way that can be used by the underlying operating system.
*/
struct stream_info
{
/**
* Supported stream types.
*/
enum type
{
/**
* Matches stream_behavior::close.
*/
close,
/**
* Matches stream_behavior::inherit.
*/
inherit,
/**
* Matches stream_behavior::redirect_to_stdout and
* stream_behavior::posix_redirect.
*/
redirect,
/**
* Matches stream_behavior::silence.
*/
use_file,
/**
* TODO: Matches nothing yet ...
*/
use_handle,
/**
* Matches stream_behavior::capture.
*/
use_pipe
};
/**
* Stream type.
*/
type type_;
/**
* Descriptor to use when stream type is set to \a redirect.
*/
int desc_to_;
/**
* File to use when stream type is set to \a use_file.
*/
std::string file_;
/**
* Handle to use when stream type is set to \a use_handle.
*/
file_handle handle_;
/**
* Pipe to use when stream type is set to \a use_pipe.
*/
boost::optional<pipe> pipe_;
/**
* Constructs a new stream_info object.
*/
stream_info(const stream_behavior &sb, bool out)
{
switch (sb.type_)
{
case stream_behavior::close:
{
type_ = close;
break;
}
case stream_behavior::inherit:
{
type_ = inherit;
break;
}
case stream_behavior::redirect_to_stdout:
{
type_ = redirect;
#if defined(BOOST_POSIX_API)
desc_to_ = STDOUT_FILENO;
#elif defined(BOOST_WINDOWS_API)
desc_to_ = 1;
#endif
break;
}
#if defined(BOOST_POSIX_API)
case stream_behavior::posix_redirect:
{
type_ = redirect;
desc_to_ = sb.desc_to_;
break;
}
#endif
case stream_behavior::silence:
{
type_ = use_file;
#if defined(BOOST_POSIX_API)
file_ = out ? "/dev/null" : "/dev/zero";
#elif defined(BOOST_WINDOWS_API)
file_ = "NUL";
#endif
break;
}
case stream_behavior::capture:
{
type_ = use_pipe;
pipe_ = pipe();
break;
}
default:
{
BOOST_ASSERT(false);
}
}
}
};
}
}
}
#endif

231
boost/process/detail/systembuf.hpp

@ -1,231 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/detail/systembuf.hpp
*
* Includes the declaration of the systembuf class. This file is for
* internal usage only and must not be included by the library user.
*/
#ifndef BOOST_PROCESS_DETAIL_SYSTEMBUF_HPP
#define BOOST_PROCESS_DETAIL_SYSTEMBUF_HPP
#include <boost/process/config.hpp>
#if defined(BOOST_POSIX_API)
# include <sys/types.h>
# include <unistd.h>
#elif defined(BOOST_WINDOWS_API)
# include <windows.h>
#else
# error "Unsupported platform."
#endif
#include <boost/noncopyable.hpp>
#include <boost/scoped_array.hpp>
#include <boost/assert.hpp>
#include <streambuf>
#include <cstddef>
namespace boost {
namespace process {
class postream;
namespace detail {
/**
* std::streambuf implementation for system file handles.
*
* systembuf provides a std::streambuf implementation for system file
* handles. Contrarywise to file_handle, this class does \b not take
* ownership of the native file handle; this should be taken care of
* somewhere else.
*
* This class follows the expected semantics of a std::streambuf object.
* However, it is not copyable to avoid introducing inconsistences with
* the on-disk file and the in-memory buffers.
*/
class systembuf : public std::streambuf, public boost::noncopyable
{
friend class ::boost::process::postream;
public:
#if defined(BOOST_PROCESS_DOXYGEN)
/**
* Opaque name for the native handle type.
*/
typedef NativeHandleType handle_type;
#elif defined(BOOST_POSIX_API)
typedef int handle_type;
#elif defined(BOOST_WINDOWS_API)
typedef HANDLE handle_type;
#endif
/**
* Constructs a new systembuf for the given file handle.
*
* This constructor creates a new systembuf object that reads or
* writes data from/to the \a h native file handle. This handle
* is \b not owned by the created systembuf object; the code
* should take care of it externally.
*
* This class buffers input and output; the buffer size may be
* tuned through the \a bufsize parameter, which defaults to 8192
* bytes.
*
* \see pistream and postream
*/
explicit systembuf(handle_type h, std::size_t bufsize = 8192)
: handle_(h),
bufsize_(bufsize),
read_buf_(new char[bufsize]),
write_buf_(new char[bufsize])
{
#if defined(BOOST_POSIX_API)
BOOST_ASSERT(handle_ >= 0);
#elif defined(BOOST_WINDOWS_API)
BOOST_ASSERT(handle_ != INVALID_HANDLE_VALUE);
#endif
BOOST_ASSERT(bufsize_ > 0);
setp(write_buf_.get(), write_buf_.get() + bufsize_);
}
protected:
/**
* Reads new data from the native file handle.
*
* This operation is called by input methods when there is no more
* data in the input buffer. The function fills the buffer with new
* data, if available.
*
* \pre All input positions are exhausted (gptr() >= egptr()).
* \post The input buffer has new data, if available.
* \returns traits_type::eof() if a read error occurrs or there are
* no more data to be read. Otherwise returns
* traits_type::to_int_type(*gptr()).
*/
virtual int_type underflow()
{
BOOST_ASSERT(gptr() >= egptr());
bool ok;
#if defined(BOOST_POSIX_API)
ssize_t cnt = ::read(handle_, read_buf_.get(), bufsize_);
ok = (cnt != -1 && cnt != 0);
#elif defined(BOOST_WINDOWS_API)
DWORD cnt;
BOOL res = ::ReadFile(handle_, read_buf_.get(), bufsize_, &cnt, NULL);
ok = (res && cnt > 0);
#endif
if (!ok)
return traits_type::eof();
else
{
setg(read_buf_.get(), read_buf_.get(), read_buf_.get() + cnt);
return traits_type::to_int_type(*gptr());
}
}
/**
* Makes room in the write buffer for additional data.
*
* This operation is called by output methods when there is no more
* space in the output buffer to hold a new element. The function
* first flushes the buffer's contents to disk and then clears it to
* leave room for more characters. The given \a c character is
* stored at the beginning of the new space.
*
* \pre All output positions are exhausted (pptr() >= epptr()).
* \post The output buffer has more space if no errors occurred
* during the write to disk.
* \post *(pptr() - 1) is \a c.
* \returns traits_type::eof() if a write error occurrs. Otherwise
* returns traits_type::not_eof(c).
*/
virtual int_type overflow(int c)
{
BOOST_ASSERT(pptr() >= epptr());
if (sync() == -1)
return traits_type::eof();
if (!traits_type::eq_int_type(c, traits_type::eof()))
{
traits_type::assign(*pptr(), c);
pbump(1);
}
return traits_type::not_eof(c);
}
/**
* Flushes the output buffer to disk.
*
* Synchronizes the systembuf buffers with the contents of the file
* associated to this object through the native file handle. The
* output buffer is flushed to disk and cleared to leave new room
* for more data.
*
* \returns 0 on success, -1 if an error occurred.
*/
virtual int sync()
{
#if defined(BOOST_POSIX_API)
ssize_t cnt = pptr() - pbase();
#elif defined(BOOST_WINDOWS_API)
long cnt = pptr() - pbase();
#endif
bool ok;
#if defined(BOOST_POSIX_API)
ok = ::write(handle_, pbase(), cnt) == cnt;
#elif defined(BOOST_WINDOWS_API)
DWORD rcnt;
BOOL res = ::WriteFile(handle_, pbase(), cnt, &rcnt, NULL);
ok = (res && static_cast<long>(rcnt) == cnt);
#endif
if (ok)
pbump(-cnt);
return ok ? 0 : -1;
}
private:
/**
* Native file handle used by the systembuf object.
*/
handle_type handle_;
/**
* Internal buffer size used during read and write operations.
*/
std::size_t bufsize_;
/**
* Internal buffer used during read operations.
*/
boost::scoped_array<char> read_buf_;
/**
* Internal buffer used during write operations.
*/
boost::scoped_array<char> write_buf_;
};
}
}
}
#endif

355
boost/process/detail/win32_ops.hpp

@ -1,355 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/detail/win32_ops.hpp
*
* Provides some convenience functions to start processes under
* Windows-compatible operating systems.
*/
#ifndef BOOST_PROCESS_DETAIL_WIN32_OPS_HPP
#define BOOST_PROCESS_DETAIL_WIN32_OPS_HPP
#include <boost/process/environment.hpp>
#include <boost/process/detail/file_handle.hpp>
#include <boost/process/detail/pipe.hpp>
#include <boost/process/detail/stream_info.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_array.hpp>
#include <boost/scoped_array.hpp>
#include <boost/assert.hpp>
#include <boost/system/system_error.hpp>
#include <boost/throw_exception.hpp>
#include <vector>
#include <string>
#include <cstddef>
#include <string.h>
#include <windows.h>
namespace boost {
namespace process {
namespace detail {
/**
* Converts the command line to a plain string. Converts the command line's
* list of arguments to the format expected by the \a lpCommandLine parameter
* in the CreateProcess() system call.
*
* This operation is only available on Windows systems.
*
* \return A dynamically allocated string holding the command line
* to be passed to the executable. It is returned in a
* shared_array object to ensure its release at some point.
*/
template <class Arguments>
inline boost::shared_array<char> collection_to_win32_cmdline(const Arguments &args)
{
typedef std::vector<std::string> arguments_t;
arguments_t args2;
typename Arguments::size_type i = 0;
std::size_t size = 0;
for (typename Arguments::const_iterator it = args.begin(); it != args.end(); ++it)
{
std::string arg = *it;
std::string::size_type pos = 0;
while ( (pos = arg.find('"', pos)) != std::string::npos)
{
arg.replace(pos, 1, "\\\"");
pos += 2;
}
if (arg.find(' ') != std::string::npos)
arg = '\"' + arg + '\"';
if (i++ != args.size() - 1)
arg += ' ';
args2.push_back(arg);
size += arg.size() + 1;
}
boost::shared_array<char> cmdline(new char[size]);
cmdline.get()[0] = '\0';
for (arguments_t::size_type i = 0; i < args.size(); ++i)
#if defined(__CYGWIN__)
::strncat(cmdline.get(), args2[i].c_str(), args2[i].size());
#else
::strcat_s(cmdline.get(), size, args2[i].c_str());
#endif
return cmdline;
}
/**
* Converts an environment to a string used by CreateProcess().
*
* Converts the environment's contents to the format used by the
* CreateProcess() system call. The returned char* string is
* allocated in dynamic memory; the caller must free it when not
* used any more. This is enforced by the use of a shared pointer.
*
* \return A dynamically allocated char* string that represents
* the environment's content. This string is of the form
* var1=value1\\0var2=value2\\0\\0.
*/
inline boost::shared_array<char> environment_to_win32_strings(const environment &env)
{
boost::shared_array<char> envp;
if (env.empty())
{
envp.reset(new char[2]);
::ZeroMemory(envp.get(), 2);
}
else
{
std::string s;
for (environment::const_iterator it = env.begin(); it != env.end(); ++it)
{
s += it->first + "=" + it->second;
s.push_back(0);
}
envp.reset(new char[s.size() + 1]);
#if defined(__CYGWIN__)
::memcpy(envp.get(), s.c_str(), s.size() + 1);
#else
::memcpy_s(envp.get(), s.size() + 1, s.c_str(), s.size() + 1);
#endif
}
return envp;
}
/**
* Helper class to configure a Win32 %child.
*
* This helper class is used to hold all the attributes that configure a
* new Win32 %child process.
*
* All its fields are public for simplicity. It is only intended for
* internal use and it is heavily coupled with the Context
* implementations.
*/
struct win32_setup
{
/**
* The work directory.
*
* This string specifies the directory in which the %child process
* starts execution. It cannot be empty.
*/
std::string work_directory;
/**
* The process startup properties.
*
* This Win32-specific object holds a list of properties that describe
* how the new process should be started. The \a STARTF_USESTDHANDLES
* flag should not be set in it because it is automatically configured
* by win32_start().
*/
STARTUPINFOA *startupinfo;
};
/**
* Starts a new child process in a Win32 operating system.
*
* This helper functions is provided to simplify the Context's task when
* it comes to starting up a new process in a Win32 operating system.
*
* \param exe The executable to spawn the child process.
* \param args The arguments for the executable.
* \param env The environment variables that the new child process
* receives.
* \param infoin Information that describes stdin's behavior.
* \param infoout Information that describes stdout's behavior.
* \param infoerr Information that describes stderr's behavior.
* \param setup A helper object holding extra child information.
* \return The new process' information as returned by the CreateProcess()
* system call. The caller is responsible of creating an
* appropriate Child representation for it.
* \pre \a setup.startupinfo_ cannot have the \a STARTF_USESTDHANDLES set
* in the \a dwFlags field.
*/
template <class Executable, class Arguments>
inline PROCESS_INFORMATION win32_start(const Executable &exe, const Arguments &args, const environment &env, stream_info &infoin, stream_info &infoout, stream_info &infoerr, const win32_setup &setup)
{
file_handle chin, chout, cherr;
BOOST_ASSERT(setup.startupinfo->cb >= sizeof(STARTUPINFOA));
BOOST_ASSERT(!(setup.startupinfo->dwFlags & STARTF_USESTDHANDLES));
boost::scoped_ptr<STARTUPINFOA> si(new STARTUPINFOA);
::CopyMemory(si.get(), setup.startupinfo, sizeof(*setup.startupinfo));
si->dwFlags |= STARTF_USESTDHANDLES;
switch (infoin.type_)
{
case stream_info::close:
{
break;
}
case stream_info::inherit:
{
chin = file_handle::win32_std(STD_INPUT_HANDLE, true);
break;
}
case stream_info::use_file:
{
HANDLE h = ::CreateFileA(infoin.file_.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::detail::win32_start: CreateFile failed"));
chin = file_handle(h);
break;
}
case stream_info::use_handle:
{
chin = infoin.handle_;
chin.win32_set_inheritable(true);
break;
}
case stream_info::use_pipe:
{
infoin.pipe_->rend().win32_set_inheritable(true);
chin = infoin.pipe_->rend();
break;
}
default:
{
BOOST_ASSERT(false);
break;
}
}
si->hStdInput = chin.valid() ? chin.get() : INVALID_HANDLE_VALUE;
switch (infoout.type_)
{
case stream_info::close:
{
break;
}
case stream_info::inherit:
{
chout = file_handle::win32_std(STD_OUTPUT_HANDLE, true);
break;
}
case stream_info::use_file:
{
HANDLE h = ::CreateFileA(infoout.file_.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::detail::win32_start: CreateFile failed"));
chout = file_handle(h);
break;
}
case stream_info::use_handle:
{
chout = infoout.handle_;
chout.win32_set_inheritable(true);
break;
}
case stream_info::use_pipe:
{
infoout.pipe_->wend().win32_set_inheritable(true);
chout = infoout.pipe_->wend();
break;
}
default:
{
BOOST_ASSERT(false);
break;
}
}
si->hStdOutput = chout.valid() ? chout.get() : INVALID_HANDLE_VALUE;
switch (infoerr.type_)
{
case stream_info::close:
{
break;
}
case stream_info::inherit:
{
cherr = file_handle::win32_std(STD_ERROR_HANDLE, true);
break;
}
case stream_info::redirect:
{
BOOST_ASSERT(infoerr.desc_to_ == 1);
BOOST_ASSERT(chout.valid());
cherr = file_handle::win32_dup(chout.get(), true);
break;
}
case stream_info::use_file:
{
HANDLE h = ::CreateFileA(infoerr.file_.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::detail::win32_start: CreateFile failed"));
cherr = file_handle(h);
break;
}
case stream_info::use_handle:
{
cherr = infoerr.handle_;
cherr.win32_set_inheritable(true);
break;
}
case stream_info::use_pipe:
{
infoerr.pipe_->wend().win32_set_inheritable(true);
cherr = infoerr.pipe_->wend();
break;
}
default:
{
BOOST_ASSERT(false);
break;
}
}
si->hStdError = cherr.valid() ? cherr.get() : INVALID_HANDLE_VALUE;
PROCESS_INFORMATION pi;
::ZeroMemory(&pi, sizeof(pi));
boost::shared_array<char> cmdline = collection_to_win32_cmdline(args);
boost::scoped_array<char> executable(new char[exe.size() + 1]);
#if defined(__CYGWIN__)
::strcpy(executable.get(), exe.c_str());
#else
::strcpy_s(executable.get(), exe.size() + 1, exe.c_str());
#endif
boost::scoped_array<char> workdir(new char[setup.work_directory.size() + 1]);
#if defined(__CYGWIN__)
::strcpy(workdir.get(), setup.work_directory.c_str());
#else
::strcpy_s(workdir.get(), setup.work_directory.size() + 1, setup.work_directory.c_str());
#endif
boost::shared_array<char> envstrs = environment_to_win32_strings(env);
if (!::CreateProcessA(executable.get()[0] ? executable.get() : NULL, cmdline.get(), NULL, NULL, TRUE, CREATE_NO_WINDOW, envstrs.get(), workdir.get(), si.get(), &pi))
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::detail::win32_start: CreateProcess failed"));
return pi;
}
}
}
}
#endif

50
boost/process/environment.hpp

@ -1,50 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/environment.hpp
*
* Includes the declaration of the environment class.
*/
#ifndef BOOST_PROCESS_ENVIRONMENT_HPP
#define BOOST_PROCESS_ENVIRONMENT_HPP
#include <string>
#include <map>
namespace boost {
namespace process {
/**
* Representation of a process' environment variables.
*
* The environment is a map that stablishes an unidirectional
* association between variable names and their values and is
* represented by a string to string map.
*
* Variables may be defined to the empty string. Be aware that doing so
* is not portable: POSIX systems will treat such variables as being
* defined to the empty value, but Windows systems are not able to
* distinguish them from undefined variables.
*
* Neither POSIX nor Windows systems support a variable with no name.
*
* It is worthy to note that the environment is sorted alphabetically.
* This is provided for-free by the map container used to implement this
* type, and this behavior is required by Windows systems.
*/
typedef std::map<std::string, std::string> environment;
}
}
#endif

583
boost/process/operations.hpp

@ -1,583 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/operations.hpp
*
* Provides miscellaneous free functions.
*/
#ifndef BOOST_PROCESS_OPERATIONS_HPP
#define BOOST_PROCESS_OPERATIONS_HPP
#include <boost/process/config.hpp>
#if defined(BOOST_POSIX_API)
# include <boost/process/detail/posix_ops.hpp>
# include <stdlib.h>
# include <unistd.h>
# if defined(__CYGWIN__)
# include <boost/scoped_array.hpp>
# include <sys/cygwin.h>
# endif
#elif defined(BOOST_WINDOWS_API)
# include <boost/process/detail/win32_ops.hpp>
# include <boost/algorithm/string/predicate.hpp>
# include <windows.h>
#else
# error "Unsupported platform."
#endif
#include <boost/process/child.hpp>
#include <boost/process/stream_behavior.hpp>
#include <boost/process/status.hpp>
#include <boost/process/detail/file_handle.hpp>
#include <boost/process/detail/pipe.hpp>
#include <boost/process/detail/stream_info.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/system/system_error.hpp>
#include <boost/throw_exception.hpp>
#include <boost/scoped_array.hpp>
#include <boost/assert.hpp>
#include <string>
#include <vector>
#include <stdexcept>
#include <cstddef>
namespace boost {
namespace process {
/**
* Locates the executable program \a file in all the directory components
* specified in \a path. If \a path is empty, the value of the PATH
* environment variable is used.
*
* The path variable is interpreted following the same conventions used
* to parse the PATH environment variable in the underlying platform.
*
* \throw boost::filesystem::filesystem_error If the file cannot be found
* in the path.
*/
inline std::string find_executable_in_path(const std::string &file, std::string path = "")
{
#if defined(BOOST_POSIX_API)
BOOST_ASSERT(file.find('/') == std::string::npos);
#elif defined(BOOST_WINDOWS_API)
BOOST_ASSERT(file.find_first_of("\\/") == std::string::npos);
#endif
std::string result;
#if defined(BOOST_POSIX_API)
if (path.empty())
{
const char *envpath = ::getenv("PATH");
// if (!envpath)
// boost::throw_exception(boost::filesystem::filesystem_error("boost::process::find_executable_in_path: retrieving PATH failed", file, boost::system::errc::make_error_code(boost::system::errc::no_such_file_or_directory)));
path = envpath;
}
BOOST_ASSERT(!path.empty());
#if defined(__CYGWIN__)
if (!::cygwin_posix_path_list_p(path.c_str()))
{
int size = ::cygwin_win32_to_posix_path_list_buf_size(path.c_str());
boost::scoped_array<char> cygpath(new char[size]);
::cygwin_win32_to_posix_path_list(path.c_str(), cygpath.get());
path = cygpath.get();
}
#endif
std::string::size_type pos1 = 0, pos2;
do
{
pos2 = path.find(':', pos1);
std::string dir = (pos2 != std::string::npos) ? path.substr(pos1, pos2 - pos1) : path.substr(pos1);
std::string f = dir + (boost::algorithm::ends_with(dir, "/") ? "" : "/") + file;
if (!::access(f.c_str(), X_OK))
result = f;
pos1 = pos2 + 1;
} while (pos2 != std::string::npos && result.empty());
#elif defined(BOOST_WINDOWS_API)
const char *exts[] = { "", ".exe", ".com", ".bat", NULL };
const char **ext = exts;
while (*ext)
{
char buf[MAX_PATH];
char *dummy;
DWORD size = ::SearchPathA(path.empty() ? NULL : path.c_str(), file.c_str(), *ext, MAX_PATH, buf, &dummy);
BOOST_ASSERT(size < MAX_PATH);
if (size > 0)
{
result = buf;
break;
}
++ext;
}
#endif
// if (result.empty())
// boost::throw_exception(boost::filesystem::filesystem_error("boost::process::find_executable_in_path: file not found", file, boost::system::errc::make_error_code(boost::system::errc::no_such_file_or_directory)));
return result;
}
/**
* Extracts the program name from a given executable.
*
* \return The program name. On Windows the program name
* is returned without a file extension.
*/
inline std::string executable_to_progname(const std::string &exe)
{
std::string::size_type begin = 0;
std::string::size_type end = std::string::npos;
#if defined(BOOST_POSIX_API)
std::string::size_type slash = exe.rfind('/');
#elif defined(BOOST_WINDOWS_API)
std::string::size_type slash = exe.find_last_of("/\\");
#endif
if (slash != std::string::npos)
begin = slash + 1;
#if defined(BOOST_WINDOWS_API)
if (exe.size() > 4 &&
(boost::algorithm::iends_with(exe, ".exe") || boost::algorithm::iends_with(exe, ".com") || boost::algorithm::iends_with(exe, ".bat")))
end = exe.size() - 4;
#endif
return exe.substr(begin, end - begin);
}
/**
* Starts a new child process.
*
* Launches a new process based on the binary image specified by the
* executable, the set of arguments to be passed to it and several
* parameters that describe the execution context.
*
* \remark Blocking remarks: This function may block if the device
* holding the executable blocks when loading the image. This might
* happen if, e.g., the binary is being loaded from a network share.
*
* \return A handle to the new child process.
*/
template <class Executable, class Arguments, class Context>
inline child launch(const Executable &exe, const Arguments &args, const Context &ctx)
{
detail::file_handle fhstdin, fhstdout, fhstderr;
BOOST_ASSERT(!args.empty());
BOOST_ASSERT(!ctx.work_directory.empty());
#if defined(BOOST_POSIX_API)
detail::info_map infoin, infoout;
if (ctx.stdin_behavior.get_type() != stream_behavior::close)
{
detail::stream_info si = detail::stream_info(ctx.stdin_behavior, false);
infoin.insert(detail::info_map::value_type(STDIN_FILENO, si));
}
if (ctx.stdout_behavior.get_type() != stream_behavior::close)
{
detail::stream_info si = detail::stream_info(ctx.stdout_behavior, true);
infoout.insert(detail::info_map::value_type(STDOUT_FILENO, si));
}
if (ctx.stderr_behavior.get_type() != stream_behavior::close)
{
detail::stream_info si = detail::stream_info(ctx.stderr_behavior, true);
infoout.insert(detail::info_map::value_type(STDERR_FILENO, si));
}
detail::posix_setup s;
s.work_directory = ctx.work_directory;
child::id_type pid = detail::posix_start(exe, args, ctx.environment, infoin, infoout, s);
if (ctx.stdin_behavior.get_type() == stream_behavior::capture)
{
fhstdin = detail::posix_info_locate_pipe(infoin, STDIN_FILENO, false);
BOOST_ASSERT(fhstdin.valid());
}
if (ctx.stdout_behavior.get_type() == stream_behavior::capture)
{
fhstdout = detail::posix_info_locate_pipe(infoout, STDOUT_FILENO, true);
BOOST_ASSERT(fhstdout.valid());
}
if (ctx.stderr_behavior.get_type() == stream_behavior::capture)
{
fhstderr = detail::posix_info_locate_pipe(infoout, STDERR_FILENO, true);
BOOST_ASSERT(fhstderr.valid());
}
return child(pid, fhstdin, fhstdout, fhstderr);
#elif defined(BOOST_WINDOWS_API)
detail::stream_info behin = detail::stream_info(ctx.stdin_behavior, false);
if (behin.type_ == detail::stream_info::use_pipe)
fhstdin = behin.pipe_->wend();
detail::stream_info behout = detail::stream_info(ctx.stdout_behavior, true);
if (behout.type_ == detail::stream_info::use_pipe)
fhstdout = behout.pipe_->rend();
detail::stream_info beherr = detail::stream_info(ctx.stderr_behavior, true);
if (beherr.type_ == detail::stream_info::use_pipe)
fhstderr = beherr.pipe_->rend();
STARTUPINFOA si;
::ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
detail::win32_setup s;
s.work_directory = ctx.work_directory;
s.startupinfo = &si;
PROCESS_INFORMATION pi = detail::win32_start(exe, args, ctx.environment, behin, behout, beherr, s);
if (!::CloseHandle(pi.hThread))
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch: CloseHandle failed"));
return child(pi.dwProcessId, fhstdin, fhstdout, fhstderr, detail::file_handle(pi.hProcess));
#endif
}
/**
* Launches a shell-based command.
*
* Executes the given command through the default system shell. The
* command is subject to pattern expansion, redirection and pipelining.
* The shell is launched as described by the parameters in the execution
* context.
*
* This function behaves similarly to the system(3) system call. In a
* POSIX system, the command is fed to /bin/sh whereas under a Windows
* system, it is fed to cmd.exe. It is difficult to write portable
* commands as the first parameter, but this function comes in handy in
* multiple situations.
*/
template <class Context>
inline child launch_shell(const std::string &command, const Context &ctx)
{
std::string exe;
std::vector<std::string> args;
#if defined(BOOST_POSIX_API)
exe = "/bin/sh";
args.push_back("sh");
args.push_back("-c");
args.push_back(command);
#elif defined(BOOST_WINDOWS_API)
char sysdir[MAX_PATH];
UINT size = ::GetSystemDirectoryA(sysdir, sizeof(sysdir));
if (!size)
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch_shell: GetWindowsDirectory failed"));
BOOST_ASSERT(size < MAX_PATH);
exe = std::string(sysdir) + (sysdir[size - 1] != '\\' ? "\\cmd.exe" : "cmd.exe");
args.push_back("cmd");
args.push_back("/c");
args.push_back(command);
#endif
return launch(exe, args, ctx);
}
/**
* Launches a pipelined set of child processes.
*
* Given a collection of pipeline_entry objects describing how to launch
* a set of child processes, spawns them all and connects their inputs and
* outputs in a way that permits pipelined communication.
*
* \pre Let 1..N be the processes in the collection: the input behavior of
* the 2..N processes must be set to close_stream().
* \pre Let 1..N be the processes in the collection: the output behavior of
* the 1..N-1 processes must be set to close_stream().
* \remark Blocking remarks: This function may block if the device holding
* the executable of one of the entries blocks when loading the
* image. This might happen if, e.g., the binary is being loaded
* from a network share.
* \return A set of Child objects that represent all the processes spawned
* by this call. You should use wait_children() to wait for their
* termination.
*/
template <class Entries>
inline children launch_pipeline(const Entries &entries)
{
BOOST_ASSERT(entries.size() >= 2);
children cs;
detail::file_handle fhinvalid;
boost::scoped_array<detail::pipe> pipes(new detail::pipe[entries.size() - 1]);
#if defined(BOOST_POSIX_API)
{
typename Entries::size_type i = 0;
const typename Entries::value_type::context_type &ctx = entries[i].context;
detail::info_map infoin, infoout;
if (ctx.stdin_behavior.get_type() != stream_behavior::close)
{
detail::stream_info si = detail::stream_info(ctx.stdin_behavior, false);
infoin.insert(detail::info_map::value_type(STDIN_FILENO, si));
}
BOOST_ASSERT(ctx.stdout_behavior.get_type() == stream_behavior::close);
detail::stream_info si2(close_stream(), true);
si2.type_ = detail::stream_info::use_handle;
si2.handle_ = pipes[i].wend().release();
infoout.insert(detail::info_map::value_type(STDOUT_FILENO, si2));
if (ctx.stderr_behavior.get_type() != stream_behavior::close)
{
detail::stream_info si = detail::stream_info(ctx.stderr_behavior, true);
infoout.insert(detail::info_map::value_type(STDERR_FILENO, si));
}
detail::posix_setup s;
s.work_directory = ctx.work_directory;
pid_t pid = detail::posix_start(entries[i].executable, entries[i].arguments, ctx.environment, infoin, infoout, s);
detail::file_handle fhstdin;
if (ctx.stdin_behavior.get_type() == stream_behavior::capture)
{
fhstdin = detail::posix_info_locate_pipe(infoin, STDIN_FILENO, false);
BOOST_ASSERT(fhstdin.valid());
}
cs.push_back(child(pid, fhstdin, fhinvalid, fhinvalid));
}
for (typename Entries::size_type i = 1; i < entries.size() - 1; ++i)
{
const typename Entries::value_type::context_type &ctx = entries[i].context;
detail::info_map infoin, infoout;
BOOST_ASSERT(ctx.stdin_behavior.get_type() == stream_behavior::close);
detail::stream_info si1(close_stream(), false);
si1.type_ = detail::stream_info::use_handle;
si1.handle_ = pipes[i - 1].rend().release();
infoin.insert(detail::info_map::value_type(STDIN_FILENO, si1));
BOOST_ASSERT(ctx.stdout_behavior.get_type() == stream_behavior::close);
detail::stream_info si2(close_stream(), true);
si2.type_ = detail::stream_info::use_handle;
si2.handle_ = pipes[i].wend().release();
infoout.insert(detail::info_map::value_type(STDOUT_FILENO, si2));
if (ctx.stderr_behavior.get_type() != stream_behavior::close)
{
detail::stream_info si = detail::stream_info(ctx.stderr_behavior, true);
infoout.insert(detail::info_map::value_type(STDERR_FILENO, si));
}
detail::posix_setup s;
s.work_directory = ctx.work_directory;
pid_t pid = detail::posix_start(entries[i].executable, entries[i].arguments, ctx.environment, infoin, infoout, s);
cs.push_back(child(pid, fhinvalid, fhinvalid, fhinvalid));
}
{
typename Entries::size_type i = entries.size() - 1;
const typename Entries::value_type::context_type &ctx = entries[i].context;
detail::info_map infoin, infoout;
BOOST_ASSERT(ctx.stdin_behavior.get_type() == stream_behavior::close);
detail::stream_info si1(close_stream(), false);
si1.type_ = detail::stream_info::use_handle;
si1.handle_ = pipes[i - 1].rend().release();
infoin.insert(detail::info_map::value_type(STDIN_FILENO, si1));
if (ctx.stdout_behavior.get_type() != stream_behavior::close)
{
detail::stream_info si = detail::stream_info(ctx.stdout_behavior, true);
infoout.insert(detail::info_map::value_type(STDOUT_FILENO, si));
}
if (ctx.stderr_behavior.get_type() != stream_behavior::close)
{
detail::stream_info si = detail::stream_info(ctx.stderr_behavior, true);
infoout.insert(detail::info_map::value_type(STDERR_FILENO, si));
}
detail::posix_setup s;
s.work_directory = ctx.work_directory;
pid_t pid = detail::posix_start(entries[i].executable, entries[i].arguments, ctx.environment, infoin, infoout, s);
detail::file_handle fhstdout, fhstderr;
if (ctx.stdout_behavior.get_type() == stream_behavior::capture)
{
fhstdout = detail::posix_info_locate_pipe(infoout, STDOUT_FILENO, true);
BOOST_ASSERT(fhstdout.valid());
}
if (ctx.stderr_behavior.get_type() == stream_behavior::capture)
{
fhstderr = detail::posix_info_locate_pipe(infoout, STDERR_FILENO, true);
BOOST_ASSERT(fhstderr.valid());
}
cs.push_back(child(pid, fhinvalid, fhstdout, fhstderr));
}
#elif defined(BOOST_WINDOWS_API)
STARTUPINFOA si;
detail::win32_setup s;
s.startupinfo = &si;
{
typename Entries::size_type i = 0;
const typename Entries::value_type::context_type &ctx = entries[i].context;
detail::stream_info sii = detail::stream_info(ctx.stdin_behavior, false);
detail::file_handle fhstdin;
if (sii.type_ == detail::stream_info::use_pipe)
fhstdin = sii.pipe_->wend();
BOOST_ASSERT(ctx.stdout_behavior.get_type() == stream_behavior::close);
detail::stream_info sio(close_stream(), true);
sio.type_ = detail::stream_info::use_handle;
sio.handle_ = pipes[i].wend().release();
detail::stream_info sie(ctx.stderr_behavior, true);
s.work_directory = ctx.work_directory;
::ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi = detail::win32_start(entries[i].executable, entries[i].arguments, ctx.environment, sii, sio, sie, s);
if (!::CloseHandle(pi.hThread))
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch_pipeline: CloseHandle failed"));
cs.push_back(child(pi.dwProcessId, fhstdin, fhinvalid, fhinvalid, detail::file_handle(pi.hProcess)));
}
for (typename Entries::size_type i = 1; i < entries.size() - 1; ++i)
{
const typename Entries::value_type::context_type &ctx = entries[i].context;
BOOST_ASSERT(ctx.stdin_behavior.get_type() == stream_behavior::close);
detail::stream_info sii(close_stream(), false);
sii.type_ = detail::stream_info::use_handle;
sii.handle_ = pipes[i - 1].rend().release();
detail::stream_info sio(close_stream(), true);
sio.type_ = detail::stream_info::use_handle;
sio.handle_ = pipes[i].wend().release();
detail::stream_info sie(ctx.stderr_behavior, true);
s.work_directory = ctx.work_directory;
::ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi = detail::win32_start(entries[i].executable, entries[i].arguments, ctx.environment, sii, sio, sie, s);
if (!::CloseHandle(pi.hThread))
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch_pipeline: CloseHandle failed"));
cs.push_back(child(pi.dwProcessId, fhinvalid, fhinvalid, fhinvalid, detail::file_handle(pi.hProcess)));
}
{
typename Entries::size_type i = entries.size() - 1;
const typename Entries::value_type::context_type &ctx = entries[i].context;
BOOST_ASSERT(ctx.stdin_behavior.get_type() == stream_behavior::close);
detail::stream_info sii(close_stream(), false);
sii.type_ = detail::stream_info::use_handle;
sii.handle_ = pipes[i - 1].rend().release();
detail::file_handle fhstdout, fhstderr;
detail::stream_info sio(ctx.stdout_behavior, true);
if (sio.type_ == detail::stream_info::use_pipe)
fhstdout = sio.pipe_->rend();
detail::stream_info sie(ctx.stderr_behavior, true);
if (sie.type_ == detail::stream_info::use_pipe)
fhstderr = sie.pipe_->rend();
s.work_directory = ctx.work_directory;
::ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi = detail::win32_start(entries[i].executable, entries[i].arguments, ctx.environment, sii, sio, sie, s);
if (!::CloseHandle(pi.hThread))
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch_pipeline: CloseHandle failed"));
cs.push_back(child(pi.dwProcessId, fhinvalid, fhstdout, fhstderr, detail::file_handle(pi.hProcess)));
}
#endif
return cs;
}
/**
* Waits for a collection of children to terminate.
*
* Given a collection of Child objects (such as std::vector<child> or
* the convenience children type), waits for the termination of all of
* them.
*
* \remark Blocking remarks: This call blocks if any of the children
* processes in the collection has not finalized execution and
* waits until it terminates.
*
* \return The exit status of the first process that returns an error
* code or, if all of them executed correctly, the exit status
* of the last process in the collection.
*/
template <class Children>
inline status wait_children(Children &cs)
{
BOOST_ASSERT(cs.size() >= 2);
typename Children::iterator it = cs.begin();
while (it != cs.end())
{
const status s = it->wait();
++it;
if (it == cs.end())
return s;
else if (!s.exited() || s.exit_status() != EXIT_SUCCESS)
{
while (it != cs.end())
{
it->wait();
++it;
}
return s;
}
}
BOOST_ASSERT(false);
return cs.begin()->wait();
}
}
}
#endif

116
boost/process/pistream.hpp

@ -1,116 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/pistream.hpp
*
* Includes the declaration of the pistream class.
*/
#ifndef BOOST_PROCESS_PISTREAM_HPP
#define BOOST_PROCESS_PISTREAM_HPP
#include <boost/process/detail/file_handle.hpp>
#include <boost/process/detail/systembuf.hpp>
#include <boost/noncopyable.hpp>
#include <istream>
namespace boost {
namespace process {
/**
* Child process' output stream.
*
* The pistream class represents an output communication channel with the
* child process. The child process writes data to this stream and the
* parent process can read it through the pistream object. In other
* words, from the child's point of view, the communication channel is an
* output one, but from the parent's point of view it is an input one;
* hence the confusing pistream name.
*
* pistream objects cannot be copied because they own the file handle
* they use to communicate with the child and because they buffer data
* that flows through the communication channel.
*
* A pistream object behaves as a std::istream stream in all senses.
* The class is only provided because it must provide a method to let
* the caller explicitly close the communication channel.
*
* \remark Blocking remarks: Functions that read data from this
* stream can block if the associated file handle blocks during
* the read. As this class is used to communicate with child
* processes through anonymous pipes, the most typical blocking
* condition happens when the child has no more data to send to
* the pipe's system buffer. When this happens, the buffer
* eventually empties and the system blocks until the writer
* generates some data.
*/
class pistream : public std::istream, public boost::noncopyable
{
public:
/**
* Creates a new process' output stream.
*
* Given a file handle, this constructor creates a new pistream
* object that owns the given file handle \a fh. Ownership of
* \a fh is transferred to the created pistream object.
*
* \pre \a fh is valid.
* \post \a fh is invalid.
* \post The new pistream object owns \a fh.
*/
explicit pistream(detail::file_handle &fh)
: std::istream(0),
handle_(fh),
systembuf_(handle_.get())
{
rdbuf(&systembuf_);
}
/**
* Returns the file handle managed by this stream.
*
* The file handle must not be copied. Copying invalidates
* the source file handle making the pistream unusable.
*/
detail::file_handle &handle()
{
return handle_;
}
/**
* Closes the file handle managed by this stream.
*
* Explicitly closes the file handle managed by this stream. This
* function can be used by the user to tell the child process it's
* not willing to receive more data.
*/
void close()
{
handle_.close();
}
private:
/**
* The file handle managed by this stream.
*/
detail::file_handle handle_;
/**
* The systembuf object used to manage this stream's data.
*/
detail::systembuf systembuf_;
};
}
}
#endif

178
boost/process/posix_child.hpp

@ -1,178 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/posix_child.hpp
*
* Includes the declaration of the posix_child class.
*/
#ifndef BOOST_PROCESS_POSIX_CHILD_HPP
#define BOOST_PROCESS_POSIX_CHILD_HPP
#include <boost/process/child.hpp>
#include <boost/process/pistream.hpp>
#include <boost/process/postream.hpp>
#include <boost/process/detail/pipe.hpp>
#include <boost/process/detail/posix_ops.hpp>
#include <boost/process/detail/stream_info.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/assert.hpp>
#include <map>
#include <unistd.h>
namespace boost {
namespace process {
/**
* POSIX implementation of the Child concept.
*
* The posix_child class implements the Child concept in a POSIX
* operating system.
*
* A POSIX child differs from a regular child (represented by a
* child object) in that it supports more than three communication
* channels with its parent. These channels are identified by regular
* file descriptors (integers).
*
* This class is built on top of the generic child so as to allow its
* trivial adoption. When a program is changed to use the POSIX-specific
* context (posix_context), it will most certainly need to migrate its
* use of the child class to posix_child. Doing so is only a matter of
* redefining the appropriate object and later using the required extra
* features: there should be no need to modify the existing code (e.g.
* method calls) in any other way.
*/
class posix_child : public child
{
public:
/**
* Gets a reference to the child's input stream \a desc.
*
* Returns a reference to a postream object that represents one of
* the multiple input communication channels with the child process.
* The channel is identified by \a desc as seen from the child's
* point of view. The parent can use the returned stream to send
* data to the child.
*
* Giving this function the STDIN_FILENO constant (defined in
* unistd.h) is a synonym for the get_stdin() call inherited from
* child.
*/
postream &get_input(int desc) const
{
if (desc == STDIN_FILENO)
return posix_child::get_stdin();
else
{
input_map_t::const_iterator it = input_map_.find(desc);
BOOST_ASSERT(it != input_map_.end());
return *it->second;
}
}
/**
* Gets a reference to the child's output stream \a desc.
*
* Returns a reference to a pistream object that represents one of
* the multiple output communication channels with the child process.
* The channel is identified by \a desc as seen from the child's
* point of view. The parent can use the returned stream to retrieve
* data from the child.
*
* Giving this function the STDOUT_FILENO or STDERR_FILENO constants
* (both defined in unistd.h) are synonyms for the get_stdout() and
* get_stderr() calls inherited from child, respectively.
*/
pistream &get_output(int desc) const
{
if (desc == STDOUT_FILENO)
return posix_child::get_stdout();
else if (desc == STDERR_FILENO)
return posix_child::get_stderr();
else
{
output_map_t::const_iterator it = output_map_.find(desc);
BOOST_ASSERT(it != output_map_.end());
return *it->second;
}
}
/**
* Constructs a new POSIX child object representing a just
* spawned child process.
*
* Creates a new child object that represents the just spawned process
* \a id.
*
* The \a infoin and \a infoout maps contain the pipes used to handle
* the redirections of the child process; at the moment, no other
* stream_info types are supported. If the launcher was asked to
* redirect any of the three standard flows, their pipes must be
* present in these maps.
*/
posix_child(id_type id, detail::info_map &infoin, detail::info_map &infoout)
: child(id,
detail::posix_info_locate_pipe(infoin, STDIN_FILENO, false),
detail::posix_info_locate_pipe(infoout, STDOUT_FILENO, true),
detail::posix_info_locate_pipe(infoout, STDERR_FILENO, true))
{
for (detail::info_map::iterator it = infoin.begin(); it != infoin.end(); ++it)
{
detail::stream_info &si = it->second;
if (si.type_ == detail::stream_info::use_pipe)
{
BOOST_ASSERT(si.pipe_->wend().valid());
boost::shared_ptr<postream> st(new postream(si.pipe_->wend()));
input_map_.insert(input_map_t::value_type(it->first, st));
}
}
for (detail::info_map::iterator it = infoout.begin(); it != infoout.end(); ++it)
{
detail::stream_info &si = it->second;
if (si.type_ == detail::stream_info::use_pipe)
{
BOOST_ASSERT(si.pipe_->rend().valid());
boost::shared_ptr<pistream> st(new pistream(si.pipe_->rend()));
output_map_.insert(output_map_t::value_type(it->first, st));
}
}
}
private:
/**
* Maps child's file descriptors to postream objects.
*/
typedef std::map<int, boost::shared_ptr<postream> > input_map_t;
/**
* Contains all relationships between child's input file
* descriptors and their corresponding postream objects.
*/
input_map_t input_map_;
/**
* Maps child's file descriptors to pistream objects.
*/
typedef std::map<int, boost::shared_ptr<pistream> > output_map_t;
/**
* Contains all relationships between child's output file
* descriptors and their corresponding pistream objects.
*/
output_map_t output_map_;
};
}
}
#endif

118
boost/process/posix_context.hpp

@ -1,118 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/posix_context.hpp
*
* Includes the declaration of the posix_context class.
*/
#ifndef BOOST_PROCESS_POSIX_CONTEXT_HPP
#define BOOST_PROCESS_POSIX_CONTEXT_HPP
#include <boost/process/context.hpp>
#include <boost/process/stream_behavior.hpp>
#include <map>
#include <string>
#include <unistd.h>
namespace boost {
namespace process {
/**
* Holds a mapping between native file descriptors and their corresponding
* pipes to set up communication between the parent and the %child process.
*/
typedef std::map<int, stream_behavior> behavior_map;
template <class Path>
class posix_basic_context : public basic_work_directory_context<Path>, public environment_context
{
public:
/**
* Constructs a new POSIX-specific context.
*
* Constructs a new context. It is configured as follows:
* * All communcation channels with the child process are closed.
* * There are no channel mergings.
* * The initial work directory of the child processes is set to the
* current working directory.
* * The environment variables table is empty.
* * The credentials are the same as those of the current process.
*/
posix_basic_context()
: uid(::getuid()),
euid(::geteuid()),
gid(::getgid()),
egid(::getegid())
{
}
/**
* List of input streams that will be redirected.
*/
behavior_map input_behavior;
/**
* List of output streams that will be redirected.
*/
behavior_map output_behavior;
/**
* The user credentials.
*
* UID that specifies the user credentials to use to run the %child
* process. Defaults to the current UID.
*/
uid_t uid;
/**
* The effective user credentials.
*
* EUID that specifies the effective user credentials to use to run
* the %child process. Defaults to the current EUID.
*/
uid_t euid;
/**
* The group credentials.
*
* GID that specifies the group credentials to use to run the %child
* process. Defaults to the current GID.
*/
gid_t gid;
/**
* The effective group credentials.
*
* EGID that specifies the effective group credentials to use to run
* the %child process. Defaults to the current EGID.
*/
gid_t egid;
/**
* The chroot directory, if any.
*
* Specifies the directory in which the %child process is chrooted
* before execution. Empty if this feature is not desired.
*/
Path chroot;
};
/**
* Default instantiation of posix_basic_context.
*/
typedef posix_basic_context<std::string> posix_context;
}
}
#endif

81
boost/process/posix_operations.hpp

@ -1,81 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/posix_operations.hpp
*
* Provides miscellaneous free functions specific to POSIX operating
* systems.
*/
#ifndef BOOST_PROCESS_POSIX_OPERATIONS_HPP
#define BOOST_PROCESS_POSIX_OPERATIONS_HPP
#include <boost/process/posix_child.hpp>
#include <boost/process/posix_context.hpp>
#include <boost/process/stream_behavior.hpp>
#include <boost/process/detail/stream_info.hpp>
#include <boost/process/detail/posix_ops.hpp>
#include <sys/types.h>
namespace boost {
namespace process {
/**
* Starts a new child process.
*
* Given an executable and the set of arguments passed to it, starts
* a new process with all the parameters configured in the context.
* The context can be reused afterwards to launch other different
* processes.
*
* \return A handle to the new child process.
*/
template <class Executable, class Arguments, class Posix_Context>
inline posix_child posix_launch(const Executable &exe, const Arguments &args, const Posix_Context &ctx)
{
detail::info_map input_info;
for (behavior_map::const_iterator it = ctx.input_behavior.begin(); it != ctx.input_behavior.end(); ++it)
{
if (it->second.get_type() != stream_behavior::close)
{
detail::stream_info si = detail::stream_info(it->second, false);
input_info.insert(detail::info_map::value_type(it->first, si));
}
}
detail::info_map output_info;
for (behavior_map::const_iterator it = ctx.output_behavior.begin(); it != ctx.output_behavior.end(); ++it)
{
if (it->second.get_type() != stream_behavior::close)
{
detail::stream_info si = detail::stream_info(it->second, true);
output_info.insert(detail::info_map::value_type(it->first, si));
}
}
detail::posix_setup s;
s.work_directory = ctx.work_directory;
s.uid = ctx.uid;
s.euid = ctx.euid;
s.gid = ctx.gid;
s.egid = ctx.egid;
s.chroot = ctx.chroot;
pid_t pid = detail::posix_start(exe, args, ctx.environment, input_info, output_info, s);
return posix_child(pid, input_info, output_info);
}
}
}
#endif

128
boost/process/posix_status.hpp

@ -1,128 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/posix_status.hpp
*
* Includes the declaration of the posix_status class.
*/
#ifndef BOOST_PROCESS_POSIX_STATUS_HPP
#define BOOST_PROCESS_POSIX_STATUS_HPP
#include <boost/process/config.hpp>
#if defined(BOOST_POSIX_API)
# include <sys/wait.h>
#elif defined(BOOST_WINDOWS_API)
#else
# error "Unsupported platform."
#endif
#include <boost/process/status.hpp>
#include <boost/assert.hpp>
namespace boost {
namespace process {
/**
* Status returned by a finalized %child process on a POSIX system.
*
* This class represents the %status returned by a child process after it
* has terminated. It contains some methods not available in the status
* class that provide information only available in POSIX systems.
*/
class posix_status : public status
{
public:
/**
* Creates a posix_status object from an existing status object.
*
* Creates a new status object representing the exit status of a
* child process. The construction is done based on an existing
* status object which already contains all the available
* information: this class only provides controlled access to it.
*/
posix_status(const status &s)
: status(s)
{
}
/**
* Returns whether the process exited due to an external
* signal.
*/
bool signaled() const
{
return WIFSIGNALED(flags_);
}
/**
* If signaled, returns the terminating signal code.
*
* If the process was signaled, returns the terminating signal code.
*
* \pre signaled() is true.
*/
int term_signal() const
{
BOOST_ASSERT(signaled());
return WTERMSIG(flags_);
}
/**
* If signaled, returns whether the process dumped core.
*
* If the process was signaled, returns whether the process
* produced a core dump.
*
* \pre signaled() is true.
*/
bool dumped_core() const
{
BOOST_ASSERT(signaled());
#ifdef WCOREDUMP
return WCOREDUMP(flags_);
#else
return false;
#endif
}
/**
* Returns whether the process was stopped by an external
* signal.
*/
bool stopped() const
{
return WIFSTOPPED(flags_);
}
/**
* If stopped, returns the stop signal code.
*
* If the process was stopped, returns the stop signal code.
*
* \pre stopped() is true.
*/
int stop_signal() const
{
BOOST_ASSERT(stopped());
return WSTOPSIG(flags_);
}
};
}
}
#endif

117
boost/process/postream.hpp

@ -1,117 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/postream.hpp
*
* Includes the declaration of the postream class.
*/
#ifndef BOOST_PROCESS_POSTREAM_HPP
#define BOOST_PROCESS_POSTREAM_HPP
#include <boost/process/detail/file_handle.hpp>
#include <boost/process/detail/systembuf.hpp>
#include <boost/noncopyable.hpp>
#include <ostream>
namespace boost {
namespace process {
/**
* Child process' input stream.
*
* The postream class represents an input communication channel with the
* child process. The child process reads data from this stream and the
* parent process can write to it through the postream object. In other
* words, from the child's point of view, the communication channel is an
* input one, but from the parent's point of view it is an output one;
* hence the confusing postream name.
*
* postream objects cannot be copied because they own the file handle
* they use to communicate with the child and because they buffer data
* that flows through the communication channel.
*
* A postream object behaves as a std::ostream stream in all senses.
* The class is only provided because it must provide a method to let
* the caller explicitly close the communication channel.
*
* \remark Blocking remarks: Functions that write data to this
* stream can block if the associated file handle blocks during
* the write. As this class is used to communicate with child
* processes through anonymous pipes, the most typical blocking
* condition happens when the child is not processing the data
* in the pipe's system buffer. When this happens, the buffer
* eventually fills up and the system blocks until the reader
* consumes some data, leaving some new room.
*/
class postream : public std::ostream, public boost::noncopyable
{
public:
/**
* Creates a new process' input stream.
*
* Given a file handle, this constructor creates a new postream
* object that owns the given file handle \a fh. Ownership of
* \a fh is transferred to the created postream object.
*
* \pre \a fh is valid.
* \post \a fh is invalid.
* \post The new postream object owns \a fh.
*/
explicit postream(detail::file_handle &fh)
: std::ostream(0),
handle_(fh),
systembuf_(handle_.get())
{
rdbuf(&systembuf_);
}
/**
* Returns the file handle managed by this stream.
*
* The file handle must not be copied. Copying invalidates
* the source file handle making the postream unusable.
*/
detail::file_handle &handle()
{
return handle_;
}
/**
* Closes the file handle managed by this stream.
*
* Explicitly closes the file handle managed by this stream. This
* function can be used by the user to tell the child process there
* is no more data to send.
*/
void close()
{
systembuf_.sync();
handle_.close();
}
private:
/**
* The file handle managed by this stream.
*/
detail::file_handle handle_;
/**
* The systembuf object used to manage this stream's data.
*/
detail::systembuf systembuf_;
};
}
}
#endif

130
boost/process/process.hpp

@ -1,130 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/process.hpp
*
* Includes the declaration of the process class.
*/
#ifndef BOOST_PROCESS_PROCESS_HPP
#define BOOST_PROCESS_PROCESS_HPP
#include <boost/process/config.hpp>
#if defined(BOOST_POSIX_API)
# include <cerrno>
# include <signal.h>
#elif defined(BOOST_WINDOWS_API)
# include <cstdlib>
# include <windows.h>
#else
# error "Unsupported platform."
#endif
#include <boost/system/system_error.hpp>
#include <boost/throw_exception.hpp>
namespace boost {
namespace process {
/**
* Generic implementation of the Process concept.
*
* The process class implements the Process concept in an operating system
* agnostic way.
*/
class process
{
public:
#if defined(BOOST_PROCESS_DOXYGEN)
/**
* Opaque name for the native process' identifier type.
*
* Each operating system identifies processes using a specific type.
* The \a id_type type is used to transparently refer to a process
* regardless of the operating system in which this class is used.
*
* This type is guaranteed to be an integral type on all supported
* platforms.
*/
typedef NativeProcessId id_type;
#elif defined(BOOST_POSIX_API)
typedef pid_t id_type;
#elif defined(BOOST_WINDOWS_API)
typedef DWORD id_type;
#endif
/**
* Constructs a new process object.
*
* Creates a new process object that represents a running process
* within the system.
*/
process(id_type id)
: id_(id)
{
}
/**
* Returns the process' identifier.
*/
id_type get_id() const
{
return id_;
}
/**
* Terminates the process execution.
*
* Forces the termination of the process execution. Some platforms
* allow processes to ignore some external termination notifications
* or to capture them for a proper exit cleanup. You can set the
* \a force flag to true in them to force their termination regardless
* of any exit handler.
*
* After this call, accessing this object can be dangerous because the
* process identifier may have been reused by a different process. It
* might still be valid, though, if the process has refused to die.
*
* \throw boost::system::system_error If the system call used to
* terminate the process fails.
*/
void terminate(bool force = false) const
{
#if defined(BOOST_POSIX_API)
if (::kill(id_, force ? SIGKILL : SIGTERM) == -1)
boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::process::terminate: kill(2) failed"));
#elif defined(BOOST_WINDOWS_API)
HANDLE h = ::OpenProcess(PROCESS_TERMINATE, FALSE, id_);
if (h == NULL)
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::process::terminate: OpenProcess failed"));
if (!::TerminateProcess(h, EXIT_FAILURE))
{
::CloseHandle(h);
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::process::terminate: TerminateProcess failed"));
}
if (!::CloseHandle(h))
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::process::terminate: CloseHandle failed"));
#endif
}
private:
/**
* The process' identifier.
*/
id_type id_;
};
}
}
#endif

134
boost/process/self.hpp

@ -1,134 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/self.hpp
*
* Includes the declaration of the self class.
*/
#ifndef BOOST_PROCESS_SELF_HPP
#define BOOST_PROCESS_SELF_HPP
#include <boost/process/config.hpp>
#if defined(BOOST_POSIX_API)
# include <unistd.h>
#elif defined(BOOST_WINDOWS_API)
# include <windows.h>
#else
# error "Unsupported platform."
#endif
#include <boost/process/process.hpp>
#include <boost/process/environment.hpp>
#include <boost/system/system_error.hpp>
#include <boost/throw_exception.hpp>
#include <boost/noncopyable.hpp>
#include <string>
#if defined(BOOST_POSIX_API)
extern "C"
{
extern char **environ;
}
#endif
namespace boost {
namespace process {
/**
* Generic implementation of the Process concept.
*
* The self singleton provides access to the current process.
*/
class self : public process, boost::noncopyable
{
public:
/**
* Returns the self instance representing the caller's process.
*/
static self &get_instance()
{
static self *instance = 0;
if (!instance)
instance = new self;
return *instance;
}
/**
* Returns the current environment.
*
* Returns the current process' environment variables. Modifying the
* returned object has no effect on the current environment.
*/
static environment get_environment()
{
environment e;
#if defined(BOOST_POSIX_API)
char **env = ::environ;
while (*env)
{
std::string s = *env;
std::string::size_type pos = s.find('=');
e.insert(boost::process::environment::value_type(s.substr(0, pos), s.substr(pos + 1)));
++env;
}
#elif defined(BOOST_WINDOWS_API)
#ifdef GetEnvironmentStrings
#undef GetEnvironmentStrings
#endif
char *environ = ::GetEnvironmentStrings();
if (!environ)
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::self::get_environment: GetEnvironmentStrings failed"));
try
{
char *env = environ;
while (*env)
{
std::string s = env;
std::string::size_type pos = s.find('=');
e.insert(boost::process::environment::value_type(s.substr(0, pos), s.substr(pos + 1)));
env += s.size() + 1;
}
}
catch (...)
{
::FreeEnvironmentStringsA(environ);
throw;
}
::FreeEnvironmentStringsA(environ);
#endif
return e;
}
private:
/**
* Constructs a new self object.
*
* Creates a new self object that represents the current process.
*/
self() :
#if defined(BOOST_POSIX_API)
process(::getpid())
#elif defined(BOOST_WINDOWS_API)
process(::GetCurrentProcessId())
#endif
{
}
};
}
}
#endif

105
boost/process/status.hpp

@ -1,105 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/status.hpp
*
* Includes the declaration of the status class.
*/
#ifndef BOOST_PROCESS_STATUS_HPP
#define BOOST_PROCESS_STATUS_HPP
#include <boost/process/config.hpp>
#if defined(BOOST_POSIX_API)
# include <sys/wait.h>
#elif defined(BOOST_WINDOWS_API)
#else
# error "Unsupported platform."
#endif
#include <boost/assert.hpp>
namespace boost {
namespace process {
class child;
/**
* Status returned by a finalized %child process.
*
* This class represents the %status returned by a child process after it
* has terminated. It only provides that information available under all
* supported platforms.
*
* \see posix_status
*/
class status
{
friend class child;
public:
/**
* Returns whether the process exited gracefully or not.
*/
bool exited() const
{
#if defined(BOOST_POSIX_API)
return WIFEXITED(flags_);
#elif defined(BOOST_WINDOWS_API)
return true;
#endif
}
/**
* If exited, returns the exit code.
*
* If the process exited, returns the exit code it returned.
*
* \pre exited() is true.
*/
int exit_status() const
{
BOOST_ASSERT(exited());
#if defined(BOOST_POSIX_API)
return WEXITSTATUS(flags_);
#elif defined(BOOST_WINDOWS_API)
return flags_;
#endif
}
protected:
/**
* Creates a status object based on exit information.
*
* Creates a new status object representing the exit status of a
* child process.
*
* \param flags In a POSIX system this parameter contains the
* flags returned by the ::waitpid() call. In a
* Windows system it contains the exit code only.
*/
status(int flags)
: flags_(flags)
{
}
/**
* OS-specific codification of exit status.
*/
int flags_;
};
}
}
#endif

234
boost/process/stream_behavior.hpp

@ -1,234 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/stream_behavior.hpp
*
* Includes the declaration of the stream_behavior class and associated
* free functions.
*/
#ifndef BOOST_PROCESS_STREAM_BEHAVIOR_HPP
#define BOOST_PROCESS_STREAM_BEHAVIOR_HPP
#include <boost/process/config.hpp>
namespace boost {
namespace process {
namespace detail {
struct stream_info;
}
/**
* Describes the possible states for a communication stream.
*/
class stream_behavior
{
public:
friend struct detail::stream_info;
friend stream_behavior capture_stream();
friend stream_behavior close_stream();
friend stream_behavior inherit_stream();
friend stream_behavior redirect_stream_to_stdout();
friend stream_behavior silence_stream();
#if defined(BOOST_POSIX_API) || defined(BOOST_PROCESS_DOXYGEN)
friend stream_behavior posix_redirect_stream(int to);
#endif
/**
* Describes the possible states for a communication stream.
*/
enum type
{
/**
* The child's stream is connected to the parent by using an
* anonymous pipe so that they can send and receive data to/from
* each other.
*/
capture,
/**
* The child's stream is closed upon startup so that it will not
* have any access to it.
*/
close,
/**
* The child's stream is connected to the same stream used by the
* parent. In other words, the corresponding parent's stream is
* inherited.
*/
inherit,
/**
* The child's stream is connected to child's standard output.
* This is typically used when configuring the standard error
* stream.
*/
redirect_to_stdout,
/**
* The child's stream is redirected to a null device so that its
* input is always zero or its output is lost, depending on
* whether the stream is an input or an output one. It is
* important to notice that this is different from close because
* the child is still able to write data. If we closed, e.g.
* stdout, the child might not work at all!
*/
silence,
#if defined(BOOST_POSIX_API) || defined(BOOST_PROCESS_DOXYGEN)
/**
* The child redirects the stream's output to the provided file
* descriptor. This is a generalization of the portable
* redirect_to_stdout behavior.
*/
posix_redirect
#endif
};
/**
* Constructs a new stream behavior of type close.
*
* The public constructor creates a new stream behavior that defaults
* to the close behavior. In general, you will want to use the
* available free functions to construct a stream behavior (including
* the close one).
*/
stream_behavior()
: type_(stream_behavior::close)
{
}
/**
* Returns this stream's behavior type.
*/
type get_type() const
{
return type_;
}
private:
/**
* Constructs a new stream behavior of type \a t.
*
* Constructs a new stream behavior of type \a t. It is the
* responsibility of the caller to fill in any other attributes
* required by the specified type, if any.
*/
stream_behavior(type t)
: type_(t)
{
}
/**
* This stream's behavior type.
*/
type type_;
#if defined(BOOST_POSIX_API) || defined(BOOST_PROCESS_DOXYGEN)
/**
* File descriptor the stream is redirected to.
*/
int desc_to_;
#endif
};
/**
* Creates a new stream_behavior of type stream_behavior::capture.
*
* Creates a new stream_behavior of type stream_behavior::capture,
* meaning that the child's stream is connected to the parent by using an
* anonymous pipe so that they can send and receive data to/from each
* other.
*/
inline stream_behavior capture_stream()
{
return stream_behavior(stream_behavior::capture);
}
/**
* Creates a new stream_behavior of type stream_behavior::close.
*
* Creates a new stream_behavior of type stream_behavior::close,
* meaning that the child's stream is closed upon startup so that it
* will not have any access to it.
*/
inline stream_behavior close_stream()
{
return stream_behavior(stream_behavior::close);
}
/**
* Creates a new stream_behavior of type stream_behavior::inherit.
*
* Creates a new stream_behavior of type stream_behavior::inherit,
* meaning that the child's stream is connected to the same stream used
* by the parent. In other words, the corresponding parent's stream is
* inherited.
*/
inline stream_behavior inherit_stream()
{
return stream_behavior(stream_behavior::inherit);
}
/**
* Creates a new stream_behavior of type
* stream_behavior::redirect_to_stdout.
*
* Creates a new stream_behavior of type
* stream_behavior::redirect_to_stdout, meaning that the child's stream is
* connected to child's standard output. This is typically used when
* configuring the standard error stream.
*/
inline stream_behavior redirect_stream_to_stdout()
{
return stream_behavior(stream_behavior::redirect_to_stdout);
}
/**
* Creates a new stream_behavior of type stream_behavior::silence.
*
* Creates a new stream_behavior of type stream_behavior::silence,
* meaning that the child's stream is redirected to a null device so that
* its input is always zero or its output is lost, depending on whether
* the stream is an input or an output one. It is important to notice
* that this is different from close because the child is still able to
* write data. If we closed, e.g. stdout, the child might not work at
* all!
*/
inline stream_behavior silence_stream()
{
return stream_behavior(stream_behavior::silence);
}
#if defined(BOOST_POSIX_API) || defined(BOOST_PROCESS_DOXYGEN)
/**
* Creates a new stream_behavior of type stream_behavior::posix_redirect.
*
* Creates a new stream_behavior of type stream_behavior::posix_redirect,
* meaning that the child's stream is redirected to the \a to child's
* file descriptor. This is a generalization of the portable
* redirect_stream_to_stdout() behavior.
*/
inline stream_behavior posix_redirect_stream(int to)
{
stream_behavior sb(stream_behavior::posix_redirect);
sb.desc_to_ = to;
return sb;
}
#endif
}
}
#endif

128
boost/process/win32_child.hpp

@ -1,128 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/win32_child.hpp
*
* Includes the declaration of the win32_child class.
*/
#ifndef BOOST_PROCESS_WIN32_CHILD_HPP
#define BOOST_PROCESS_WIN32_CHILD_HPP
#include <boost/process/child.hpp>
#include <boost/process/detail/file_handle.hpp>
#include <windows.h>
namespace boost {
namespace process {
/**
* Windows implementation of the Child concept.
*
* The win32_child class implements the Child concept in a Windows
* operating system.
*
* A Windows child differs from a regular %child (represented by a
* child object) in that it holds additional information about a process.
* Aside from the standard handle, it also includes a handle to the
* process' main thread, together with identifiers to both entities.
*
* This class is built on top of the generic child so as to allow its
* trivial adoption. When a program is changed to use the
* Windows-specific context (win32_context), it will most certainly need
* to migrate its use of the child class to win32_child. Doing so is only
* a matter of redefining the appropriate object and later using the
* required extra features: there should be no need to modify the existing
* code (e.g. method calls) in any other way.
*/
class win32_child : public child
{
public:
/**
* Constructs a new Windows child object representing a just
* spawned %child process.
*
* Creates a new %child object that represents the process described by
* the \a pi structure.
*
* The \a fhstdin, \a fhstdout and \a fhstderr parameters hold the
* communication streams used to interact with the %child process if
* the launcher configured redirections. See the parent class'
* constructor for more details on these.
*
* \see child
*/
win32_child(const PROCESS_INFORMATION &pi, detail::file_handle fhstdin, detail::file_handle fhstdout, detail::file_handle fhstderr)
: child(pi.dwProcessId, fhstdin, fhstdout, fhstderr, pi.hProcess),
process_information_(pi),
thread_handle_(process_information_.hThread)
{
}
/**
* Returns the process handle.
*
* Returns a process-specific handle that can be used to access the
* process. This is the value of the \a hProcess field in the
* PROCESS_INFORMATION structure returned by CreateProcess().
*
* \see get_id()
*/
HANDLE get_handle() const
{
return process_information_.hProcess;
}
/**
* Returns the primary thread's handle.
*
* Returns a handle to the primary thread of the new process. This is
* the value of the \a hThread field in the PROCESS_INFORMATION
* structure returned by CreateProcess().
*
* \see get_primary_thread_id()
*/
HANDLE get_primary_thread_handle() const
{
return process_information_.hThread;
}
/**
* Returns the primary thread's identifier.
*
* Returns a system-wide value that identifies the process's primary
* thread. This is the value of the \a dwThreadId field in the
* PROCESS_INFORMATION structure returned by CreateProcess().
*
* \see get_primary_thread_handle()
*/
DWORD get_primary_thread_id() const
{
return process_information_.dwThreadId;
}
private:
/**
* Windows-specific process information.
*/
PROCESS_INFORMATION process_information_;
/**
* Thread handle owned by RAII object.
*/
detail::file_handle thread_handle_;
};
}
}
#endif

61
boost/process/win32_context.hpp

@ -1,61 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/win_32context.hpp
*
* Includes the declaration of the win32_context class.
*/
#ifndef BOOST_PROCESS_WIN32_CONTEXT_HPP
#define BOOST_PROCESS_WIN32_CONTEXT_HPP
#include <boost/process/context.hpp>
#include <string>
#include <windows.h>
namespace boost {
namespace process {
/**
* Generic implementation of the Context concept.
*
* The context class implements the Context concept in an operating
* system agnostic way; it allows spawning new child processes using
* a single and common interface across different systems.
*/
template <class String>
class win32_basic_context : public basic_context<String>
{
public:
/**
* Initializes the Win32-specific process startup information with NULL.
*/
win32_basic_context()
: startupinfo(NULL)
{
}
/**
* Win32-specific process startup information.
*/
STARTUPINFOA *startupinfo;
};
/**
* Default instantiation of win32_basic_context.
*/
typedef win32_basic_context<std::string> win32_context;
}
}
#endif

77
boost/process/win32_operations.hpp

@ -1,77 +0,0 @@
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008, 2009 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
/**
* \file boost/process/win32_operations.hpp
*
* Provides miscellaneous free functions specific to Windows operating
* systems.
*/
#ifndef BOOST_PROCESS_WIN32_OPERATIONS_HPP
#define BOOST_PROCESS_WIN32_OPERATIONS_HPP
#include <boost/process/win32_child.hpp>
#include <boost/process/detail/file_handle.hpp>
#include <boost/process/detail/stream_info.hpp>
#include <boost/process/detail/win32_ops.hpp>
#include <windows.h>
namespace boost {
namespace process {
/**
* Starts a new child process.
*
* Given an executable and the set of arguments passed to it, starts
* a new process with all the parameters configured in the context.
* The context can be reused afterwards to launch other different
* processes.
*
* \return A handle to the new child process.
*/
template <class Executable, class Arguments, class Win32_Context>
inline win32_child win32_launch(const Executable &exe, const Arguments &args, const Win32_Context &ctx)
{
detail::file_handle fhstdin, fhstdout, fhstderr;
detail::stream_info behin = detail::stream_info(ctx.stdin_behavior, false);
if (behin.type_ == detail::stream_info::use_pipe)
fhstdin = behin.pipe_->wend();
detail::stream_info behout = detail::stream_info(ctx.stdout_behavior, true);
if (behout.type_ == detail::stream_info::use_pipe)
fhstdout = behout.pipe_->rend();
detail::stream_info beherr = detail::stream_info(ctx.stderr_behavior, true);
if (beherr.type_ == detail::stream_info::use_pipe)
fhstderr = beherr.pipe_->rend();
detail::win32_setup s;
s.work_directory = ctx.work_directory;
STARTUPINFOA si;
if (!ctx.startupinfo)
{
::ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
s.startupinfo = &si;
}
else
s.startupinfo = ctx.startupinfo;
PROCESS_INFORMATION pi = detail::win32_start(exe, args, ctx.environment, behin, behout, beherr, s);
return win32_child(pi, fhstdin, fhstdout, fhstderr);
}
}
}
#endif

2364
doc/Doxyfile

File diff suppressed because it is too large

1
evmjit

@ -0,0 +1 @@
Subproject commit 1b490244bf4864b96448d56a7cd20f3d5b0a0b9b

2
libdevcore/Common.cpp

@ -27,7 +27,7 @@ using namespace dev;
namespace dev namespace dev
{ {
char const* Version = "0.7.12"; char const* Version = "0.8.0";
} }

2
libdevcore/CommonIO.cpp

@ -65,6 +65,8 @@ bytes dev::contents(std::string const& _file)
// get length of file: // get length of file:
is.seekg (0, is.end); is.seekg (0, is.end);
streamoff length = is.tellg(); streamoff length = is.tellg();
if (length == 0) // return early, MSVC does not like reading 0 bytes
return {};
is.seekg (0, is.beg); is.seekg (0, is.beg);
bytes ret(length); bytes ret(length);
is.read((char*)ret.data(), length); is.read((char*)ret.data(), length);

52
libdevcore/Diff.h

@ -0,0 +1,52 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file Diff.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
namespace dev
{
enum class ExistDiff
{
Same,
New,
Dead
};
template <class T>
class Diff
{
public:
Diff() {}
Diff(T _from, T _to): m_from(_from), m_to(_to) {}
T const& from() const { return m_from; }
T const& to() const { return m_to; }
explicit operator bool() const { return m_from != m_to; }
private:
T m_from;
T m_to;
};
}

3
libethcore/BlockInfo.cpp

@ -22,6 +22,7 @@
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/RLP.h> #include <libdevcore/RLP.h>
#include <libdevcrypto/TrieDB.h> #include <libdevcrypto/TrieDB.h>
#include <libethcore/CommonEth.h>
#include "ProofOfWork.h" #include "ProofOfWork.h"
#include "Exceptions.h" #include "Exceptions.h"
#include "BlockInfo.h" #include "BlockInfo.h"
@ -190,7 +191,7 @@ u256 BlockInfo::calculateDifficulty(BlockInfo const& _parent) const
if (!parentHash) if (!parentHash)
return c_genesisDifficulty; return c_genesisDifficulty;
else else
return timestamp >= _parent.timestamp + 5 ? _parent.difficulty - (_parent.difficulty >> 10) : (_parent.difficulty + (_parent.difficulty >> 10)); return timestamp >= _parent.timestamp + (c_protocolVersion == 49 ? 5 : 8) ? _parent.difficulty - (_parent.difficulty >> 10) : (_parent.difficulty + (_parent.difficulty >> 10));
} }
void BlockInfo::verifyParent(BlockInfo const& _parent) const void BlockInfo::verifyParent(BlockInfo const& _parent) const

2
libethcore/CommonEth.cpp

@ -48,7 +48,7 @@ static const vector<pair<u256, string>> g_units =
{((u256(1000000000) * 1000000000) * 1000000000) * 1000, "Tether"}, {((u256(1000000000) * 1000000000) * 1000000000) * 1000, "Tether"},
{(u256(1000000000) * 1000000000) * 1000000000, "Gether"}, {(u256(1000000000) * 1000000000) * 1000000000, "Gether"},
{(u256(1000000000) * 1000000000) * 1000000, "Mether"}, {(u256(1000000000) * 1000000000) * 1000000, "Mether"},
{(u256(1000000000) * 1000000000) * 1000, "Kether"}, {(u256(1000000000) * 1000000000) * 1000, "grand"},
{u256(1000000000) * 1000000000, "ether"}, {u256(1000000000) * 1000000000, "ether"},
{u256(1000000000) * 1000000, "finney"}, {u256(1000000000) * 1000000, "finney"},
{u256(1000000000) * 1000, "szabo"}, {u256(1000000000) * 1000, "szabo"},

8
libethcore/CommonEth.h

@ -48,7 +48,7 @@ std::vector<std::pair<u256, std::string>> const& units();
using LogBloom = h512; using LogBloom = h512;
// The various denominations; here for ease of use where needed within code. // The various denominations; here for ease of use where needed within code.
static const u256 Uether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000; /*static const u256 Uether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000;
static const u256 Vether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000; static const u256 Vether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000;
static const u256 Dether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000; static const u256 Dether = ((((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000;
static const u256 Nether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000; static const u256 Nether = (((u256(1000000000) * 1000000000) * 1000000000) * 1000000000) * 1000000000;
@ -59,13 +59,13 @@ static const u256 Pether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000
static const u256 Tether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000; static const u256 Tether = ((u256(1000000000) * 1000000000) * 1000000000) * 1000;
static const u256 Gether = (u256(1000000000) * 1000000000) * 1000000000; static const u256 Gether = (u256(1000000000) * 1000000000) * 1000000000;
static const u256 Mether = (u256(1000000000) * 1000000000) * 1000000; static const u256 Mether = (u256(1000000000) * 1000000000) * 1000000;
static const u256 Kether = (u256(1000000000) * 1000000000) * 1000; static const u256 grand = (u256(1000000000) * 1000000000) * 1000;*/
static const u256 ether = u256(1000000000) * 1000000000; static const u256 ether = u256(1000000000) * 1000000000;
static const u256 finney = u256(1000000000) * 1000000; static const u256 finney = u256(1000000000) * 1000000;
static const u256 szabo = u256(1000000000) * 1000; static const u256 szabo = u256(1000000000) * 1000;
static const u256 Gwei = u256(1000000000); /*static const u256 Gwei = u256(1000000000);
static const u256 Mwei = u256(1000000); static const u256 Mwei = u256(1000000);
static const u256 Kwei = u256(1000); static const u256 Kwei = u256(1000);*/
static const u256 wei = u256(1); static const u256 wei = u256(1);
} }

18
libethereum/AccountDiff.cpp

@ -33,11 +33,19 @@ AccountChange AccountDiff::changeType() const
return exist ? exist.from() ? AccountChange::Deletion : AccountChange::Creation : (bn && sc) ? AccountChange::All : bn ? AccountChange::Intrinsic: sc ? AccountChange::CodeStorage : AccountChange::None; return exist ? exist.from() ? AccountChange::Deletion : AccountChange::Creation : (bn && sc) ? AccountChange::All : bn ? AccountChange::Intrinsic: sc ? AccountChange::CodeStorage : AccountChange::None;
} }
char const* AccountDiff::lead() const char const* dev::eth::lead(AccountChange _c)
{ {
bool bn = (balance || nonce); switch (_c)
bool sc = (!storage.empty() || code); {
return exist ? exist.from() ? "XXX" : "+++" : (bn && sc) ? "***" : bn ? " * " : sc ? "* *" : " "; case AccountChange::None: return " ";
case AccountChange::Creation: return "+++";
case AccountChange::Deletion: return "XXX";
case AccountChange::Intrinsic: return " * ";
case AccountChange::CodeStorage: return "* *";
case AccountChange::All: return "***";
}
assert(false);
return "";
} }
namespace dev { namespace dev {
@ -77,7 +85,7 @@ std::ostream& operator<<(std::ostream& _out, dev::eth::StateDiff const& _s)
dev::eth::AccountDiff d; dev::eth::AccountDiff d;
_out << d; _out << d;
for (auto const& i: _s.accounts) for (auto const& i: _s.accounts)
_out << i.second.lead() << " " << i.first << ": " << i.second << endl; _out << lead(i.second.changeType()) << " " << i.first << ": " << i.second << endl;
return _out; return _out;
} }

55
libethereum/AccountDiff.h

@ -22,6 +22,7 @@
#pragma once #pragma once
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/Diff.h>
#include <libethcore/CommonEth.h> #include <libethcore/CommonEth.h>
namespace dev namespace dev
@ -29,47 +30,55 @@ namespace dev
namespace eth namespace eth
{ {
enum class ExistDiff { Same, New, Dead }; /// Type of change that an account can have from state to state.
template <class T> enum class AccountChange
class Diff
{ {
public: None, ///< Nothing changed at all.
Diff() {} Creation, ///< Account came into existance.
Diff(T _from, T _to): m_from(_from), m_to(_to) {} Deletion, ///< Account was deleted.
Intrinsic, ///< Account was already in existance and some internal aspect of the account altered such as balance, nonce or storage.
T const& from() const { return m_from; } CodeStorage, ///< Account was already in existance and the code of the account changed.
T const& to() const { return m_to; } All ///< Account was already in existance and all aspects of the account changed.
explicit operator bool() const { return m_from != m_to; }
private:
T m_from;
T m_to;
}; };
enum class AccountChange { None, Creation, Deletion, Intrinsic, CodeStorage, All }; /// @returns a three-character code that expresses the type of change.
char const* lead(AccountChange _c);
/**
* @brief Stores the difference between two accounts (typically the same account at two times).
*
* In order to determine what about an account has altered, this struct can be used to specify
* alterations. Use changed() and changeType() to determine what, if anything, is different.
*
* Five members are accessible: to determine the nature of the changes.
*/
struct AccountDiff struct AccountDiff
{ {
/// @returns true if the account has changed at all.
inline bool changed() const { return storage.size() || code || nonce || balance || exist; } inline bool changed() const { return storage.size() || code || nonce || balance || exist; }
char const* lead() const; /// @returns a three-character code that expresses the change.
AccountChange changeType() const; AccountChange changeType() const;
Diff<bool> exist; Diff<bool> exist; ///< The account's existance; was it created/deleted or not?
Diff<u256> balance; Diff<u256> balance; ///< The account's balance; did it alter?
Diff<u256> nonce; Diff<u256> nonce; ///< The account's nonce; did it alter?
std::map<u256, Diff<u256>> storage; std::map<u256, Diff<u256>> storage; ///< The account's storage addresses; each has its own Diff.
Diff<bytes> code; Diff<bytes> code; ///< The account's code; in general this should only have changed if exist also changed.
}; };
/**
* @brief Stores the difference between two states; this is just their encumbent accounts.
*/
struct StateDiff struct StateDiff
{ {
std::map<Address, AccountDiff> accounts; std::map<Address, AccountDiff> accounts; ///< The state's account changes; each has its own AccountDiff.
}; };
} }
/// Simple stream output for the StateDiff.
std::ostream& operator<<(std::ostream& _out, dev::eth::StateDiff const& _s); std::ostream& operator<<(std::ostream& _out, dev::eth::StateDiff const& _s);
/// Simple stream output for the AccountDiff.
std::ostream& operator<<(std::ostream& _out, dev::eth::AccountDiff const& _s); std::ostream& operator<<(std::ostream& _out, dev::eth::AccountDiff const& _s);
} }

2
libethereum/State.cpp

@ -1017,7 +1017,7 @@ u256 State::execute(bytesConstRef _rlp, bytes* o_output, bool _commit)
ctrace << "Executing" << e.t() << "on" << h; ctrace << "Executing" << e.t() << "on" << h;
ctrace << toHex(e.t().rlp()); ctrace << toHex(e.t().rlp());
#endif #endif
#if ETH_TRACE #if ETH_VMTRACE
e.go(e.simpleTrace()); e.go(e.simpleTrace());
#else #else
e.go(); e.go();

3
libevm/CMakeLists.txt

@ -30,6 +30,9 @@ target_link_libraries(${EXECUTABLE} ethcore)
target_link_libraries(${EXECUTABLE} devcrypto) target_link_libraries(${EXECUTABLE} devcrypto)
target_link_libraries(${EXECUTABLE} evmcore) target_link_libraries(${EXECUTABLE} evmcore)
target_link_libraries(${EXECUTABLE} devcore) target_link_libraries(${EXECUTABLE} devcore)
if (EVMJIT)
target_link_libraries(${EXECUTABLE} evmjit-cpp)
endif()
install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

10
libevm/VMFactory.cpp

@ -18,6 +18,10 @@
#include "VMFactory.h" #include "VMFactory.h"
#include "VM.h" #include "VM.h"
#if ETH_EVMJIT
#include <evmjit/libevmjit-cpp/JitVM.h>
#endif
namespace dev namespace dev
{ {
namespace eth namespace eth
@ -34,8 +38,12 @@ void VMFactory::setKind(VMKind _kind)
std::unique_ptr<VMFace> VMFactory::create(u256 _gas) std::unique_ptr<VMFace> VMFactory::create(u256 _gas)
{ {
asserts(g_kind == VMKind::Interpreter && "Only interpreter supported for now"); #if ETH_EVMJIT
return std::unique_ptr<VMFace>(g_kind == VMKind::JIT ? (VMFace*)new JitVM(_gas) : new VM(_gas));
#else
asserts(g_kind == VMKind::Interpreter && "JIT disabled in build configuration");
return std::unique_ptr<VMFace>(new VM(_gas)); return std::unique_ptr<VMFace>(new VM(_gas));
#endif
} }
} }

105
libjsqrc/ethereum.js

@ -18,9 +18,19 @@ require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof requ
/** @file abi.js /** @file abi.js
* @authors: * @authors:
* Marek Kotewicz <marek@ethdev.com> * Marek Kotewicz <marek@ethdev.com>
* Gav Wood <g@ethdev.com>
* @date 2014 * @date 2014
*/ */
// TODO: make these be actually accurate instead of falling back onto JS's doubles.
var hexToDec = function (hex) {
return parseInt(hex, 16).toString();
};
var decToHex = function (dec) {
return parseInt(dec).toString(16);
};
var findIndex = function (array, callback) { var findIndex = function (array, callback) {
var end = false; var end = false;
var i = 0; var i = 0;
@ -36,8 +46,8 @@ var findMethodIndex = function (json, methodName) {
}); });
}; };
var padLeft = function (number, n) { var padLeft = function (string, chars) {
return (new Array(n * 2 - number.toString().length + 1)).join("0") + number; return Array(chars - string.length + 1).join("0") + string;
}; };
var setupInputTypes = function () { var setupInputTypes = function () {
@ -49,7 +59,13 @@ var setupInputTypes = function () {
} }
var padding = parseInt(type.slice(expected.length)) / 8; var padding = parseInt(type.slice(expected.length)) / 8;
return padLeft(value, padding); if (typeof value === "number")
value = value.toString(16);
else if (value.indexOf('0x') === 0)
value = value.substr(2);
else
value = (+value).toString(16);
return padLeft(value, padding * 2);
}; };
}; };
@ -59,17 +75,18 @@ var setupInputTypes = function () {
return false; return false;
} }
return padLeft(formatter ? value : formatter(value), padding); return padLeft(formatter ? formatter(value) : value, padding * 2);
}; };
}; };
var formatBool = function (value) { var formatBool = function (value) {
return value ? '1' : '0'; return value ? '0x1' : '0x0';
}; };
return [ return [
prefixedType('uint'), prefixedType('uint'),
prefixedType('int'), prefixedType('int'),
prefixedType('hash'),
namedType('address', 20), namedType('address', 20),
namedType('bool', 1, formatBool), namedType('bool', 1, formatBool),
]; ];
@ -85,16 +102,13 @@ var toAbiInput = function (json, methodName, params) {
return; return;
} }
// it needs to be checked in WebThreeStubServer bytes = "0x" + padLeft(index.toString(16), 2);
// something wrong might be with this additional zero
bytes = bytes + index + 'x' + '0';
var method = json[index]; var method = json[index];
for (var i = 0; i < method.inputs.length; i++) { for (var i = 0; i < method.inputs.length; i++) {
var found = false; var found = false;
for (var j = 0; j < inputTypes.length && !found; j++) { for (var j = 0; j < inputTypes.length && !found; j++) {
var val = parseInt(params[i]).toString(16); found = inputTypes[j](method.inputs[i].type, params[i]);
found = inputTypes[j](method.inputs[i].type, val);
} }
if (!found) { if (!found) {
console.error('unsupported json type: ' + method.inputs[i].type); console.error('unsupported json type: ' + method.inputs[i].type);
@ -119,12 +133,16 @@ var setupOutputTypes = function () {
var namedType = function (name, padding) { var namedType = function (name, padding) {
return function (type) { return function (type) {
return name === type ? padding * 2: -1; return name === type ? padding * 2 : -1;
}; };
}; };
var formatInt = function (value) { var formatInt = function (value) {
return parseInt(value, 16); return value.length <= 8 ? +parseInt(value, 16) : hexToDec(value);
};
var formatHash = function (value) {
return "0x" + value;
}; };
var formatBool = function (value) { var formatBool = function (value) {
@ -134,6 +152,7 @@ var setupOutputTypes = function () {
return [ return [
{ padding: prefixedType('uint'), format: formatInt }, { padding: prefixedType('uint'), format: formatInt },
{ padding: prefixedType('int'), format: formatInt }, { padding: prefixedType('int'), format: formatInt },
{ padding: prefixedType('hash'), format: formatHash },
{ padding: namedType('address', 20) }, { padding: namedType('address', 20) },
{ padding: namedType('bool', 1), format: formatBool } { padding: namedType('bool', 1), format: formatBool }
]; ];
@ -164,7 +183,7 @@ var fromAbiOutput = function (json, methodName, output) {
} }
var res = output.slice(0, padding); var res = output.slice(0, padding);
var formatter = outputTypes[j - 1].format; var formatter = outputTypes[j - 1].format;
result.push(formatter ? formatter(res): res); result.push(formatter ? formatter(res) : ("0x" + res));
output = output.slice(padding); output = output.slice(padding);
} }
@ -484,6 +503,7 @@ module.exports = HttpRpcProvider;
* Jeffrey Wilcke <jeff@ethdev.com> * Jeffrey Wilcke <jeff@ethdev.com>
* Marek Kotewicz <marek@ethdev.com> * Marek Kotewicz <marek@ethdev.com>
* Marian Oancea <marian@ethdev.com> * Marian Oancea <marian@ethdev.com>
* Gav Wood <g@ethdev.com>
* @date 2014 * @date 2014
*/ */
@ -526,6 +546,12 @@ function flattenPromise (obj) {
return Promise.resolve(obj); return Promise.resolve(obj);
} }
var web3Methods = function () {
return [
{ name: 'sha3', call: 'web3_sha3' }
];
};
var ethMethods = function () { var ethMethods = function () {
var blockCall = function (args) { var blockCall = function (args) {
return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber"; return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber";
@ -670,19 +696,20 @@ var setupProperties = function (obj, properties) {
}); });
}; };
// TODO: import from a dependency, don't duplicate.
var hexToDec = function (hex) {
return parseInt(hex, 16).toString();
};
var decToHex = function (dec) {
return parseInt(dec).toString(16);
};
var web3 = { var web3 = {
_callbacks: {}, _callbacks: {},
_events: {}, _events: {},
providers: {}, providers: {},
toHex: function(str) {
var hex = "";
for(var i = 0; i < str.length; i++) {
var n = str.charCodeAt(i).toString(16);
hex += n.length < 2 ? '0' + n : n;
}
return hex;
},
toAscii: function(hex) { toAscii: function(hex) {
// Find termination // Find termination
@ -702,10 +729,6 @@ var web3 = {
return str; return str;
}, },
toDecimal: function (val) {
return parseInt(val, 16);
},
fromAscii: function(str, pad) { fromAscii: function(str, pad) {
pad = pad === undefined ? 32 : pad; pad = pad === undefined ? 32 : pad;
var hex = this.toHex(str); var hex = this.toHex(str);
@ -714,6 +737,33 @@ var web3 = {
return "0x" + hex; return "0x" + hex;
}, },
toDecimal: function (val) {
return hexToDec(val.substring(2));
},
fromDecimal: function (val) {
return "0x" + decToHex(val);
},
toEth: function(str) {
var val = typeof str === "string" ? str.indexOf('0x') == 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;
var unit = 0;
var units = [ 'wei', 'Kwei', 'Mwei', 'Gwei', 'szabo', 'finney', 'ether', 'grand', 'Mether', 'Gether', 'Tether', 'Pether', 'Eether', 'Zether', 'Yether', 'Nether', 'Dether', 'Vether', 'Uether' ];
while (val > 3000 && unit < units.length - 1)
{
val /= 1000;
unit++;
}
var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2);
while (true) {
var o = s;
s = s.replace(/(\d)(\d\d\d[\.\,])/, function($0, $1, $2) { return $1 + ',' + $2; });
if (o == s)
break;
}
return s + ' ' + units[unit];
},
eth: { eth: {
prototype: Object(), // jshint ignore:line prototype: Object(), // jshint ignore:line
watch: function (params) { watch: function (params) {
@ -759,6 +809,7 @@ var web3 = {
} }
}; };
setupMethods(web3, web3Methods());
setupMethods(web3.eth, ethMethods()); setupMethods(web3.eth, ethMethods());
setupProperties(web3.eth, ethProperties()); setupProperties(web3.eth, ethProperties());
setupMethods(web3.db, dbMethods()); setupMethods(web3.db, dbMethods());
@ -1065,4 +1116,4 @@ module.exports = web3;
},{"./lib/autoprovider":2,"./lib/contract":3,"./lib/httprpc":4,"./lib/main":5,"./lib/qt":6,"./lib/websocket":7}]},{},[]) },{"./lib/autoprovider":2,"./lib/contract":3,"./lib/httprpc":4,"./lib/main":5,"./lib/qt":6,"./lib/websocket":7}]},{},[])
//# sourceMappingURL=ethereum.js.map //# sourceMappingURL=ethereum.js.map

2
libserpent/compiler.cpp

@ -173,7 +173,7 @@ programData opcodeify(Node node,
} }
// Comments do nothing // Comments do nothing
else if (node.val == "comment") { else if (node.val == "comment") {
Node nodelist[] = { }; Node* nodelist = nullptr;
return pd(aux, multiToken(nodelist, 0, m), 0); return pd(aux, multiToken(nodelist, 0, m), 0);
} }
// Custom operation sequence // Custom operation sequence

1
libserpent/rewriteutils.cpp

@ -2,6 +2,7 @@
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include <map> #include <map>
#include <string>
#include "util.h" #include "util.h"
#include "lllparser.h" #include "lllparser.h"
#include "bignum.h" #include "bignum.h"

1
libserpent/util.cpp

@ -5,6 +5,7 @@
#include "util.h" #include "util.h"
#include "bignum.h" #include "bignum.h"
#include <fstream> #include <fstream>
#include <string>
#include <cerrno> #include <cerrno>
//Token or value node constructor //Token or value node constructor

2
libsolidity/ExpressionCompiler.cpp

@ -62,6 +62,8 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2; m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;
m_currentLValue.retrieveValue(_assignment, true); m_currentLValue.retrieveValue(_assignment, true);
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType()); appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType());
if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1;
} }
m_currentLValue.storeValue(_assignment); m_currentLValue.storeValue(_assignment);
m_currentLValue.reset(); m_currentLValue.reset();

47
libsolidity/InterfaceHandler.cpp

@ -56,6 +56,7 @@ std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinitio
}; };
method["name"] = f->getName(); method["name"] = f->getName();
method["constant"] = f->isDeclaredConst();
method["inputs"] = populateParameters(f->getParameters()); method["inputs"] = populateParameters(f->getParameters());
method["outputs"] = populateParameters(f->getReturnParameters()); method["outputs"] = populateParameters(f->getReturnParameters());
methods.append(method); methods.append(method);
@ -166,9 +167,12 @@ static inline std::string::const_iterator skipLineOrEOS(std::string::const_itera
std::string::const_iterator InterfaceHandler::parseDocTagLine(std::string::const_iterator _pos, std::string::const_iterator InterfaceHandler::parseDocTagLine(std::string::const_iterator _pos,
std::string::const_iterator _end, std::string::const_iterator _end,
std::string& _tagString, std::string& _tagString,
DocTagType _tagType) DocTagType _tagType,
bool _appending)
{ {
auto nlPos = std::find(_pos, _end, '\n'); auto nlPos = std::find(_pos, _end, '\n');
if (_appending && _pos < _end && *_pos != ' ')
_tagString += " ";
std::copy(_pos, nlPos, back_inserter(_tagString)); std::copy(_pos, nlPos, back_inserter(_tagString));
m_lastTag = _tagType; m_lastTag = _tagType;
return skipLineOrEOS(nlPos, _end); return skipLineOrEOS(nlPos, _end);
@ -201,7 +205,8 @@ std::string::const_iterator InterfaceHandler::appendDocTagParam(std::string::con
solAssert(!m_params.empty(), "Internal: Tried to append to empty parameter"); solAssert(!m_params.empty(), "Internal: Tried to append to empty parameter");
auto pair = m_params.back(); auto pair = m_params.back();
pair.second += " "; if (_pos < _end && *_pos != ' ')
pair.second += " ";
auto nlPos = std::find(_pos, _end, '\n'); auto nlPos = std::find(_pos, _end, '\n');
std::copy(_pos, nlPos, back_inserter(pair.second)); std::copy(_pos, nlPos, back_inserter(pair.second));
@ -221,17 +226,17 @@ std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_ite
if (m_lastTag == DocTagType::NONE || _tag != "") if (m_lastTag == DocTagType::NONE || _tag != "")
{ {
if (_tag == "dev") if (_tag == "dev")
return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV); return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV, false);
else if (_tag == "notice") else if (_tag == "notice")
return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE); return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE, false);
else if (_tag == "return") else if (_tag == "return")
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN); return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN, false);
else if (_tag == "author") else if (_tag == "author")
{ {
if (_owner == CommentOwner::CONTRACT) if (_owner == CommentOwner::CONTRACT)
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR); return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR, false);
else if (_owner == CommentOwner::FUNCTION) else if (_owner == CommentOwner::FUNCTION)
return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR); return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR, false);
else else
// LTODO: for now this else makes no sense but later comments will go to more language constructs // LTODO: for now this else makes no sense but later comments will go to more language constructs
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag is legal only for contracts")); BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag is legal only for contracts"));
@ -239,7 +244,7 @@ std::string::const_iterator InterfaceHandler::parseDocTag(std::string::const_ite
else if (_tag == "title") else if (_tag == "title")
{ {
if (_owner == CommentOwner::CONTRACT) if (_owner == CommentOwner::CONTRACT)
return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE); return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE, false);
else else
// LTODO: Unknown tag, throw some form of warning and not just an exception // LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag is legal only for contracts")); BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag is legal only for contracts"));
@ -261,34 +266,22 @@ std::string::const_iterator InterfaceHandler::appendDocTag(std::string::const_it
switch (m_lastTag) switch (m_lastTag)
{ {
case DocTagType::DEV: case DocTagType::DEV:
m_dev += " "; return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV, true);
return parseDocTagLine(_pos, _end, m_dev, DocTagType::DEV);
case DocTagType::NOTICE: case DocTagType::NOTICE:
m_notice += " "; return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE, true);
return parseDocTagLine(_pos, _end, m_notice, DocTagType::NOTICE);
case DocTagType::RETURN: case DocTagType::RETURN:
m_return += " "; return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN, true);
return parseDocTagLine(_pos, _end, m_return, DocTagType::RETURN);
case DocTagType::AUTHOR: case DocTagType::AUTHOR:
if (_owner == CommentOwner::CONTRACT) if (_owner == CommentOwner::CONTRACT)
{ return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR, true);
m_contractAuthor += " ";
return parseDocTagLine(_pos, _end, m_contractAuthor, DocTagType::AUTHOR);
}
else if (_owner == CommentOwner::FUNCTION) else if (_owner == CommentOwner::FUNCTION)
{ return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR, true);
m_author += " ";
return parseDocTagLine(_pos, _end, m_author, DocTagType::AUTHOR);
}
else else
// LTODO: Unknown tag, throw some form of warning and not just an exception // LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag in illegal comment")); BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@author tag in illegal comment"));
case DocTagType::TITLE: case DocTagType::TITLE:
if (_owner == CommentOwner::CONTRACT) if (_owner == CommentOwner::CONTRACT)
{ return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE, true);
m_title += " ";
return parseDocTagLine(_pos, _end, m_title, DocTagType::TITLE);
}
else else
// LTODO: Unknown tag, throw some form of warning and not just an exception // LTODO: Unknown tag, throw some form of warning and not just an exception
BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag in illegal comment")); BOOST_THROW_EXCEPTION(DocstringParsingError() << errinfo_comment("@title tag in illegal comment"));
@ -329,7 +322,7 @@ void InterfaceHandler::parseDocString(std::string const& _string, CommentOwner _
currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos), _owner); currPos = parseDocTag(tagNameEndPos + 1, end, std::string(tagPos + 1, tagNameEndPos), _owner);
} }
else if (m_lastTag != DocTagType::NONE) // continuation of the previous tag else if (m_lastTag != DocTagType::NONE) // continuation of the previous tag
currPos = appendDocTag(currPos + 1, end, _owner); currPos = appendDocTag(currPos, end, _owner);
else if (currPos != end) // skip the line if a newline was found else if (currPos != end) // skip the line if a newline was found
currPos = nlPos + 1; currPos = nlPos + 1;
} }

3
libsolidity/InterfaceHandler.h

@ -92,7 +92,8 @@ private:
std::string::const_iterator parseDocTagLine(std::string::const_iterator _pos, std::string::const_iterator parseDocTagLine(std::string::const_iterator _pos,
std::string::const_iterator _end, std::string::const_iterator _end,
std::string& _tagString, std::string& _tagString,
DocTagType _tagType); DocTagType _tagType,
bool _appending);
std::string::const_iterator parseDocTagParam(std::string::const_iterator _pos, std::string::const_iterator parseDocTagParam(std::string::const_iterator _pos,
std::string::const_iterator _end); std::string::const_iterator _end);
std::string::const_iterator appendDocTagParam(std::string::const_iterator _pos, std::string::const_iterator appendDocTagParam(std::string::const_iterator _pos,

132
libsolidity/Scanner.cpp

@ -80,7 +80,7 @@ bool isLineTerminator(char c)
} }
bool isWhiteSpace(char c) bool isWhiteSpace(char c)
{ {
return c == ' ' || c == '\n' || c == '\t'; return c == ' ' || c == '\n' || c == '\t' || c == '\r';
} }
bool isIdentifierStart(char c) bool isIdentifierStart(char c)
{ {
@ -209,6 +209,15 @@ bool Scanner::skipWhitespace()
return getSourcePos() != startPosition; return getSourcePos() != startPosition;
} }
bool Scanner::skipWhitespaceExceptLF()
{
int const startPosition = getSourcePos();
while (isWhiteSpace(m_char) && !isLineTerminator(m_char))
advance();
// Return whether or not we skipped any characters.
return getSourcePos() != startPosition;
}
Token::Value Scanner::skipSingleLineComment() Token::Value Scanner::skipSingleLineComment()
{ {
// The line terminator at the end of the line is not considered // The line terminator at the end of the line is not considered
@ -219,10 +228,11 @@ Token::Value Scanner::skipSingleLineComment()
return Token::WHITESPACE; return Token::WHITESPACE;
} }
Token::Value Scanner::scanDocumentationComment() Token::Value Scanner::scanSingleLineDocComment()
{ {
LiteralScope literal(this, LITERAL_TYPE_COMMENT); LiteralScope literal(this, LITERAL_TYPE_COMMENT);
advance(); //consume the last '/' advance(); //consume the last '/' at ///
skipWhitespaceExceptLF();
while (!isSourcePastEndOfInput()) while (!isSourcePastEndOfInput())
{ {
if (isLineTerminator(m_char)) if (isLineTerminator(m_char))
@ -250,7 +260,6 @@ Token::Value Scanner::scanDocumentationComment()
Token::Value Scanner::skipMultiLineComment() Token::Value Scanner::skipMultiLineComment()
{ {
solAssert(m_char == '*', "");
advance(); advance();
while (!isSourcePastEndOfInput()) while (!isSourcePastEndOfInput())
{ {
@ -270,6 +279,97 @@ Token::Value Scanner::skipMultiLineComment()
return Token::ILLEGAL; return Token::ILLEGAL;
} }
Token::Value Scanner::scanMultiLineDocComment()
{
LiteralScope literal(this, LITERAL_TYPE_COMMENT);
bool endFound = false;
bool charsAdded = false;
advance(); //consume the last '*' at /**
skipWhitespaceExceptLF();
while (!isSourcePastEndOfInput())
{
//handle newlines in multline comments
if (isLineTerminator(m_char))
{
skipWhitespace();
if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) != '/')
{ // skip first '*' in subsequent lines
if (charsAdded)
addCommentLiteralChar('\n');
m_char = m_source.advanceAndGet(2);
}
else if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/')
{ // if after newline the comment ends, don't insert the newline
m_char = m_source.advanceAndGet(2);
endFound = true;
break;
}
else if (charsAdded)
addCommentLiteralChar('\n');
}
if (!m_source.isPastEndOfInput(1) && m_source.get(0) == '*' && m_source.get(1) == '/')
{
m_char = m_source.advanceAndGet(2);
endFound = true;
break;
}
addCommentLiteralChar(m_char);
charsAdded = true;
advance();
}
literal.complete();
if (!endFound)
return Token::ILLEGAL;
else
return Token::COMMENT_LITERAL;
}
Token::Value Scanner::scanSlash()
{
int firstSlashPosition = getSourcePos();
advance();
if (m_char == '/')
{
if (!advance()) /* double slash comment directly before EOS */
return Token::WHITESPACE;
else if (m_char == '/')
{
// doxygen style /// comment
Token::Value comment;
m_nextSkippedComment.location.start = firstSlashPosition;
comment = scanSingleLineDocComment();
m_nextSkippedComment.location.end = getSourcePos();
m_nextSkippedComment.token = comment;
return Token::WHITESPACE;
}
else
return skipSingleLineComment();
}
else if (m_char == '*')
{
// doxygen style /** natspec comment
if (!advance()) /* slash star comment before EOS */
return Token::WHITESPACE;
else if (m_char == '*')
{
Token::Value comment;
m_nextSkippedComment.location.start = firstSlashPosition;
comment = scanMultiLineDocComment();
m_nextSkippedComment.location.end = getSourcePos();
m_nextSkippedComment.token = comment;
return Token::WHITESPACE;
}
else
return skipMultiLineComment();
}
else if (m_char == '=')
return selectToken(Token::ASSIGN_DIV);
else
return Token::DIV;
}
void Scanner::scanToken() void Scanner::scanToken()
{ {
m_nextToken.literal.clear(); m_nextToken.literal.clear();
@ -372,29 +472,7 @@ void Scanner::scanToken()
break; break;
case '/': case '/':
// / // /* /= // / // /* /=
advance(); token = scanSlash();
if (m_char == '/')
{
if (!advance()) /* double slash comment directly before EOS */
token = Token::WHITESPACE;
else if (m_char == '/')
{
Token::Value comment;
m_nextSkippedComment.location.start = getSourcePos();
comment = scanDocumentationComment();
m_nextSkippedComment.location.end = getSourcePos();
m_nextSkippedComment.token = comment;
token = Token::WHITESPACE;
}
else
token = skipSingleLineComment();
}
else if (m_char == '*')
token = skipMultiLineComment();
else if (m_char == '=')
token = selectToken(Token::ASSIGN_DIV);
else
token = Token::DIV;
break; break;
case '&': case '&':
// & && &= // & && &=

7
libsolidity/Scanner.h

@ -182,6 +182,8 @@ private:
/// Skips all whitespace and @returns true if something was skipped. /// Skips all whitespace and @returns true if something was skipped.
bool skipWhitespace(); bool skipWhitespace();
/// Skips all whitespace except Line feeds and returns true if something was skipped
bool skipWhitespaceExceptLF();
Token::Value skipSingleLineComment(); Token::Value skipSingleLineComment();
Token::Value skipMultiLineComment(); Token::Value skipMultiLineComment();
@ -190,7 +192,10 @@ private:
Token::Value scanIdentifierOrKeyword(); Token::Value scanIdentifierOrKeyword();
Token::Value scanString(); Token::Value scanString();
Token::Value scanDocumentationComment(); Token::Value scanSingleLineDocComment();
Token::Value scanMultiLineDocComment();
/// Scans a slash '/' and depending on the characters returns the appropriate token
Token::Value scanSlash();
/// Scans an escape-sequence which is part of a string and adds the /// Scans an escape-sequence which is part of a string and adds the
/// decoded character to the current literal. Returns true if a pattern /// decoded character to the current literal. Returns true if a pattern

22
libsolidity/Token.h

@ -107,9 +107,9 @@ namespace solidity
T(COMMA, ",", 1) \ T(COMMA, ",", 1) \
T(OR, "||", 4) \ T(OR, "||", 4) \
T(AND, "&&", 5) \ T(AND, "&&", 5) \
T(BIT_OR, "|", 6) \ T(BIT_OR, "|", 8) \
T(BIT_XOR, "^", 7) \ T(BIT_XOR, "^", 9) \
T(BIT_AND, "&", 8) \ T(BIT_AND, "&", 10) \
T(SHL, "<<", 11) \ T(SHL, "<<", 11) \
T(SAR, ">>", 11) \ T(SAR, ">>", 11) \
T(SHR, ">>>", 11) \ T(SHR, ">>>", 11) \
@ -122,13 +122,13 @@ namespace solidity
/* Compare operators sorted by precedence. */ \ /* Compare operators sorted by precedence. */ \
/* IsCompareOp() relies on this block of enum values */ \ /* IsCompareOp() relies on this block of enum values */ \
/* being contiguous and sorted in the same order! */ \ /* being contiguous and sorted in the same order! */ \
T(EQ, "==", 9) \ T(EQ, "==", 6) \
T(NE, "!=", 9) \ T(NE, "!=", 6) \
T(LT, "<", 10) \ T(LT, "<", 7) \
T(GT, ">", 10) \ T(GT, ">", 7) \
T(LTE, "<=", 10) \ T(LTE, "<=", 7) \
T(GTE, ">=", 10) \ T(GTE, ">=", 7) \
K(IN, "in", 10) \ K(IN, "in", 7) \
\ \
/* Unary operators. */ \ /* Unary operators. */ \
/* IsUnaryOp() relies on this block of enum values */ \ /* IsUnaryOp() relies on this block of enum values */ \
@ -142,7 +142,7 @@ namespace solidity
/* Keywords */ \ /* Keywords */ \
K(BREAK, "break", 0) \ K(BREAK, "break", 0) \
K(CASE, "case", 0) \ K(CASE, "case", 0) \
K(CONST, "const", 0) \ K(CONST, "constant", 0) \
K(CONTINUE, "continue", 0) \ K(CONTINUE, "continue", 0) \
K(CONTRACT, "contract", 0) \ K(CONTRACT, "contract", 0) \
K(DEFAULT, "default", 0) \ K(DEFAULT, "default", 0) \

5
libweb3jsonrpc/WebThreeStubServer.cpp

@ -244,6 +244,11 @@ std::shared_ptr<dev::shh::Interface> WebThreeStubServer::face() const
return m_web3.whisper(); return m_web3.whisper();
} }
std::string WebThreeStubServer::web3_sha3(std::string const& _param1)
{
return toJS(sha3(jsToBytes(_param1)));
}
Json::Value WebThreeStubServer::eth_accounts() Json::Value WebThreeStubServer::eth_accounts()
{ {
Json::Value ret(Json::arrayValue); Json::Value ret(Json::arrayValue);

1
libweb3jsonrpc/WebThreeStubServer.h

@ -64,6 +64,7 @@ class WebThreeStubServer: public AbstractWebThreeStubServer
public: public:
WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts); WebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts);
virtual std::string web3_sha3(std::string const& _param1);
virtual Json::Value eth_accounts(); virtual Json::Value eth_accounts();
virtual std::string eth_balanceAt(std::string const& _address); virtual std::string eth_balanceAt(std::string const& _address);
virtual Json::Value eth_blockByHash(std::string const& _hash); virtual Json::Value eth_blockByHash(std::string const& _hash);

16
libweb3jsonrpc/abstractwebthreestubserver.h

@ -12,6 +12,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
public: public:
AbstractWebThreeStubServer(jsonrpc::AbstractServerConnector &conn) : jsonrpc::AbstractServer<AbstractWebThreeStubServer>(conn) AbstractWebThreeStubServer(jsonrpc::AbstractServerConnector &conn) : jsonrpc::AbstractServer<AbstractWebThreeStubServer>(conn)
{ {
this->bindAndAddMethod(new jsonrpc::Procedure("web3_sha3", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::web3_sha3I);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_coinbase", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_coinbaseI); this->bindAndAddMethod(new jsonrpc::Procedure("eth_coinbase", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_coinbaseI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_setCoinbase", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_setCoinbaseI); this->bindAndAddMethod(new jsonrpc::Procedure("eth_setCoinbase", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING, NULL), &AbstractWebThreeStubServer::eth_setCoinbaseI);
this->bindAndAddMethod(new jsonrpc::Procedure("eth_listening", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_listeningI); this->bindAndAddMethod(new jsonrpc::Procedure("eth_listening", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, NULL), &AbstractWebThreeStubServer::eth_listeningI);
@ -61,9 +62,12 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
this->bindAndAddMethod(new jsonrpc::Procedure("shh_changed", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::shh_changedI); this->bindAndAddMethod(new jsonrpc::Procedure("shh_changed", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_ARRAY, "param1",jsonrpc::JSON_INTEGER, NULL), &AbstractWebThreeStubServer::shh_changedI);
} }
inline virtual void web3_sha3I(const Json::Value &request, Json::Value &response)
{
response = this->web3_sha3(request[0u].asString());
}
inline virtual void eth_coinbaseI(const Json::Value &request, Json::Value &response) inline virtual void eth_coinbaseI(const Json::Value &request, Json::Value &response)
{ {
(void)request;
response = this->eth_coinbase(); response = this->eth_coinbase();
} }
inline virtual void eth_setCoinbaseI(const Json::Value &request, Json::Value &response) inline virtual void eth_setCoinbaseI(const Json::Value &request, Json::Value &response)
@ -72,7 +76,6 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
} }
inline virtual void eth_listeningI(const Json::Value &request, Json::Value &response) inline virtual void eth_listeningI(const Json::Value &request, Json::Value &response)
{ {
(void)request;
response = this->eth_listening(); response = this->eth_listening();
} }
inline virtual void eth_setListeningI(const Json::Value &request, Json::Value &response) inline virtual void eth_setListeningI(const Json::Value &request, Json::Value &response)
@ -81,7 +84,6 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
} }
inline virtual void eth_miningI(const Json::Value &request, Json::Value &response) inline virtual void eth_miningI(const Json::Value &request, Json::Value &response)
{ {
(void)request;
response = this->eth_mining(); response = this->eth_mining();
} }
inline virtual void eth_setMiningI(const Json::Value &request, Json::Value &response) inline virtual void eth_setMiningI(const Json::Value &request, Json::Value &response)
@ -90,22 +92,18 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
} }
inline virtual void eth_gasPriceI(const Json::Value &request, Json::Value &response) inline virtual void eth_gasPriceI(const Json::Value &request, Json::Value &response)
{ {
(void)request;
response = this->eth_gasPrice(); response = this->eth_gasPrice();
} }
inline virtual void eth_accountsI(const Json::Value &request, Json::Value &response) inline virtual void eth_accountsI(const Json::Value &request, Json::Value &response)
{ {
(void)request;
response = this->eth_accounts(); response = this->eth_accounts();
} }
inline virtual void eth_peerCountI(const Json::Value &request, Json::Value &response) inline virtual void eth_peerCountI(const Json::Value &request, Json::Value &response)
{ {
(void)request;
response = this->eth_peerCount(); response = this->eth_peerCount();
} }
inline virtual void eth_defaultBlockI(const Json::Value &request, Json::Value &response) inline virtual void eth_defaultBlockI(const Json::Value &request, Json::Value &response)
{ {
(void)request;
response = this->eth_defaultBlock(); response = this->eth_defaultBlock();
} }
inline virtual void eth_setDefaultBlockI(const Json::Value &request, Json::Value &response) inline virtual void eth_setDefaultBlockI(const Json::Value &request, Json::Value &response)
@ -114,7 +112,6 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
} }
inline virtual void eth_numberI(const Json::Value &request, Json::Value &response) inline virtual void eth_numberI(const Json::Value &request, Json::Value &response)
{ {
(void)request;
response = this->eth_number(); response = this->eth_number();
} }
inline virtual void eth_balanceAtI(const Json::Value &request, Json::Value &response) inline virtual void eth_balanceAtI(const Json::Value &request, Json::Value &response)
@ -171,7 +168,6 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
} }
inline virtual void eth_compilersI(const Json::Value &request, Json::Value &response) inline virtual void eth_compilersI(const Json::Value &request, Json::Value &response)
{ {
(void)request;
response = this->eth_compilers(); response = this->eth_compilers();
} }
inline virtual void eth_lllI(const Json::Value &request, Json::Value &response) inline virtual void eth_lllI(const Json::Value &request, Json::Value &response)
@ -232,7 +228,6 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
} }
inline virtual void shh_newIdentityI(const Json::Value &request, Json::Value &response) inline virtual void shh_newIdentityI(const Json::Value &request, Json::Value &response)
{ {
(void)request;
response = this->shh_newIdentity(); response = this->shh_newIdentity();
} }
inline virtual void shh_haveIdentityI(const Json::Value &request, Json::Value &response) inline virtual void shh_haveIdentityI(const Json::Value &request, Json::Value &response)
@ -259,6 +254,7 @@ class AbstractWebThreeStubServer : public jsonrpc::AbstractServer<AbstractWebThr
{ {
response = this->shh_changed(request[0u].asInt()); response = this->shh_changed(request[0u].asInt());
} }
virtual std::string web3_sha3(const std::string& param1) = 0;
virtual std::string eth_coinbase() = 0; virtual std::string eth_coinbase() = 0;
virtual bool eth_setCoinbase(const std::string& param1) = 0; virtual bool eth_setCoinbase(const std::string& param1) = 0;
virtual bool eth_listening() = 0; virtual bool eth_listening() = 0;

2
libweb3jsonrpc/spec.json

@ -1,4 +1,6 @@
[ [
{ "name": "web3_sha3", "params": [""], "order": [], "returns" : "" },
{ "name": "eth_coinbase", "params": [], "order": [], "returns" : "" }, { "name": "eth_coinbase", "params": [], "order": [], "returns" : "" },
{ "name": "eth_setCoinbase", "params": [""], "order": [], "returns" : true }, { "name": "eth_setCoinbase", "params": [""], "order": [], "returns" : true },
{ "name": "eth_listening", "params": [], "order": [], "returns" : false }, { "name": "eth_listening", "params": [], "order": [], "returns" : false },

3
neth/main.cpp

@ -415,6 +415,7 @@ int main(int argc, char** argv)
if (!(mainwin = initscr())) if (!(mainwin = initscr()))
{ {
cerr << "Error initialising ncurses."; cerr << "Error initialising ncurses.";
delete [] str;
return -1; return -1;
} }
@ -1023,7 +1024,7 @@ void print_in_middle(WINDOW *win, int starty, int startx, int width, string str,
length = str.length(); length = str.length();
temp = (width - length) / 2; temp = (width - length) / 2;
x = startx + (int)temp; x = x + (int)temp;
wattron(win, color); wattron(win, color);
mvwprintw(win, y, x, "%s", str.c_str()); mvwprintw(win, y, x, "%s", str.c_str());
wattroff(win, color); wattroff(win, color);

53
test/SolidityABIJSON.cpp

@ -76,6 +76,7 @@ BOOST_AUTO_TEST_CASE(basic_test)
char const* interface = R"([ char const* interface = R"([
{ {
"name": "f", "name": "f",
"constant": false,
"inputs": [ "inputs": [
{ {
"name": "a", "name": "a",
@ -114,6 +115,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods)
char const* interface = R"([ char const* interface = R"([
{ {
"name": "f", "name": "f",
"constant": false,
"inputs": [ "inputs": [
{ {
"name": "a", "name": "a",
@ -129,6 +131,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods)
}, },
{ {
"name": "g", "name": "g",
"constant": false,
"inputs": [ "inputs": [
{ {
"name": "b", "name": "b",
@ -156,6 +159,7 @@ BOOST_AUTO_TEST_CASE(multiple_params)
char const* interface = R"([ char const* interface = R"([
{ {
"name": "f", "name": "f",
"constant": false,
"inputs": [ "inputs": [
{ {
"name": "a", "name": "a",
@ -189,6 +193,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order)
char const* interface = R"([ char const* interface = R"([
{ {
"name": "c", "name": "c",
"constant": false,
"inputs": [ "inputs": [
{ {
"name": "b", "name": "b",
@ -204,6 +209,7 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order)
}, },
{ {
"name": "f", "name": "f",
"constant": false,
"inputs": [ "inputs": [
{ {
"name": "a", "name": "a",
@ -222,6 +228,53 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order)
checkInterface(sourceCode, interface); checkInterface(sourceCode, interface);
} }
BOOST_AUTO_TEST_CASE(const_function)
{
char const* sourceCode = "contract test {\n"
" function foo(uint a, uint b) returns(uint d) { return a + b; }\n"
" function boo(uint32 a) const returns(uint b) { return a * 4; }\n"
"}\n";
char const* interface = R"([
{
"name": "boo",
"constant": true,
"inputs": [{
"name": "a",
"type": "uint32"
}],
"outputs": [
{
"name": "b",
"type": "uint256"
}
]
},
{
"name": "foo",
"constant": false,
"inputs": [
{
"name": "a",
"type": "uint256"
},
{
"name": "b",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}
])";
checkInterface(sourceCode, interface);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

35
test/SolidityEndToEndTest.cpp

@ -504,6 +504,41 @@ BOOST_AUTO_TEST_CASE(state_smoke_test)
BOOST_CHECK(callContractFunction(0, bytes(1, 0x00)) == toBigEndian(u256(0x3))); BOOST_CHECK(callContractFunction(0, bytes(1, 0x00)) == toBigEndian(u256(0x3)));
} }
BOOST_AUTO_TEST_CASE(compound_assign)
{
char const* sourceCode = "contract test {\n"
" uint value1;\n"
" uint value2;\n"
" function f(uint x, uint y) returns (uint w) {\n"
" uint value3 = y;"
" value1 += x;\n"
" value3 *= x;"
" value2 *= value3 + value1;\n"
" return value2 += 7;"
" }\n"
"}\n";
compileAndRun(sourceCode);
u256 value1;
u256 value2;
auto f = [&](u256 const& _x, u256 const& _y) -> u256
{
u256 value3 = _y;
value1 += _x;
value3 *= _x;
value2 *= value3 + value1;
return value2 += 7;
};
testSolidityAgainstCpp(0, f, u256(0), u256(6));
testSolidityAgainstCpp(0, f, u256(1), u256(3));
testSolidityAgainstCpp(0, f, u256(2), u256(25));
testSolidityAgainstCpp(0, f, u256(3), u256(69));
testSolidityAgainstCpp(0, f, u256(4), u256(84));
testSolidityAgainstCpp(0, f, u256(5), u256(2));
testSolidityAgainstCpp(0, f, u256(6), u256(51));
testSolidityAgainstCpp(0, f, u256(7), u256(48));
}
BOOST_AUTO_TEST_CASE(simple_mapping) BOOST_AUTO_TEST_CASE(simple_mapping)
{ {
char const* sourceCode = "contract test {\n" char const* sourceCode = "contract test {\n"

10
test/SolidityNameAndTypeResolution.cpp

@ -311,6 +311,16 @@ BOOST_AUTO_TEST_CASE(forward_function_reference)
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
} }
BOOST_AUTO_TEST_CASE(comparison_bitop_precedence)
{
char const* text = "contract First {\n"
" function fun() returns (bool ret) {\n"
" return 1 & 2 == 8 & 9 && 1 ^ 2 < 4 | 6;\n"
" }\n"
"}\n";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

29
test/SolidityNatspecJSON.cpp

@ -394,6 +394,35 @@ BOOST_AUTO_TEST_CASE(dev_multiline_return)
checkNatspec(sourceCode, natspec, false); checkNatspec(sourceCode, natspec, false);
} }
BOOST_AUTO_TEST_CASE(dev_multiline_comment)
{
char const* sourceCode = "contract test {\n"
" /**\n"
" * @dev Multiplies a number by 7 and adds second parameter\n"
" * @param a Documentation for the first parameter starts here.\n"
" * Since it's a really complicated parameter we need 2 lines\n"
" * @param second Documentation for the second parameter\n"
" * @return The result of the multiplication\n"
" * and cookies with nutella\n"
" */"
" function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n"
"}\n";
char const* natspec = "{"
"\"methods\":{"
" \"mul\":{ \n"
" \"details\": \"Multiplies a number by 7 and adds second parameter\",\n"
" \"params\": {\n"
" \"a\": \"Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines\",\n"
" \"second\": \"Documentation for the second parameter\"\n"
" },\n"
" \"return\": \"The result of the multiplication and cookies with nutella\"\n"
" }\n"
"}}";
checkNatspec(sourceCode, natspec, false);
}
BOOST_AUTO_TEST_CASE(dev_contract_no_doc) BOOST_AUTO_TEST_CASE(dev_contract_no_doc)
{ {
char const* sourceCode = "contract test {\n" char const* sourceCode = "contract test {\n"

14
test/SolidityParser.cpp

@ -128,7 +128,7 @@ BOOST_AUTO_TEST_CASE(function_natspec_documentation)
BOOST_REQUIRE_NO_THROW(contract = parseText(text)); BOOST_REQUIRE_NO_THROW(contract = parseText(text));
auto functions = contract->getDefinedFunctions(); auto functions = contract->getDefinedFunctions();
BOOST_REQUIRE_NO_THROW(function = functions.at(0)); BOOST_REQUIRE_NO_THROW(function = functions.at(0));
BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is a test function"); BOOST_CHECK_EQUAL(*function->getDocumentation(), "This is a test function");
} }
BOOST_AUTO_TEST_CASE(function_normal_comments) BOOST_AUTO_TEST_CASE(function_normal_comments)
@ -166,17 +166,17 @@ BOOST_AUTO_TEST_CASE(multiple_functions_natspec_documentation)
auto functions = contract->getDefinedFunctions(); auto functions = contract->getDefinedFunctions();
BOOST_REQUIRE_NO_THROW(function = functions.at(0)); BOOST_REQUIRE_NO_THROW(function = functions.at(0));
BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is test function 1"); BOOST_CHECK_EQUAL(*function->getDocumentation(), "This is test function 1");
BOOST_REQUIRE_NO_THROW(function = functions.at(1)); BOOST_REQUIRE_NO_THROW(function = functions.at(1));
BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is test function 2"); BOOST_CHECK_EQUAL(*function->getDocumentation(), "This is test function 2");
BOOST_REQUIRE_NO_THROW(function = functions.at(2)); BOOST_REQUIRE_NO_THROW(function = functions.at(2));
BOOST_CHECK_MESSAGE(function->getDocumentation() == nullptr, BOOST_CHECK_MESSAGE(function->getDocumentation() == nullptr,
"Should not have gotten natspec comment for functionName3()"); "Should not have gotten natspec comment for functionName3()");
BOOST_REQUIRE_NO_THROW(function = functions.at(3)); BOOST_REQUIRE_NO_THROW(function = functions.at(3));
BOOST_CHECK_EQUAL(*function->getDocumentation(), " This is test function 4"); BOOST_CHECK_EQUAL(*function->getDocumentation(), "This is test function 4");
} }
BOOST_AUTO_TEST_CASE(multiline_function_documentation) BOOST_AUTO_TEST_CASE(multiline_function_documentation)
@ -194,7 +194,7 @@ BOOST_AUTO_TEST_CASE(multiline_function_documentation)
BOOST_REQUIRE_NO_THROW(function = functions.at(0)); BOOST_REQUIRE_NO_THROW(function = functions.at(0));
BOOST_CHECK_EQUAL(*function->getDocumentation(), BOOST_CHECK_EQUAL(*function->getDocumentation(),
" This is a test function\n" "This is a test function\n"
" and it has 2 lines"); " and it has 2 lines");
} }
@ -220,11 +220,11 @@ BOOST_AUTO_TEST_CASE(natspec_comment_in_function_body)
auto functions = contract->getDefinedFunctions(); auto functions = contract->getDefinedFunctions();
BOOST_REQUIRE_NO_THROW(function = functions.at(0)); BOOST_REQUIRE_NO_THROW(function = functions.at(0));
BOOST_CHECK_EQUAL(*function->getDocumentation(), " fun1 description"); BOOST_CHECK_EQUAL(*function->getDocumentation(), "fun1 description");
BOOST_REQUIRE_NO_THROW(function = functions.at(1)); BOOST_REQUIRE_NO_THROW(function = functions.at(1));
BOOST_CHECK_EQUAL(*function->getDocumentation(), BOOST_CHECK_EQUAL(*function->getDocumentation(),
" This is a test function\n" "This is a test function\n"
" and it has 2 lines"); " and it has 2 lines");
} }

54
test/SolidityScanner.cpp

@ -157,7 +157,14 @@ BOOST_AUTO_TEST_CASE(documentation_comments_parsed_begin)
{ {
Scanner scanner(CharStream("/// Send $(value / 1000) chocolates to the user")); Scanner scanner(CharStream("/// Send $(value / 1000) chocolates to the user"));
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS); BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS);
BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), " Send $(value / 1000) chocolates to the user"); BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user");
}
BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed_begin)
{
Scanner scanner(CharStream("/** Send $(value / 1000) chocolates to the user*/"));
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS);
BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user");
} }
BOOST_AUTO_TEST_CASE(documentation_comments_parsed) BOOST_AUTO_TEST_CASE(documentation_comments_parsed)
@ -167,7 +174,43 @@ BOOST_AUTO_TEST_CASE(documentation_comments_parsed)
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), " Send $(value / 1000) chocolates to the user"); BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user");
}
BOOST_AUTO_TEST_CASE(multiline_documentation_comments_parsed)
{
Scanner scanner(CharStream("some other tokens /**\n"
"* Send $(value / 1000) chocolates to the user\n"
"*/"));
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user");
}
BOOST_AUTO_TEST_CASE(multiline_documentation_no_stars)
{
Scanner scanner(CharStream("some other tokens /**\n"
" Send $(value / 1000) chocolates to the user\n"
"*/"));
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user");
}
BOOST_AUTO_TEST_CASE(multiline_documentation_whitespace_hell)
{
Scanner scanner(CharStream("some other tokens /** \t \r \n"
"\t \r * Send $(value / 1000) chocolates to the user\n"
"*/"));
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS);
BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "Send $(value / 1000) chocolates to the user");
} }
BOOST_AUTO_TEST_CASE(comment_before_eos) BOOST_AUTO_TEST_CASE(comment_before_eos)
@ -184,6 +227,13 @@ BOOST_AUTO_TEST_CASE(documentation_comment_before_eos)
BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), ""); BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "");
} }
BOOST_AUTO_TEST_CASE(empty_multiline_documentation_comment_before_eos)
{
Scanner scanner(CharStream("/***/"));
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS);
BOOST_CHECK_EQUAL(scanner.getCurrentCommentLiteral(), "");
}
BOOST_AUTO_TEST_CASE(comments_mixed_in_sequence) BOOST_AUTO_TEST_CASE(comments_mixed_in_sequence)
{ {
Scanner scanner(CharStream("hello_world ///documentation comment \n" Scanner scanner(CharStream("hello_world ///documentation comment \n"

19
test/TestHelper.cpp

@ -26,6 +26,7 @@
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
#include <libethereum/Client.h> #include <libethereum/Client.h>
#include <liblll/Compiler.h> #include <liblll/Compiler.h>
#include <libevm/VMFactory.h>
using namespace std; using namespace std;
using namespace dev::eth; using namespace dev::eth;
@ -330,7 +331,7 @@ void checkStorage(map<u256, u256> _expectedStore, map<u256, u256> _resultStore,
} }
} }
BOOST_CHECK_EQUAL(_resultStore.size(), _expectedStore.size()); BOOST_CHECK_EQUAL(_resultStore.size(), _expectedStore.size());
for (auto&& resultStorePair : _resultStore) for (auto&& resultStorePair: _resultStore)
{ {
if (!_expectedStore.count(resultStorePair.first)) if (!_expectedStore.count(resultStorePair.first))
BOOST_ERROR(_expectedAddr << ": unexpected store key " << resultStorePair.first); BOOST_ERROR(_expectedAddr << ": unexpected store key " << resultStorePair.first);
@ -472,4 +473,20 @@ void executeTests(const string& _name, const string& _testPathAppendix, std::fun
} }
} }
void processCommandLineOptions()
{
auto argc = boost::unit_test::framework::master_test_suite().argc;
auto argv = boost::unit_test::framework::master_test_suite().argv;
for (auto i = 0; i < argc; ++i)
{
if (std::string(argv[i]) == "--jit")
{
eth::VMFactory::setKind(eth::VMKind::JIT);
break;
}
}
}
} } // namespaces } } // namespaces

1
test/TestHelper.h

@ -76,6 +76,7 @@ void checkLog(eth::LogEntries _resultLogs, eth::LogEntries _expectedLogs);
void executeTests(const std::string& _name, const std::string& _testPathAppendix, std::function<void(json_spirit::mValue&, bool)> doTests); void executeTests(const std::string& _name, const std::string& _testPathAppendix, std::function<void(json_spirit::mValue&, bool)> doTests);
std::string getTestPath(); std::string getTestPath();
void userDefinedTest(std::string testTypeFlag, std::function<void(json_spirit::mValue&, bool)> doTests); void userDefinedTest(std::string testTypeFlag, std::function<void(json_spirit::mValue&, bool)> doTests);
void processCommandLineOptions();
template<typename mapType> template<typename mapType>
void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs) void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs)

22
test/createRandomTest.cpp

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file createRandomTest.cpp /** @file createRandomTest.cpp
* @author Christoph Jentzsch <jentzsch.simulationsoftware@gmail.com> * @author Christoph Jentzsch <jentzsch.simulationsoftware@gmail.com>

4
test/jsonrpc.cpp

@ -19,7 +19,7 @@
* @date 2014 * @date 2014
*/ */
#if ETH_JSONRPC && 0 #if ETH_JSONRPC
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
@ -29,7 +29,7 @@
#include <libwebthree/WebThree.h> #include <libwebthree/WebThree.h>
#include <libweb3jsonrpc/WebThreeStubServer.h> #include <libweb3jsonrpc/WebThreeStubServer.h>
#include <libweb3jsonrpc/CorsHttpServer.h> #include <libweb3jsonrpc/CorsHttpServer.h>
#include <json/json.h> //#include <json/json.h>
#include <jsonrpccpp/server/connectors/httpserver.h> #include <jsonrpccpp/server/connectors/httpserver.h>
#include <jsonrpccpp/client/connectors/httpclient.h> #include <jsonrpccpp/client/connectors/httpclient.h>
#include <set> #include <set>

163
test/stRefundTestFiller.json

@ -141,5 +141,168 @@
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : "" "data" : ""
} }
},
"refund50_1" : {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ [[ 1 ]] 0 [[ 2 ]] 0 [[ 3 ]] 0 [[ 4 ]] 0 [[ 5 ]] 0 }",
"storage" : {
"0x01" : "0x01",
"0x02" : "0x01",
"0x03" : "0x01",
"0x04" : "0x01",
"0x05" : "0x01"
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000",
"nonce" : 0,
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "10000",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
},
"refund50_2" : {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ [[ 10 ]] 1 [[ 11 ]] 1 [[ 1 ]] 0 [[ 2 ]] 0 [[ 3 ]] 0 [[ 4 ]] 0 [[ 5 ]] 0 }",
"storage" : {
"0x01" : "0x01",
"0x02" : "0x01",
"0x03" : "0x01",
"0x04" : "0x01",
"0x05" : "0x01"
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000",
"nonce" : 0,
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "10000",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
},
"refund500" : {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ @@1 @@2 [[ 10 ]] (EXP 2 0xff) [[ 11 ]] (BALANCE (ADDRESS)) [[ 1 ]] 0 [[ 2 ]] 0 [[ 3 ]] 0 [[ 4 ]] 0 [[ 5 ]] 0 [[ 6 ]] 0 }",
"storage" : {
"0x01" : "0x01",
"0x02" : "0x01",
"0x03" : "0x01",
"0x04" : "0x01",
"0x05" : "0x01",
"0x06" : "0x01"
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000",
"nonce" : 0,
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "10000",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
},
"refund600" : {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : 1,
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000000000000000",
"nonce" : 0,
"code" : "{ @@1 @@2 [[ 10 ]] (EXP 2 0xffff) [[ 11 ]] (BALANCE (ADDRESS)) [[ 1 ]] 0 [[ 2 ]] 0 [[ 3 ]] 0 [[ 4 ]] 0 [[ 5 ]] 0 [[ 6 ]] 0 }",
"storage" : {
"0x01" : "0x01",
"0x02" : "0x01",
"0x03" : "0x01",
"0x04" : "0x01",
"0x05" : "0x01",
"0x06" : "0x01"
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000",
"nonce" : 0,
"code" : "",
"storage": {}
}
},
"transaction" : {
"nonce" : "0",
"gasPrice" : "1",
"gasLimit" : "10000",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data" : ""
}
} }
} }

2
test/state.cpp

@ -43,6 +43,8 @@ namespace dev { namespace test {
void doStateTests(json_spirit::mValue& v, bool _fillin) void doStateTests(json_spirit::mValue& v, bool _fillin)
{ {
processCommandLineOptions();
for (auto& i: v.get_obj()) for (auto& i: v.get_obj())
{ {
cerr << i.first << endl; cerr << i.first << endl;

24
test/vm.cpp

@ -20,6 +20,7 @@
* vm test functions. * vm test functions.
*/ */
#include <chrono>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <libethereum/Executive.h> #include <libethereum/Executive.h>
#include <libevm/VMFactory.h> #include <libevm/VMFactory.h>
@ -308,6 +309,8 @@ namespace dev { namespace test {
void doVMTests(json_spirit::mValue& v, bool _fillin) void doVMTests(json_spirit::mValue& v, bool _fillin)
{ {
processCommandLineOptions();
for (auto& i: v.get_obj()) for (auto& i: v.get_obj())
{ {
cnote << i.first; cnote << i.first;
@ -317,7 +320,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
BOOST_REQUIRE(o.count("pre") > 0); BOOST_REQUIRE(o.count("pre") > 0);
BOOST_REQUIRE(o.count("exec") > 0); BOOST_REQUIRE(o.count("exec") > 0);
dev::test::FakeExtVM fev; FakeExtVM fev;
fev.importEnv(o["env"].get_obj()); fev.importEnv(o["env"].get_obj());
fev.importState(o["pre"].get_obj()); fev.importState(o["pre"].get_obj());
@ -332,12 +335,12 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
} }
bytes output; bytes output;
auto vm = eth::VMFactory::create(fev.gas);
u256 gas; u256 gas;
bool vmExceptionOccured = false; bool vmExceptionOccured = false;
auto startTime = std::chrono::high_resolution_clock::now();
try try
{ {
auto vm = eth::VMFactory::create(fev.gas);
output = vm->go(fev, fev.simpleTrace()).toBytes(); output = vm->go(fev, fev.simpleTrace()).toBytes();
gas = vm->gas(); gas = vm->gas();
} }
@ -357,6 +360,21 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
BOOST_ERROR("Failed VM Test with Exception: " << _e.what()); BOOST_ERROR("Failed VM Test with Exception: " << _e.what());
} }
auto endTime = std::chrono::high_resolution_clock::now();
auto argc = boost::unit_test::framework::master_test_suite().argc;
auto argv = boost::unit_test::framework::master_test_suite().argv;
for (auto i = 0; i < argc; ++i)
{
if (std::string(argv[i]) == "--show-times")
{
auto testDuration = endTime - startTime;
cnote << "Execution time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(testDuration).count()
<< " ms";
break;
}
}
// delete null entries in storage for the sake of comparison // delete null entries in storage for the sake of comparison
for (auto &a: fev.addresses) for (auto &a: fev.addresses)

Loading…
Cancel
Save