Browse Source

debug ui redesign

cl-refactor
yann300 10 years ago
parent
commit
5c89ffaeb6
  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. 12
      libdevcore/CommonJS.h
  35. 45
      libdevcore/Diff.h
  36. 3
      libethcore/BlockInfo.cpp
  37. 4
      libethcore/CommonEth.cpp
  38. 8
      libethcore/CommonEth.h
  39. 18
      libethereum/AccountDiff.cpp
  40. 55
      libethereum/AccountDiff.h
  41. 2
      libethereum/State.cpp
  42. 3
      libevm/CMakeLists.txt
  43. 10
      libevm/VMFactory.cpp
  44. 103
      libjsqrc/ethereum.js
  45. 2
      libsolidity/ExpressionCompiler.cpp
  46. 1
      libsolidity/InterfaceHandler.cpp
  47. 22
      libsolidity/Token.h
  48. 5
      libweb3jsonrpc/WebThreeStubServer.cpp
  49. 1
      libweb3jsonrpc/WebThreeStubServer.h
  50. 16
      libweb3jsonrpc/abstractwebthreestubserver.h
  51. 2
      libweb3jsonrpc/spec.json
  52. 55
      mix/AppContext.cpp
  53. 14
      mix/AppContext.h
  54. 91
      mix/AssemblyDebuggerControl.cpp
  55. 10
      mix/AssemblyDebuggerControl.h
  56. 5
      mix/AssemblyDebuggerModel.cpp
  57. 24
      mix/AssemblyDebuggerModel.h
  58. 7
      mix/CodeEditorExtensionManager.cpp
  59. 32
      mix/CodeModel.cpp
  60. 23
      mix/CodeModel.h
  61. 2
      mix/ConstantCompilationControl.cpp
  62. 1
      mix/DebuggingStateWrapper.h
  63. 49
      mix/KeyEventManager.cpp
  64. 2
      mix/MixApplication.cpp
  65. 9
      mix/QContractDefinition.h
  66. 10
      mix/QFunctionDefinition.h
  67. 3
      mix/QVariableDeclaration.h
  68. 24
      mix/StateListView.cpp
  69. 15
      mix/StateListView.h
  70. 202
      mix/TransactionListModel.cpp
  71. 168
      mix/TransactionListModel.h
  72. 3
      mix/qml.qrc
  73. 35
      mix/qml/CompilationStatus.qml
  74. 119
      mix/qml/DataDump.qml
  75. 38
      mix/qml/DebugBasicInfo.qml
  76. 119
      mix/qml/ImageButton.qml
  77. 182
      mix/qml/StateDialog.qml
  78. 125
      mix/qml/StateList.qml
  79. 95
      mix/qml/StepActionImage.qml
  80. 98
      mix/qml/Storage.qml
  81. 75
      mix/qml/TransactionDialog.qml
  82. 89
      mix/qml/TransactionList.qml
  83. BIN
      mix/qml/img/jumpintoback.png
  84. BIN
      mix/qml/img/jumpintobackdisabled.png
  85. BIN
      mix/qml/img/jumpintoforward.png
  86. BIN
      mix/qml/img/jumpintoforwarddisabled.png
  87. BIN
      mix/qml/img/jumpoutback.png
  88. BIN
      mix/qml/img/jumpoutbackdisabled.png
  89. BIN
      mix/qml/img/jumpoutforward.png
  90. BIN
      mix/qml/img/jumpoutforwarddisabled.png
  91. BIN
      mix/qml/img/jumpoverback.png
  92. BIN
      mix/qml/img/jumpoverbackdisabled.png
  93. BIN
      mix/qml/img/jumpoverforward.png
  94. 181
      mix/qml/js/ErrorLocationFormater.js
  95. 68
      mix/qml/js/main.js
  96. 21
      mix/qml/main.qml
  97. 3
      neth/main.cpp
  98. 53
      test/SolidityABIJSON.cpp
  99. 35
      test/SolidityEndToEndTest.cpp
  100. 10
      test/SolidityNameAndTypeResolution.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.13"; 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);

12
libdevcore/CommonJS.h

@ -46,19 +46,19 @@ inline std::string toJS(dev::bytes const& _n)
return "0x" + dev::toHex(_n); return "0x" + dev::toHex(_n);
} }
/// Convert string to byte array. /// Convert string to byte array. Input parameters can be hex or dec. Returns empty array if invalid input e.g neither dec or hex.
bytes jsToBytes(std::string const& _s); bytes jsToBytes(std::string const& _s);
/// Add '0' on the head of _b until _l. /// Add '0' on the head of _b until _l.
bytes padded(bytes _b, unsigned _l); bytes padded(bytes _b, unsigned _l);
/// Removing all trailing '0'. /// Removing all trailing '0'. Returns empty array if input contains only '0' char.
bytes unpadded(bytes _s); bytes unpadded(bytes _s);
/// Remove all '0' on the head of _s. /// Remove all '0' on the head of _s. Returns 0 if _s contains only '0'.
std::string unpadLeft(std::string _s); std::string unpadLeft(std::string _s);
/// Convert u256 to readable string. /// Convert u256 into user-readable string. Returns int/hex value of 64 bits int, hex of 160 bits FixedHash. As a fallback try to handle input as h256.
std::string prettyU256(u256 _n); std::string prettyU256(u256 _n);
/// Convert h256 to readable string. /// Convert h256 into user-readable string (by directly using std::string constructor).
std::string fromRaw(h256 _n, unsigned* _inc = nullptr); std::string fromRaw(h256 _n, unsigned* _inc = nullptr);
/// Convert string to Address (h160). /// Convert string to Address (h160), returns empty address if (_a.size != 40).
Address fromString(std::string const& _a); Address fromString(std::string const& _a);
template <unsigned N> FixedHash<N> jsToFixed(std::string const& _s) template <unsigned N> FixedHash<N> jsToFixed(std::string const& _s)

45
mix/KeyEventManager.h → libdevcore/Diff.h

@ -14,40 +14,39 @@
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 KeyEventManager.h /** @file Diff.h
* @author Yann yann@ethdev.com * @author Gav Wood <i@gavwood.com>
* @date 2014 * @date 2014
* Used as an event handler for all classes which need keyboard interactions
*/ */
#pragma once #pragma once
#include <QObject>
namespace dev namespace dev
{ {
namespace mix
{
class KeyEventManager: public QObject enum class ExistDiff
{ {
Q_OBJECT Same,
New,
Dead
};
template <class T>
class Diff
{
public: public:
KeyEventManager() {} Diff() {}
/// Allows _receiver to handle key pressed event. Diff(T _from, T _to): m_from(_from), m_to(_to) {}
void registerEvent(const QObject* _receiver, const char* _slot);
/// Unregister _receiver. T const& from() const { return m_from; }
void unRegisterEvent(QObject* _receiver); T const& to() const { return m_to; }
signals: explicit operator bool() const { return m_from != m_to; }
/// Emited when a key is pressed.
void onKeyPressed(int _event); private:
T m_from;
public slots: T m_to;
/// Called when a key is pressed.
void keyPressed(QVariant _event);
}; };
} }
}

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

4
libethcore/CommonEth.cpp

@ -32,7 +32,7 @@ namespace dev
namespace eth namespace eth
{ {
const unsigned c_protocolVersion = 50; const unsigned c_protocolVersion = 49;
const unsigned c_databaseVersion = 5; const unsigned c_databaseVersion = 5;
static const vector<pair<u256, string>> g_units = static const vector<pair<u256, string>> g_units =
@ -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
} }
} }

103
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());

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();

1
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);

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 },

55
mix/AppContext.cpp

@ -25,10 +25,13 @@
#include <QDebug> #include <QDebug>
#include <QMessageBox> #include <QMessageBox>
#include <QQmlComponent> #include <QQmlComponent>
#include <QQmlContext>
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QStandardPaths>
#include <QFile>
#include <QDir>
#include <libdevcrypto/FileSystem.h> #include <libdevcrypto/FileSystem.h>
#include <libwebthree/WebThree.h> #include <libwebthree/WebThree.h>
#include "KeyEventManager.h"
#include "AppContext.h" #include "AppContext.h"
#include "CodeModel.h" #include "CodeModel.h"
@ -37,35 +40,39 @@ using namespace dev::eth;
using namespace dev::solidity; using namespace dev::solidity;
using namespace dev::mix; using namespace dev::mix;
const QString c_projectFileName = "project.mix";
AppContext::AppContext(QQmlApplicationEngine* _engine) AppContext::AppContext(QQmlApplicationEngine* _engine)
{ {
m_applicationEngine = _engine; m_applicationEngine = _engine;
m_keyEventManager = std::unique_ptr<KeyEventManager>(new KeyEventManager()); //m_webThree = std::unique_ptr<dev::WebThreeDirect>(new WebThreeDirect(std::string("Mix/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/Mix", false, {"eth", "shh"}));
m_webThree = std::unique_ptr<dev::WebThreeDirect>(new WebThreeDirect(std::string("Mix/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/Mix", false, {"eth", "shh"}));
m_codeModel = std::unique_ptr<CodeModel>(new CodeModel(this)); m_codeModel = std::unique_ptr<CodeModel>(new CodeModel(this));
m_applicationEngine->rootContext()->setContextProperty("codeModel", m_codeModel.get());
m_applicationEngine->rootContext()->setContextProperty("appContext", this);
} }
AppContext::~AppContext() AppContext::~AppContext()
{ {
} }
QQmlApplicationEngine* AppContext::appEngine() void AppContext::loadProject()
{
return m_applicationEngine;
}
void AppContext::initKeyEventManager(QObject* _res)
{ {
QObject* mainContent = _res->findChild<QObject*>("mainContent", Qt::FindChildrenRecursively); QString path = QStandardPaths::locate(QStandardPaths::DataLocation, c_projectFileName);
if (mainContent) if (!path.isEmpty())
QObject::connect(mainContent, SIGNAL(keyPressed(QVariant)), m_keyEventManager.get(), SLOT(keyPressed(QVariant))); {
else QFile file(path);
qDebug() << "Unable to find QObject of mainContent.qml. KeyEvent will not be handled!"; if(file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QTextStream stream(&file);
QString json = stream.readAll();
emit projectLoaded(json);
}
}
} }
KeyEventManager* AppContext::getKeyEventManager() QQmlApplicationEngine* AppContext::appEngine()
{ {
return m_keyEventManager.get(); return m_applicationEngine;
} }
void AppContext::displayMessageDialog(QString _title, QString _message) void AppContext::displayMessageDialog(QString _title, QString _message)
@ -81,8 +88,18 @@ void AppContext::displayMessageDialog(QString _title, QString _message)
QMetaObject::invokeMethod(dialogWin, "open"); QMetaObject::invokeMethod(dialogWin, "open");
} }
void AppContext::resourceLoaded(QObject *_obj, QUrl _url) void AppContext::saveProject(QString const& _json)
{ {
Q_UNUSED(_url); QDir dirPath(QStandardPaths::writableLocation(QStandardPaths::DataLocation));
initKeyEventManager(_obj); QString path = QDir(dirPath).filePath(c_projectFileName);
if (!path.isEmpty())
{
dirPath.mkpath(dirPath.path());
QFile file(path);
if(file.open(QIODevice::WriteOnly | QIODevice::Text))
{
QTextStream stream(&file);
stream << _json;
}
}
} }

14
mix/AppContext.h

@ -47,7 +47,6 @@ namespace mix
{ {
class CodeModel; class CodeModel;
class KeyEventManager;
/** /**
* @brief Provides access to application scope variable. * @brief Provides access to application scope variable.
*/ */
@ -60,26 +59,25 @@ public:
AppContext(QQmlApplicationEngine* _engine); AppContext(QQmlApplicationEngine* _engine);
~AppContext(); ~AppContext();
QQmlApplicationEngine* appEngine(); QQmlApplicationEngine* appEngine();
/// Initialize KeyEventManager (used to handle key pressed event).
void initKeyEventManager(QObject* _obj);
/// Get the current KeyEventManager instance.
KeyEventManager* getKeyEventManager();
/// Get code model /// Get code model
CodeModel* codeModel() { return m_codeModel.get(); } CodeModel* codeModel() { return m_codeModel.get(); }
/// Display an alert message. /// Display an alert message.
void displayMessageDialog(QString _title, QString _message); void displayMessageDialog(QString _title, QString _message);
/// Load project settings
void loadProject();
signals:
void projectLoaded(QString const& _json);
private: private:
QQmlApplicationEngine* m_applicationEngine; //owned by app QQmlApplicationEngine* m_applicationEngine; //owned by app
std::unique_ptr<dev::WebThreeDirect> m_webThree; std::unique_ptr<dev::WebThreeDirect> m_webThree;
std::unique_ptr<KeyEventManager> m_keyEventManager;
std::unique_ptr<CodeModel> m_codeModel; std::unique_ptr<CodeModel> m_codeModel;
public slots: public slots:
/// Delete the current instance when application quit. /// Delete the current instance when application quit.
void quitApplication() {} void quitApplication() {}
/// Initialize components after the loading of the main QML view.
void resourceLoaded(QObject* _obj, QUrl _url); void saveProject(QString const& _json);
}; };
} }

91
mix/AssemblyDebuggerControl.cpp

@ -17,6 +17,11 @@
* display opcode debugging. * display opcode debugging.
*/ */
//These 2 includes should be at the top to avoid conflicts with macros defined in windows.h
//@todo fix this is solidity headers
#include <libsolidity/Token.h>
#include <libsolidity/Types.h>
#include <utility>
#include <QtConcurrent/QtConcurrent> #include <QtConcurrent/QtConcurrent>
#include <QDebug> #include <QDebug>
#include <QQmlContext> #include <QQmlContext>
@ -26,20 +31,30 @@
#include <libethereum/Transaction.h> #include <libethereum/Transaction.h>
#include "AssemblyDebuggerModel.h" #include "AssemblyDebuggerModel.h"
#include "AssemblyDebuggerControl.h" #include "AssemblyDebuggerControl.h"
#include "KeyEventManager.h"
#include "AppContext.h" #include "AppContext.h"
#include "DebuggingStateWrapper.h" #include "DebuggingStateWrapper.h"
#include "TransactionListModel.h"
#include "QContractDefinition.h" #include "QContractDefinition.h"
#include "QVariableDeclaration.h" #include "QVariableDeclaration.h"
#include "ContractCallDataEncoder.h" #include "ContractCallDataEncoder.h"
#include "KeyEventManager.h"
#include "CodeModel.h" #include "CodeModel.h"
#include "AssemblyDebuggerModel.h"
using namespace dev::eth; using namespace dev::eth;
using namespace dev::mix; using namespace dev::mix;
/// @todo Move this to QML
dev::u256 fromQString(QString const& _s)
{
return dev::jsToU256(_s.toStdString());
}
/// @todo Move this to QML
QString toQString(dev::u256 _value)
{
std::ostringstream s;
s << _value;
return QString::fromStdString(s.str());
}
AssemblyDebuggerControl::AssemblyDebuggerControl(AppContext* _context): Extension(_context, ExtensionDisplayBehavior::ModalDialog) AssemblyDebuggerControl::AssemblyDebuggerControl(AppContext* _context): Extension(_context, ExtensionDisplayBehavior::ModalDialog)
{ {
qRegisterMetaType<QVariableDefinition*>("QVariableDefinition*"); qRegisterMetaType<QVariableDefinition*>("QVariableDefinition*");
@ -53,6 +68,8 @@ AssemblyDebuggerControl::AssemblyDebuggerControl(AppContext* _context): Extensio
connect(this, SIGNAL(dataAvailable(bool, DebuggingStatusResult, QList<QVariableDefinition*>, QList<QObject*>, AssemblyDebuggerData)), connect(this, SIGNAL(dataAvailable(bool, DebuggingStatusResult, QList<QVariableDefinition*>, QList<QObject*>, AssemblyDebuggerData)),
this, SLOT(updateGUI(bool, DebuggingStatusResult, QList<QVariableDefinition*>, QList<QObject*>, AssemblyDebuggerData)), Qt::QueuedConnection); this, SLOT(updateGUI(bool, DebuggingStatusResult, QList<QVariableDefinition*>, QList<QObject*>, AssemblyDebuggerData)), Qt::QueuedConnection);
_context->appEngine()->rootContext()->setContextProperty("debugModel", this);
m_modelDebugger = std::unique_ptr<AssemblyDebuggerModel>(new AssemblyDebuggerModel); m_modelDebugger = std::unique_ptr<AssemblyDebuggerModel>(new AssemblyDebuggerModel);
} }
@ -68,29 +85,48 @@ QString AssemblyDebuggerControl::title() const
void AssemblyDebuggerControl::start() const void AssemblyDebuggerControl::start() const
{ {
//start to listen on F5
m_ctx->getKeyEventManager()->registerEvent(this, SLOT(keyPressed(int)));
} }
void AssemblyDebuggerControl::keyPressed(int _key) void AssemblyDebuggerControl::debugDeployment()
{ {
if (_key == Qt::Key_F5) deployContract();
{ }
QtConcurrent::run([this]()
{ void AssemblyDebuggerControl::debugState(QVariantMap _state)
deployContract(); {
}); u256 balance = fromQString(_state.value("balance").toString());
} QVariantList transactions = _state.value("transactions").toList();
else if (_key == Qt::Key_F6)
resetState();
deployContract();
for (auto const& t : transactions)
{ {
m_modelDebugger->resetState(); QVariantMap transaction = t.toMap();
m_ctx->displayMessageDialog(QApplication::tr("State status"), QApplication::tr("State reseted ... need to redeploy contract"));
QString functionId = transaction.value("functionId").toString();
u256 value = fromQString(transaction.value("value").toString());
u256 gas = fromQString(transaction.value("gas").toString());
u256 gasPrice = fromQString(transaction.value("gasPrice").toString());
QVariantMap params = transaction.value("parameters").toMap();
TransactionSettings transactionSettings(functionId, value, gas, gasPrice);
for (auto p = params.cbegin(); p != params.cend(); ++p)
transactionSettings.parameterValues.insert(std::make_pair(p.key(), fromQString(p.value().toString())));
runTransaction(transactionSettings);
} }
} }
void AssemblyDebuggerControl::callContract(TransactionSettings _tr, Address _contract) void AssemblyDebuggerControl::resetState()
{ {
auto compilerRes = m_ctx->codeModel()->lastCompilationResult(); m_modelDebugger->resetState();
m_ctx->displayMessageDialog(QApplication::tr("State status"), QApplication::tr("State reseted ... need to redeploy contract"));
}
void AssemblyDebuggerControl::callContract(TransactionSettings _tr, dev::Address _contract)
{
auto compilerRes = m_ctx->codeModel()->code();
if (!compilerRes->successfull()) if (!compilerRes->successfull())
m_ctx->displayMessageDialog("debugger","compilation failed"); m_ctx->displayMessageDialog("debugger","compilation failed");
else else
@ -98,11 +134,11 @@ void AssemblyDebuggerControl::callContract(TransactionSettings _tr, Address _con
ContractCallDataEncoder c; ContractCallDataEncoder c;
QContractDefinition const* contractDef = compilerRes->contract(); QContractDefinition const* contractDef = compilerRes->contract();
QFunctionDefinition* f = nullptr; QFunctionDefinition* f = nullptr;
for (int k = 0; k < contractDef->functions().size(); k++) for (int k = 0; k < contractDef->functionsList().size(); k++)
{ {
if (contractDef->functions().at(k)->name() == _tr.functionId) if (contractDef->functionsList().at(k)->name() == _tr.functionId)
{ {
f = (QFunctionDefinition*)contractDef->functions().at(k); f = contractDef->functionsList().at(k);
break; break;
} }
} }
@ -111,9 +147,9 @@ void AssemblyDebuggerControl::callContract(TransactionSettings _tr, Address _con
else else
{ {
c.encode(f->index()); c.encode(f->index());
for (int k = 0; k < f->parameters().size(); k++) for (int k = 0; k < f->parametersList().size(); k++)
{ {
QVariableDeclaration* var = (QVariableDeclaration*)f->parameters().at(k); QVariableDeclaration* var = (QVariableDeclaration*)f->parametersList().at(k);
c.encode(var, _tr.parameterValues[var->name()]); c.encode(var, _tr.parameterValues[var->name()]);
} }
DebuggingContent debuggingContent = m_modelDebugger->callContract(_contract, c.encodedData(), _tr); DebuggingContent debuggingContent = m_modelDebugger->callContract(_contract, c.encodedData(), _tr);
@ -125,7 +161,7 @@ void AssemblyDebuggerControl::callContract(TransactionSettings _tr, Address _con
void AssemblyDebuggerControl::deployContract() void AssemblyDebuggerControl::deployContract()
{ {
auto compilerRes = m_ctx->codeModel()->lastCompilationResult(); auto compilerRes = m_ctx->codeModel()->code();
if (!compilerRes->successfull()) if (!compilerRes->successfull())
emit dataAvailable(false, DebuggingStatusResult::Compilationfailed); emit dataAvailable(false, DebuggingStatusResult::Compilationfailed);
else else
@ -166,8 +202,5 @@ void AssemblyDebuggerControl::updateGUI(bool _success, DebuggingStatusResult con
void AssemblyDebuggerControl::runTransaction(TransactionSettings const& _tr) void AssemblyDebuggerControl::runTransaction(TransactionSettings const& _tr)
{ {
QtConcurrent::run([this, _tr]() callContract(_tr, m_previousDebugResult.contractAddress);
{
callContract(_tr, m_previousDebugResult.contractAddress);
});
} }

10
mix/AssemblyDebuggerControl.h

@ -19,9 +19,12 @@
#pragma once #pragma once
//These 2 includes should be at the top to avoid conflicts with macros defined in windows.h
//@todo fix this is solidity headers
#include <libsolidity/Token.h>
#include <libsolidity/Types.h>
#include <QKeySequence> #include <QKeySequence>
#include "Extension.h" #include "Extension.h"
#include "TransactionListModel.h"
#include "AssemblyDebuggerModel.h" #include "AssemblyDebuggerModel.h"
using AssemblyDebuggerData = std::tuple<QList<QObject*>, dev::mix::QQMLMap*>; using AssemblyDebuggerData = std::tuple<QList<QObject*>, dev::mix::QQMLMap*>;
@ -65,8 +68,9 @@ private:
DebuggingContent m_previousDebugResult; //TODO: to be replaced in a more consistent struct. Used for now to keep the contract address in case of future transaction call. DebuggingContent m_previousDebugResult; //TODO: to be replaced in a more consistent struct. Used for now to keep the contract address in case of future transaction call.
public slots: public slots:
/// Handle key pressed. F5 deploy contract - F6 reset state. void debugDeployment();
void keyPressed(int); void debugState(QVariantMap _state);
void resetState();
/// Update UI with machine states result. Display a modal dialog. /// Update UI with machine states result. Display a modal dialog.
void updateGUI(bool _success, DebuggingStatusResult const& _reason, QList<QVariableDefinition*> const& _returnParams = QList<QVariableDefinition*>(), QList<QObject*> const& _wStates = QList<QObject*>(), AssemblyDebuggerData const& _code = AssemblyDebuggerData()); void updateGUI(bool _success, DebuggingStatusResult const& _reason, QList<QVariableDefinition*> const& _returnParams = QList<QVariableDefinition*>(), QList<QObject*> const& _wStates = QList<QObject*>(), AssemblyDebuggerData const& _code = AssemblyDebuggerData());
/// Run the given transaction. /// Run the given transaction.

5
mix/AssemblyDebuggerModel.cpp

@ -17,6 +17,10 @@
* used as a model to debug contract assembly code. * used as a model to debug contract assembly code.
*/ */
//These 2 includes should be at the top to avoid conflicts with macros defined in windows.h
//@todo fix this is solidity headers
#include <libsolidity/Token.h>
#include <libsolidity/Types.h>
#include <QApplication> #include <QApplication>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libevm/VM.h> #include <libevm/VM.h>
@ -25,7 +29,6 @@
#include <libethereum/ExtVM.h> #include <libethereum/ExtVM.h>
#include "AssemblyDebuggerModel.h" #include "AssemblyDebuggerModel.h"
#include "AppContext.h" #include "AppContext.h"
#include "TransactionListModel.h"
#include "DebuggingStateWrapper.h" #include "DebuggingStateWrapper.h"
using namespace std; using namespace std;

24
mix/AssemblyDebuggerModel.h

@ -19,6 +19,10 @@
#pragma once #pragma once
//These 2 includes should be at the top to avoid conflicts with macros defined in windows.h
//@todo fix this is solidity headers
#include <libsolidity/Token.h>
#include <libsolidity/Types.h>
#include <QObject> #include <QObject>
#include <QList> #include <QList>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
@ -26,13 +30,31 @@
#include <libethereum/State.h> #include <libethereum/State.h>
#include <libethereum/Executive.h> #include <libethereum/Executive.h>
#include "DebuggingStateWrapper.h" #include "DebuggingStateWrapper.h"
#include "TransactionListModel.h"
namespace dev namespace dev
{ {
namespace mix namespace mix
{ {
/// Backend transaction config class
struct TransactionSettings
{
TransactionSettings(QString const& _functionId, u256 _value, u256 _gas, u256 _gasPrice):
functionId(_functionId), value(_value), gas(_gas), gasPrice(_gasPrice) {}
/// Contract function name
QString functionId;
/// Transaction value
u256 value;
/// Gas
u256 gas;
/// Gas price
u256 gasPrice;
/// Mapping from contract function parameter name to value
std::map<QString, u256> parameterValues;
};
/** /**
* @brief Long-life object for managing all executions. * @brief Long-life object for managing all executions.
*/ */

7
mix/CodeEditorExtensionManager.cpp

@ -28,7 +28,7 @@
#include <libevm/VM.h> #include <libevm/VM.h>
#include "ConstantCompilationControl.h" #include "ConstantCompilationControl.h"
#include "AssemblyDebuggerControl.h" #include "AssemblyDebuggerControl.h"
#include "TransactionListView.h" #include "StateListView.h"
#include "AppContext.h" #include "AppContext.h"
#include "MixApplication.h" #include "MixApplication.h"
#include "CodeModel.h" #include "CodeModel.h"
@ -66,12 +66,11 @@ void CodeEditorExtensionManager::initExtensions()
{ {
initExtension(std::make_shared<ConstantCompilationControl>(m_appContext)); initExtension(std::make_shared<ConstantCompilationControl>(m_appContext));
std::shared_ptr<AssemblyDebuggerControl> debug = std::make_shared<AssemblyDebuggerControl>(m_appContext); std::shared_ptr<AssemblyDebuggerControl> debug = std::make_shared<AssemblyDebuggerControl>(m_appContext);
std::shared_ptr<TransactionListView> tr = std::make_shared<TransactionListView>(m_appContext); std::shared_ptr<StateListView> stateList = std::make_shared<StateListView>(m_appContext);
QObject::connect(tr->model(), &TransactionListModel::transactionStarted, debug.get(), &AssemblyDebuggerControl::runTransaction);
QObject::connect(m_doc, &QTextDocument::contentsChanged, [=]() { m_appContext->codeModel()->registerCodeChange(m_doc->toPlainText()); }); QObject::connect(m_doc, &QTextDocument::contentsChanged, [=]() { m_appContext->codeModel()->registerCodeChange(m_doc->toPlainText()); });
initExtension(debug); initExtension(debug);
initExtension(tr); initExtension(stateList);
} }
void CodeEditorExtensionManager::initExtension(std::shared_ptr<Extension> _ext) void CodeEditorExtensionManager::initExtension(std::shared_ptr<Extension> _ext)

32
mix/CodeModel.cpp

@ -23,10 +23,13 @@
#include <sstream> #include <sstream>
#include <QDebug> #include <QDebug>
#include <QApplication> #include <QApplication>
#include <QtQml>
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
#include <libsolidity/SourceReferenceFormatter.h> #include <libsolidity/SourceReferenceFormatter.h>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include "QContractDefinition.h" #include "QContractDefinition.h"
#include "QFunctionDefinition.h"
#include "QVariableDeclaration.h"
#include "CodeModel.h" #include "CodeModel.h"
namespace dev namespace dev
@ -39,6 +42,11 @@ void BackgroundWorker::queueCodeChange(int _jobId, QString const& _content)
m_model->runCompilationJob(_jobId, _content); m_model->runCompilationJob(_jobId, _content);
} }
CompilationResult::CompilationResult(QObject *_parent):
QObject(_parent), m_successfull(false),
m_contract(new QContractDefinition())
{}
CompilationResult::CompilationResult(const solidity::CompilerStack& _compiler, QObject *_parent): CompilationResult::CompilationResult(const solidity::CompilerStack& _compiler, QObject *_parent):
QObject(_parent), m_successfull(true), QObject(_parent), m_successfull(true),
m_contract(new QContractDefinition(&_compiler.getContractDefinition(std::string()))), m_contract(new QContractDefinition(&_compiler.getContractDefinition(std::string()))),
@ -55,10 +63,17 @@ CompilationResult::CompilationResult(CompilationResult const& _prev, QString con
{} {}
CodeModel::CodeModel(QObject* _parent) : QObject(_parent), CodeModel::CodeModel(QObject* _parent) : QObject(_parent),
m_backgroundWorker(this), m_backgroundJobId(0) m_result(new CompilationResult(nullptr)), m_backgroundWorker(this), m_backgroundJobId(0)
{ {
m_backgroundWorker.moveToThread(&m_backgroundThread); m_backgroundWorker.moveToThread(&m_backgroundThread);
connect(this, &CodeModel::scheduleCompilationJob, &m_backgroundWorker, &BackgroundWorker::queueCodeChange, Qt::QueuedConnection); connect(this, &CodeModel::scheduleCompilationJob, &m_backgroundWorker, &BackgroundWorker::queueCodeChange, Qt::QueuedConnection);
connect(this, &CodeModel::compilationCompleteInternal, this, &CodeModel::onCompilationComplete, Qt::QueuedConnection);
qRegisterMetaType<CompilationResult*>("CompilationResult*");
qRegisterMetaType<QContractDefinition*>("QContractDefinition*");
qRegisterMetaType<QFunctionDefinition*>("QFunctionDefinition*");
qRegisterMetaType<QVariableDeclaration*>("QVariableDeclaration*");
qmlRegisterType<QFunctionDefinition>("org.ethereum.qml", 1, 0, "QFunctionDefinition");
qmlRegisterType<QVariableDeclaration>("org.ethereum.qml", 1, 0, "QVariableDeclaration");
m_backgroundThread.start(); m_backgroundThread.start();
} }
@ -93,19 +108,26 @@ void CodeModel::runCompilationJob(int _jobId, QString const& _code)
{ {
cs.setSource(_code.toStdString()); cs.setSource(_code.toStdString());
cs.compile(false); cs.compile(false);
std::shared_ptr<CompilationResult> result(new CompilationResult(cs, nullptr)); std::unique_ptr<CompilationResult> result(new CompilationResult(cs, nullptr));
m_result.swap(result);
qDebug() << QString(QApplication::tr("compilation succeeded")); qDebug() << QString(QApplication::tr("compilation succeeded"));
emit compilationCompleteInternal(result.release());
} }
catch (dev::Exception const& _exception) catch (dev::Exception const& _exception)
{ {
std::ostringstream error; std::ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", cs); solidity::SourceReferenceFormatter::printExceptionInformation(error, _exception, "Error", cs);
std::shared_ptr<CompilationResult> result(new CompilationResult(*m_result, QString::fromStdString(error.str()), nullptr)); std::unique_ptr<CompilationResult> result(new CompilationResult(*m_result, QString::fromStdString(error.str()), nullptr));
m_result.swap(result);
qDebug() << QString(QApplication::tr("compilation failed") + " " + m_result->compilerMessage()); qDebug() << QString(QApplication::tr("compilation failed") + " " + m_result->compilerMessage());
emit compilationCompleteInternal(result.release());
} }
}
void CodeModel::onCompilationComplete(CompilationResult*_newResult)
{
m_result.reset(_newResult);
emit compilationComplete(); emit compilationComplete();
if (m_result->successfull())
emit codeChanged();
} }
} }

23
mix/CodeModel.h

@ -59,16 +59,18 @@ private:
class CompilationResult : public QObject class CompilationResult : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QContractDefinition const* contract READ contract) Q_PROPERTY(QContractDefinition* contract READ contract)
public: public:
/// Empty compilation result constructor
CompilationResult(QObject* parent);
/// Successfull compilation result constructor /// Successfull compilation result constructor
CompilationResult(solidity::CompilerStack const& _compiler, QObject* parent); CompilationResult(solidity::CompilerStack const& _compiler, QObject* parent);
/// Failed compilation result constructor /// Failed compilation result constructor
CompilationResult(CompilationResult const& _prev, QString const& _compilerMessage, QObject* parent); CompilationResult(CompilationResult const& _prev, QString const& _compilerMessage, QObject* parent);
/// @returns contract definition /// @returns contract definition
QContractDefinition const* contract() const { return m_contract.get(); } QContractDefinition* contract() { return m_contract.get(); }
/// Indicates if the compilation was successfull /// Indicates if the compilation was successfull
bool successfull() const { return m_successfull; } bool successfull() const { return m_successfull; }
@ -84,7 +86,7 @@ public:
private: private:
bool m_successfull; bool m_successfull;
std::shared_ptr<QContractDefinition const> m_contract; std::shared_ptr<QContractDefinition> m_contract;
QString m_compilerMessage; ///< @todo: use some structure here QString m_compilerMessage; ///< @todo: use some structure here
dev::bytes m_bytes; dev::bytes m_bytes;
QString m_assemblyCode; QString m_assemblyCode;
@ -107,7 +109,11 @@ public:
~CodeModel(); ~CodeModel();
/// @returns latest compilation result /// @returns latest compilation result
std::shared_ptr<CompilationResult> lastCompilationResult() { return m_result; } CompilationResult* code() { return m_result.get(); }
/// @returns latest compilation resul
CompilationResult const* code() const { return m_result.get(); }
Q_PROPERTY(CompilationResult* code READ code NOTIFY codeChanged)
signals: signals:
/// Emited on compilation status change /// Emited on compilation status change
@ -116,6 +122,13 @@ signals:
void compilationComplete(); void compilationComplete();
/// Internal signal used to transfer compilation job to background thread /// Internal signal used to transfer compilation job to background thread
void scheduleCompilationJob(int _jobId, QString const& _content); void scheduleCompilationJob(int _jobId, QString const& _content);
/// Emitted if there are any changes in the code model
void codeChanged();
/// Emitted on compilation complete. Internal
void compilationCompleteInternal(CompilationResult* _newResult);
private slots:
void onCompilationComplete(CompilationResult*_newResult);
public slots: public slots:
/// Update code model on source code change /// Update code model on source code change
@ -125,7 +138,7 @@ private:
void runCompilationJob(int _jobId, QString const& _content); void runCompilationJob(int _jobId, QString const& _content);
void stop(); void stop();
std::shared_ptr<CompilationResult> m_result; std::unique_ptr<CompilationResult> m_result;
QThread m_backgroundThread; QThread m_backgroundThread;
BackgroundWorker m_backgroundWorker; BackgroundWorker m_backgroundWorker;
int m_backgroundJobId = 0; //protects from starting obsolete compilation job int m_backgroundJobId = 0; //protects from starting obsolete compilation job

2
mix/ConstantCompilationControl.cpp

@ -55,7 +55,7 @@ void ConstantCompilationControl::start() const
void ConstantCompilationControl::update() void ConstantCompilationControl::update()
{ {
auto result = m_ctx->codeModel()->lastCompilationResult(); auto result = m_ctx->codeModel()->code();
QObject* status = m_view->findChild<QObject*>("status", Qt::FindChildrenRecursively); QObject* status = m_view->findChild<QObject*>("status", Qt::FindChildrenRecursively);
QObject* content = m_view->findChild<QObject*>("content", Qt::FindChildrenRecursively); QObject* content = m_view->findChild<QObject*>("content", Qt::FindChildrenRecursively);

1
mix/DebuggingStateWrapper.h

@ -23,6 +23,7 @@
#pragma once #pragma once
#include <QStringList> #include <QStringList>
#include <QMap>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libethereum/State.h> #include <libethereum/State.h>
#include <libethereum/Executive.h> #include <libethereum/Executive.h>

49
mix/KeyEventManager.cpp

@ -1,49 +0,0 @@
/*
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 KeyEventManager.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Used as an event handler for all classes which need keyboard interactions.
* Can be improve by adding the possibility to register to a specific key.
*/
#include <QDebug>
#include <QKeySequence>
#include "KeyEventManager.h"
namespace dev
{
namespace mix
{
void KeyEventManager::registerEvent(const QObject* _receiver, const char* _slot)
{
QObject::connect(this, SIGNAL(onKeyPressed(int)), _receiver, _slot);
}
void KeyEventManager::unRegisterEvent(QObject* _receiver)
{
QObject::disconnect(_receiver);
}
void KeyEventManager::keyPressed(QVariant _event)
{
emit onKeyPressed(_event.toInt());
}
}
}

2
mix/MixApplication.cpp

@ -32,8 +32,8 @@ MixApplication::MixApplication(int _argc, char* _argv[]):
{ {
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager"); qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
QObject::connect(this, SIGNAL(lastWindowClosed()), context(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff QObject::connect(this, SIGNAL(lastWindowClosed()), context(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff
QObject::connect(engine(), SIGNAL(objectCreated(QObject*, QUrl)), context(), SLOT(resourceLoaded(QObject*, QUrl)));
m_engine->load(QUrl("qrc:/qml/main.qml")); m_engine->load(QUrl("qrc:/qml/main.qml"));
m_appContext->loadProject();
} }
MixApplication::~MixApplication() MixApplication::~MixApplication()

9
mix/QContractDefinition.h

@ -22,6 +22,7 @@
#pragma once #pragma once
#include <QObject> #include <QObject>
#include <QQmlListProperty>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include "QFunctionDefinition.h" #include "QFunctionDefinition.h"
#include "QBasicNodeDefinition.h" #include "QBasicNodeDefinition.h"
@ -34,16 +35,18 @@ namespace mix
class QContractDefinition: public QBasicNodeDefinition class QContractDefinition: public QBasicNodeDefinition
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QList<QFunctionDefinition*> functions READ functions) Q_PROPERTY(QQmlListProperty<dev::mix::QFunctionDefinition> functions READ functions CONSTANT)
public: public:
QContractDefinition() {}
QContractDefinition(solidity::ContractDefinition const* _contract); QContractDefinition(solidity::ContractDefinition const* _contract);
/// Get all the functions of the contract. /// Get all the functions of the contract.
QList<QFunctionDefinition*> functions() const { return m_functions; } QQmlListProperty<QFunctionDefinition> functions() const { return QQmlListProperty<QFunctionDefinition>(const_cast<QContractDefinition*>(this), const_cast<QContractDefinition*>(this)->m_functions); }
QList<QFunctionDefinition*> const& functionsList() const { return m_functions; }
private: private:
QList<QFunctionDefinition*> m_functions; QList<QFunctionDefinition*> m_functions;
}; };
} }
} }

10
mix/QFunctionDefinition.h

@ -22,8 +22,9 @@
#pragma once #pragma once
#include <QObject> #include <QObject>
#include <QQmlListProperty>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <QVariableDeclaration.h> #include "QVariableDeclaration.h"
#include "QBasicNodeDefinition.h" #include "QBasicNodeDefinition.h"
namespace dev namespace dev
@ -34,13 +35,16 @@ namespace mix
class QFunctionDefinition: public QBasicNodeDefinition class QFunctionDefinition: public QBasicNodeDefinition
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QList<QVariableDeclaration*> parameters READ parameters) Q_PROPERTY(QQmlListProperty<dev::mix::QVariableDeclaration> parameters READ parameters)
Q_PROPERTY(int index READ index) Q_PROPERTY(int index READ index)
public: public:
QFunctionDefinition() {}
QFunctionDefinition(solidity::FunctionDefinition const* _f, int _index); QFunctionDefinition(solidity::FunctionDefinition const* _f, int _index);
/// Get all input parameters of this function. /// Get all input parameters of this function.
QList<QVariableDeclaration*> parameters() const { return m_parameters; } QList<QVariableDeclaration*> const& parametersList() const { return m_parameters; }
/// Get all input parameters of this function as QML property.
QQmlListProperty<QVariableDeclaration> parameters() const { return QQmlListProperty<QVariableDeclaration>(const_cast<QFunctionDefinition*>(this), const_cast<QFunctionDefinition*>(this)->m_parameters); }
/// Get all return parameters of this function. /// Get all return parameters of this function.
QList<QVariableDeclaration*> returnParameters() const { return m_returnParameters; } QList<QVariableDeclaration*> returnParameters() const { return m_returnParameters; }
/// Get the index of this function on the contract ABI. /// Get the index of this function on the contract ABI.

3
mix/QVariableDeclaration.h

@ -35,6 +35,7 @@ class QVariableDeclaration: public QBasicNodeDefinition
Q_PROPERTY(QString type READ type CONSTANT) Q_PROPERTY(QString type READ type CONSTANT)
public: public:
QVariableDeclaration() {}
QVariableDeclaration(solidity::VariableDeclaration const* _v): QBasicNodeDefinition(_v), m_type(QString::fromStdString(_v->getType()->toString())) {} QVariableDeclaration(solidity::VariableDeclaration const* _v): QBasicNodeDefinition(_v), m_type(QString::fromStdString(_v->getType()->toString())) {}
QString type() const { return m_type; } QString type() const { return m_type; }
private: private:
@ -43,5 +44,3 @@ private:
} }
} }
Q_DECLARE_METATYPE(dev::mix::QVariableDeclaration*)

24
mix/TransactionListView.cpp → mix/StateListView.cpp

@ -14,7 +14,7 @@
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 TransactionListView.cpp /** @file StateListView.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com * @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2014 * @date 2014
* Ethereum IDE client. * Ethereum IDE client.
@ -25,31 +25,23 @@
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QQmlContext> #include <QQmlContext>
#include <QDebug> #include <QDebug>
#include "TransactionListView.h" #include "StateListView.h"
#include "TransactionListModel.h"
using namespace dev::mix; using namespace dev::mix;
TransactionListView::TransactionListView(AppContext* _context): Extension(_context, ExtensionDisplayBehavior::RightView) StateListView::StateListView(AppContext* _context): Extension(_context, ExtensionDisplayBehavior::RightView)
{ {
m_model.reset(new TransactionListModel(this, _context));
m_appEngine->rootContext()->setContextProperty("transactionListModel", m_model.get());
} }
TransactionListView::~TransactionListView() QString StateListView::contentUrl() const
{ {
//implementation is in cpp file so that all types deleted are complete return QStringLiteral("qrc:/qml/StateList.qml");
} }
QString TransactionListView::contentUrl() const QString StateListView::title() const
{ {
return QStringLiteral("qrc:/qml/TransactionList.qml"); return QApplication::tr("State");
} }
QString TransactionListView::title() const void StateListView::start() const
{
return QApplication::tr("Transactions");
}
void TransactionListView::start() const
{ {
} }

15
mix/TransactionListView.h → mix/StateListView.h

@ -11,7 +11,7 @@
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 TransactionListView.h /** @file StateListView.h
* @author Arkadiy Paronyan arkadiy@ethdev.com * @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2014 * @date 2014
* Ethereum IDE client. * Ethereum IDE client.
@ -21,7 +21,6 @@
#include <memory> #include <memory>
#include <QTextDocument> #include <QTextDocument>
#include "TransactionListView.h"
#include "Extension.h" #include "Extension.h"
namespace dev namespace dev
@ -29,25 +28,17 @@ namespace dev
namespace mix namespace mix
{ {
class TransactionListModel;
/// Transactions list control /// Transactions list control
/// @todo This should be moved into state as a sequence /// @todo This should be moved into state as a sequence
class TransactionListView: public Extension class StateListView: public Extension
{ {
Q_OBJECT Q_OBJECT
public: public:
TransactionListView(AppContext* _context); StateListView(AppContext* _context);
~TransactionListView();
void start() const override; void start() const override;
QString title() const override; QString title() const override;
QString contentUrl() const override; QString contentUrl() const override;
/// @returns the underlying model
TransactionListModel* model() const { return m_model.get(); }
private:
std::unique_ptr<TransactionListModel> m_model;
}; };
} }

202
mix/TransactionListModel.cpp

@ -1,202 +0,0 @@
/*
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 TransactionListModel.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <QObject>
#include <QQmlEngine>
#include <QTextDocument>
#include <QAbstractListModel>
#include "libdevcore/CommonJS.h"
#include "TransactionListModel.h"
#include "QContractDefinition.h"
#include "QFunctionDefinition.h"
#include "QVariableDeclaration.h"
#include "AppContext.h"
#include "CodeModel.h"
namespace dev
{
namespace mix
{
/// @todo Move this to QML
u256 fromQString(QString const& _s)
{
return dev::jsToU256(_s.toStdString());
}
/// @todo Move this to QML
QString toQString(u256 _value)
{
std::ostringstream s;
s << _value;
return QString::fromStdString(s.str());
}
TransactionListItem::TransactionListItem(int _index, TransactionSettings const& _t, QObject* _parent):
QObject(_parent), m_index(_index), m_title(_t.title), m_functionId(_t.functionId), m_value(toQString(_t.value)),
m_gas(toQString(_t.gas)), m_gasPrice(toQString(_t.gasPrice))
{}
TransactionListModel::TransactionListModel(QObject* _parent, AppContext* _appContext):
QAbstractListModel(_parent), m_appContext(_appContext)
{}
QHash<int, QByteArray> TransactionListModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[TitleRole] = "title";
roles[IdRole] = "transactionIndex";
return roles;
}
int TransactionListModel::rowCount(QModelIndex const& _parent) const
{
Q_UNUSED(_parent);
return m_transactions.size();
}
QVariant TransactionListModel::data(QModelIndex const& _index, int _role) const
{
if (_index.row() < 0 || _index.row() >= (int)m_transactions.size())
return QVariant();
auto const& transaction = m_transactions.at(_index.row());
switch (_role)
{
case TitleRole:
return QVariant(transaction.title);
case IdRole:
return QVariant(_index.row());
default:
return QVariant();
}
}
///@todo: get parameters from code model
QList<TransactionParameterItem*> buildParameters(CodeModel* _codeModel, TransactionSettings const& _transaction, QString const& _functionId)
{
QList<TransactionParameterItem*> params;
QContractDefinition const* contract = _codeModel->lastCompilationResult()->contract();
auto functions = contract->functions();
for (auto f : functions)
{
if (f->name() != _functionId)
continue;
auto parameters = f->parameters();
for (auto p : parameters)
{
QString paramValue;
if (f->name() == _transaction.functionId)
{
auto paramValueIter = _transaction.parameterValues.find(p->name());
if (paramValueIter != _transaction.parameterValues.cend())
paramValue = toQString(paramValueIter->second);
}
TransactionParameterItem* item = new TransactionParameterItem(p->name(), p->type(), paramValue);
QQmlEngine::setObjectOwnership(item, QQmlEngine::JavaScriptOwnership);
params.append(item);
}
}
return params;
}
///@todo: get fnctions from code model
QList<QString> TransactionListModel::getFunctions()
{
QList<QString> functionNames;
QContractDefinition const* contract = m_appContext->codeModel()->lastCompilationResult()->contract();
auto functions = contract->functions();
for (auto f : functions)
{
functionNames.append(f->name());
}
return functionNames;
}
QVariantList TransactionListModel::getParameters(int _index, QString const& _functionId)
{
TransactionSettings const& transaction = (_index >= 0 && _index < (int)m_transactions.size()) ? m_transactions[_index] : TransactionSettings();
auto plist = buildParameters(m_appContext->codeModel(), transaction, _functionId);
QVariantList vl;
for (QObject* p : plist)
vl.append(QVariant::fromValue(p));
return vl;
}
QObject* TransactionListModel::getItem(int _index)
{
TransactionSettings const& transaction = (_index >= 0 && _index < (int)m_transactions.size()) ? m_transactions[_index] : TransactionSettings();
TransactionListItem* item = new TransactionListItem(_index, transaction, nullptr);
QQmlEngine::setObjectOwnership(item, QQmlEngine::JavaScriptOwnership);
return item;
}
void TransactionListModel::edit(QObject* _data)
{
//these properties come from TransactionDialog QML object
///@todo change the model to a qml component
int index = _data->property("transactionIndex").toInt();
QString title = _data->property("transactionTitle").toString();
QString gas = _data->property("gas").toString();
QString gasPrice = _data->property("gasPrice").toString();
QString value = _data->property("transactionValue").toString();
QString functionId = _data->property("functionId").toString();
QAbstractListModel* paramsModel = qvariant_cast<QAbstractListModel*>(_data->property("transactionParams"));
TransactionSettings transaction(title, functionId, fromQString(value), fromQString(gas), fromQString(gasPrice));
int paramCount = paramsModel->rowCount(QModelIndex());
for (int p = 0; p < paramCount; ++p)
{
QString paramName = paramsModel->data(paramsModel->index(p, 0), Qt::DisplayRole).toString();
QString paramValue = paramsModel->data(paramsModel->index(p, 0), Qt::DisplayRole + 2).toString();
if (!paramValue.isEmpty() && !paramName.isEmpty())
transaction.parameterValues[paramName] = fromQString(paramValue);
}
if (index >= 0 && index < (int)m_transactions.size())
{
beginRemoveRows(QModelIndex(), index, index);
m_transactions.erase(m_transactions.begin() + index);
endRemoveRows();
}
else
index = rowCount(QModelIndex());
beginInsertRows(QModelIndex(), index, index);
m_transactions.push_back(transaction);
emit countChanged();
endInsertRows();
}
int TransactionListModel::getCount() const
{
return rowCount(QModelIndex());
}
void TransactionListModel::runTransaction(int _index)
{
TransactionSettings tr = m_transactions.at(_index);
emit transactionStarted(tr);
}
}
}

168
mix/TransactionListModel.h

@ -1,168 +0,0 @@
/*
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 TransactionListView.h
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#pragma once
#include <QObject>
#include <QVariant>
#include <QAbstractListModel>
#include <QHash>
#include <QByteArray>
#include <libdevcore/Common.h>
#include <libethcore/CommonEth.h>
namespace dev
{
namespace mix
{
class AppContext;
/// Backend transaction config class
struct TransactionSettings
{
TransactionSettings():
value(0), gas(10000), gasPrice(10 * dev::eth::szabo) {}
TransactionSettings(QString const& _title, QString const& _functionId, u256 _value, u256 _gas, u256 _gasPrice):
title(_title), functionId(_functionId), value(_value), gas(_gas), gasPrice(_gasPrice) {}
/// User specified transaction title
QString title;
/// Contract function name
QString functionId;
/// Transaction value
u256 value;
/// Gas
u256 gas;
/// Gas price
u256 gasPrice;
/// Mapping from contract function parameter name to value
std::map<QString, u256> parameterValues;
};
/// QML transaction parameter class
class TransactionParameterItem: public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(QString type READ type CONSTANT)
Q_PROPERTY(QString value READ value CONSTANT)
public:
TransactionParameterItem(QString const& _name, QString const& _type, QString const& _value):
m_name(_name), m_type(_type), m_value(_value) {}
/// Parameter name, set by contract definition
QString name() { return m_name; }
/// Parameter type, set by contract definition
QString type() { return m_type; }
/// Parameter value, set by user
QString value() { return m_value; }
private:
QString m_name;
QString m_type;
QString m_value;
};
class TransactionListItem: public QObject
{
Q_OBJECT
Q_PROPERTY(int index READ index CONSTANT)
Q_PROPERTY(QString title READ title CONSTANT)
Q_PROPERTY(QString functionId READ functionId CONSTANT)
Q_PROPERTY(QString gas READ gas CONSTANT)
Q_PROPERTY(QString gasPrice READ gasPrice CONSTANT)
Q_PROPERTY(QString value READ value CONSTANT)
public:
TransactionListItem(int _index, TransactionSettings const& _t, QObject* _parent);
/// User specified transaction title
QString title() { return m_title; }
/// Gas
QString gas() { return m_gas; }
/// Gas cost
QString gasPrice() { return m_gasPrice; }
/// Transaction value
QString value() { return m_value; }
/// Contract function name
QString functionId() { return m_functionId; }
/// Index of this transaction in the transactions list
int index() { return m_index; }
private:
int m_index;
QString m_title;
QString m_functionId;
QString m_value;
QString m_gas;
QString m_gasPrice;
};
/// QML model for a list of transactions
class TransactionListModel: public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(int count READ getCount() NOTIFY countChanged())
enum Roles
{
TitleRole = Qt::DisplayRole,
IdRole = Qt::UserRole + 1
};
public:
TransactionListModel(QObject* _parent, AppContext* _appContext);
~TransactionListModel() {}
QHash<int, QByteArray> roleNames() const override;
int rowCount(QModelIndex const& _parent) const override;
QVariant data(QModelIndex const& _index, int _role) const override;
int getCount() const;
/// Apply changes from transaction dialog. Argument is a dialog model as defined in TransactionDialog.qml
/// @todo Change that to transaction item
Q_INVOKABLE void edit(QObject* _data);
/// @returns transaction item for a give index
Q_INVOKABLE QObject* getItem(int _index);
/// @returns a list of functions for current contract
Q_INVOKABLE QList<QString> getFunctions();
/// @returns function parameters along with parameter values if set. @see TransactionParameterItem
Q_INVOKABLE QVariantList getParameters(int _id, QString const& _functionId);
/// Launch transaction execution UI handler
Q_INVOKABLE void runTransaction(int _index);
signals:
/// Transaction count has changed
void countChanged();
/// Transaction has be launched
void transactionStarted(dev::mix::TransactionSettings);
private:
std::vector<TransactionSettings> m_transactions;
AppContext* m_appContext;
};
}
}

3
mix/qml.qrc

@ -8,8 +8,9 @@
<file>qml/js/Debugger.js</file> <file>qml/js/Debugger.js</file>
<file>qml/BasicMessage.qml</file> <file>qml/BasicMessage.qml</file>
<file>qml/TransactionDialog.qml</file> <file>qml/TransactionDialog.qml</file>
<file>qml/TransactionList.qml</file>
<file>qml/ModalDialog.qml</file> <file>qml/ModalDialog.qml</file>
<file>qml/AlertMessageDialog.qml</file> <file>qml/AlertMessageDialog.qml</file>
<file>qml/StateDialog.qml</file>
<file>qml/StateList.qml</file>
</qresource> </qresource>
</RCC> </RCC>

35
mix/qml/CompilationStatus.qml

@ -0,0 +1,35 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
Rectangle {
anchors.fill: parent
width: parent.width
height: parent.height
color: "lightgray"
Text {
font.pointSize: 9
anchors.left: parent.left
anchors.top: parent.top
anchors.topMargin: 3
anchors.leftMargin: 3
height: 9
font.family: "Monospace"
objectName: "status"
id: status
}
TextArea {
readOnly: true
anchors.left: parent.left
anchors.leftMargin: 10
anchors.top: status.bottom
anchors.topMargin: 3
font.pointSize: 9
font.family: "Monospace"
height: parent.height * 0.8
width: parent.width - 20
wrapMode: Text.Wrap
backgroundVisible: false
objectName: "content"
id: content
}
}

119
mix/qml/DataDump.qml

@ -0,0 +1,119 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
import CodeEditorExtensionManager 1.0
Rectangle {
objectName: "mainContent"
signal keyPressed(variant event)
focus: true
Keys.enabled: true
Keys.onPressed:
{
root.keyPressed(event.key);
}
anchors.fill: parent
id: root
function ensureRightView()
{
if (!rightView.visible)
{
rightView.show();
}
}
CodeEditorExtensionManager {
headerView: headerPaneTabs;
rightView: rightPaneTabs;
editor: codeEditor
}
GridLayout
{
anchors.fill: parent
rows: 2
flow: GridLayout.TopToBottom
columnSpacing: 0
rowSpacing: 0
Rectangle {
Layout.row: 0
Layout.fillWidth: true
Layout.preferredHeight: 50
id: headerView
TabView {
id: headerPaneTabs
tabsVisible: false
antialiasing: true
anchors.fill: parent
style: TabViewStyle {
frameOverlap: 1
tab: Rectangle {}
frame: Rectangle {}
}
}
}
SplitView {
resizing: false
Layout.row: 1
orientation: Qt.Horizontal;
Layout.fillWidth: true
Layout.preferredHeight: root.height - headerView.height;
Rectangle {
id: editorRect;
height: parent.height;
width: parent.width;
TextArea {
id: codeEditor
anchors.fill: parent;
font.family: "Monospace"
font.pointSize: 12
backgroundVisible: true;
textColor: "white"
tabChangesFocus: false
style: TextAreaStyle {
backgroundColor: "black"
}
Keys.onPressed: {
if (event.key === Qt.Key_Tab) {
codeEditor.insert(codeEditor.cursorPosition, "\t");
event.accepted = true;
}
}
}
}
Rectangle {
visible: false;
id: rightView;
property real panelRelWidth: 0.38
function show() {
visible = true;
editorRect.width = parent.width * (1 - 0.38)
codeEditor.focus = false;
rightPaneTabs.focus = true;
}
height: parent.height;
width: Layout.minimumWidth
Layout.minimumWidth: parent.width * 0.38
Rectangle {
anchors.fill: parent;
id: rightPaneView
TabView {
id: rightPaneTabs
tabsVisible: false
antialiasing: true
anchors.fill: parent
style: TabViewStyle {
frameOverlap: 1
tab: Rectangle {}
frame: Rectangle {}
}
}
}
}
}
}
}

38
mix/qml/DebugBasicInfo.qml

@ -0,0 +1,38 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
Item {
id: button
signal clicked
signal pressed
signal released
width: sprite.width
height: sprite.height
MouseArea {
id: mouseArea
enabled: button.enabled
anchors.fill: button
hoverEnabled: true
onClicked: button.clicked()
onPressed: button.pressed()
onReleased: button.released()
}
onClicked: {
}
onPressed: {
opacity = 0.5
}
onReleased: {
opacity = 1.0
}
}

119
mix/qml/ImageButton.qml

@ -0,0 +1,119 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
import CodeEditorExtensionManager 1.0
Rectangle {
objectName: "mainContent"
signal keyPressed(variant event)
focus: true
Keys.enabled: true
Keys.onPressed:
{
root.keyPressed(event.key);
}
anchors.fill: parent
id: root
function ensureRightView()
{
if (!rightView.visible)
{
rightView.show();
}
}
CodeEditorExtensionManager {
headerView: headerPaneTabs;
rightView: rightPaneTabs;
editor: codeEditor
}
GridLayout
{
anchors.fill: parent
rows: 2
flow: GridLayout.TopToBottom
columnSpacing: 0
rowSpacing: 0
Rectangle {
Layout.row: 0
Layout.fillWidth: true
Layout.preferredHeight: 50
id: headerView
TabView {
id: headerPaneTabs
tabsVisible: false
antialiasing: true
anchors.fill: parent
style: TabViewStyle {
frameOverlap: 1
tab: Rectangle {}
frame: Rectangle {}
}
}
}
SplitView {
resizing: false
Layout.row: 1
orientation: Qt.Horizontal;
Layout.fillWidth: true
Layout.preferredHeight: root.height - headerView.height;
Rectangle {
id: editorRect;
height: parent.height;
width: parent.width;
TextArea {
id: codeEditor
anchors.fill: parent;
font.family: "Monospace"
font.pointSize: 12
backgroundVisible: true;
textColor: "white"
tabChangesFocus: false
style: TextAreaStyle {
backgroundColor: "black"
}
Keys.onPressed: {
if (event.key === Qt.Key_Tab) {
codeEditor.insert(codeEditor.cursorPosition, "\t");
event.accepted = true;
}
}
}
}
Rectangle {
visible: false;
id: rightView;
property real panelRelWidth: 0.38
function show() {
visible = true;
editorRect.width = parent.width * (1 - 0.38)
codeEditor.focus = false;
rightPaneTabs.focus = true;
}
height: parent.height;
width: Layout.minimumWidth
Layout.minimumWidth: parent.width * 0.38
Rectangle {
anchors.fill: parent;
id: rightPaneView
TabView {
id: rightPaneTabs
tabsVisible: false
antialiasing: true
anchors.fill: parent
style: TabViewStyle {
frameOverlap: 1
tab: Rectangle {}
frame: Rectangle {}
}
}
}
}
}
}
}

182
mix/qml/StateDialog.qml

@ -0,0 +1,182 @@
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
Window {
modality: Qt.WindowModal
width:640
height:480
visible: false
property alias stateTitle : titleField.text
property alias stateBalance : balanceField.text
property int stateIndex
property var stateTransactions: []
signal accepted
function open(index, item) {
stateIndex = index;
stateTitle = item.title;
stateBalance = item.balance;
transactionsModel.clear();
stateTransactions = [];
var transactions = item.transactions;
for (var t = 0; t < transactions.length; t++) {
transactionsModel.append(item.transactions[t]);
stateTransactions.push(item.transactions[t]);
}
visible = true;
titleField.focus = true;
}
function close() {
visible = false;
}
function getItem() {
var item = {
title: stateDialog.stateTitle,
balance: stateDialog.stateBalance,
transactions: []
}
item.transactions = stateTransactions;
return item;
}
GridLayout {
id: dialogContent
columns: 2
anchors.fill: parent
anchors.margins: 10
rowSpacing: 10
columnSpacing: 10
Label {
text: qsTr("Title")
}
TextField {
id: titleField
focus: true
Layout.fillWidth: true
}
Label {
text: qsTr("Balance")
}
TextField {
id: balanceField
Layout.fillWidth: true
}
Label {
text: qsTr("Transactions")
}
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
model: transactionsModel
delegate: transactionRenderDelegate
}
Label {
}
Button {
text: qsTr("Add")
onClicked: transactionsModel.addTransaction()
}
}
RowLayout {
anchors.bottom: parent.bottom
anchors.right: parent.right;
Button {
text: qsTr("Ok");
onClicked: {
close();
accepted();
}
}
Button {
text: qsTr("Cancel");
onClicked: close();
}
}
ListModel {
id: transactionsModel
function editTransaction(index) {
transactionDialog.open(index, transactionsModel.get(index));
}
function addTransaction() {
// Set next id here to work around Qt bug
// https://bugreports.qt-project.org/browse/QTBUG-41327
// Second call to signal handler would just edit the item that was just created, no harm done
var item = {
value: "0",
functionId: "",
gas: "1000000000000",
gasPrice: "100000"
};
transactionDialog.open(transactionsModel.count, item);
}
function deleteTransaction(index) {
stateTransactions.splice(index, 1);
transactionsModel.remove(index);
}
}
Component {
id: transactionRenderDelegate
Item {
id: wrapperItem
height: 20
width: parent.width
RowLayout {
anchors.fill: parent
Text {
Layout.fillWidth: true
Layout.fillHeight: true
text: functionId
font.pointSize: 12
verticalAlignment: Text.AlignBottom
}
ToolButton {
text: qsTr("Edit");
Layout.fillHeight: true
onClicked: transactionsModel.editTransaction(index)
}
ToolButton {
text: qsTr("Delete");
Layout.fillHeight: true
onClicked: transactionsModel.deleteTransaction(index)
}
}
}
}
TransactionDialog {
id: transactionDialog
onAccepted: {
var item = transactionDialog.getItem();
if (transactionDialog.transactionIndex < transactionsModel.count) {
transactionsModel.set(transactionDialog.transactionIndex, item);
stateTransactions[index] = item;
} else {
transactionsModel.append(item);
stateTransactions.push(item);
}
}
}
}

125
mix/qml/StateList.qml

@ -0,0 +1,125 @@
import QtQuick 2.2
import QtQuick.Controls.Styles 1.2
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
Rectangle {
color: "transparent"
id: stateListContainer
focus: true
anchors.topMargin: 10
anchors.left: parent.left
height: parent.height
width: parent.width
property var stateList: []
Connections {
target: appContext
onProjectLoaded: {
var items = JSON.parse(_json);
for(var i = 0; i < items.length; i++) {
stateListModel.append(items[i]);
stateList.push(items[i])
}
}
}
ListView {
anchors.top: parent.top
height: parent.height
width: parent.width
model: stateListModel
delegate: renderDelegate
}
Button {
anchors.bottom: parent.bottom
text: qsTr("Add")
onClicked: stateListModel.addState();
}
StateDialog {
id: stateDialog
onAccepted: {
var item = stateDialog.getItem();
if (stateDialog.stateIndex < stateListModel.count) {
stateList[stateDialog.stateIndex] = item;
stateListModel.set(stateDialog.stateIndex, item);
} else {
stateList.push(item);
stateListModel.append(item);
}
stateListModel.save();
}
}
ListModel {
id: stateListModel
function addState() {
var item = {
title: "",
balance: "1000000000000",
transactions: []
};
stateDialog.open(stateListModel.count, item);
}
function editState(index) {
stateDialog.open(index, stateList[index]);
}
function runState(index) {
var item = stateList[index];
debugModel.debugState(item);
}
function deleteState(index) {
stateListModel.remove(index);
stateList.splice(index, 1);
save();
}
function save() {
var json = JSON.stringify(stateList);
appContext.saveProject(json);
}
}
Component {
id: renderDelegate
Item {
id: wrapperItem
height: 20
width: parent.width
RowLayout {
anchors.fill: parent
Text {
Layout.fillWidth: true
Layout.fillHeight: true
text: title
font.pointSize: 12
verticalAlignment: Text.AlignBottom
}
ToolButton {
text: qsTr("Edit");
Layout.fillHeight: true
onClicked: stateListModel.editState(index);
}
ToolButton {
text: qsTr("Delete");
Layout.fillHeight: true
onClicked: stateListModel.deleteState(index);
}
ToolButton {
text: qsTr("Run");
Layout.fillHeight: true
onClicked: stateListModel.runState(index);
}
}
}
}
}

95
mix/qml/StepActionImage.qml

@ -0,0 +1,95 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
ColumnLayout {
property string title
property variant listModel;
height: 250
RowLayout {
Image {
source: "qrc:/qml/img/jumpoverback.png"
width: 15
sourceSize.width: 15
id: storageImgArrow
}
Text {
anchors.left: storageImgArrow.right
color: "#8b8b8b"
text: title
id: storageListTitle
}
MouseArea
{
anchors.fill: parent
onClicked: {
if (storageContainer.state == "collapsed")
storageContainer.state = "";
else
storageContainer.state = "collapsed";
}
}
}
Rectangle
{
Layout.fillWidth: true
states: [
State {
name: "collapsed"
PropertyChanges {
target: storageContainer
height: 0
opacity: 0
visible: false
}
PropertyChanges {
target: storageContainer.parent
height: 20
}
PropertyChanges {
target: storageList
height: 0
opacity: 0
visible: false
}
}
]
id: storageContainer
border.width: 3
border.color: "#deddd9"
anchors.top: storageListTitle.bottom
height: 223
anchors.topMargin: 5
width: parent.width
ListView {
anchors.top: parent.top
anchors.left: parent.left
anchors.topMargin: 5
anchors.leftMargin: 5
width: parent.width
height: parent.height
id: storageList
model: listModel
delegate:
Component {
Item {
height: 20
width: parent.width
Text {
color: "#8b8b8b"
text: modelData
font.pointSize: 9
}
}
}
}
}
}

98
mix/qml/Storage.qml

@ -0,0 +1,98 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1
ColumnLayout {
property variant listModel;
property string title;
height: 250
//width: width //debugPanel.width
//anchors.left: machineStates.left
//anchors.right: machineStates.right
//anchors.leftMargin: machineStates.sideMargin
RowLayout {
Image {
source: "qrc:/qml/img/jumpoverback.png"
width: 15
sourceSize.width: 15
id: imgArrow
}
Text {
color: "#8b8b8b"
text: title
id: listTitle
}
MouseArea
{
anchors.fill: parent
onClicked: {
if (listContainer.state === "collapsed")
listContainer.state = "";
else
listContainer.state = "collapsed";
}
}
}
Rectangle
{
Layout.fillWidth: true
states: [
State {
name: "collapsed"
PropertyChanges {
target: listContainer
height: 0
opacity: 0
visible: false
}
PropertyChanges {
target: listContainer.parent
height: 20
}
PropertyChanges {
target: dumpList
height: 0
opacity: 0
visible: false
}
}
]
id: listContainer
border.width: 3
border.color: "#deddd9"
anchors.top: listTitle.bottom
height: 223
anchors.topMargin: 5
width: parent.width
ListView {
anchors.top: parent.top
anchors.left: parent.left
anchors.topMargin: 5
anchors.leftMargin: 5
width: parent.width
height: parent.height
id: dumpList
delegate: Component {
Item {
height: 20
width: parent.width
Text {
color: "#8b8b8b"
text: modelData
font.pointSize: 9
}
}
}
}
}
}

75
mix/qml/TransactionDialog.qml

@ -5,51 +5,43 @@ import QtQuick.Window 2.0
Window { Window {
modality: Qt.WindowModal modality: Qt.WindowModal
width:640 width:640
height:480 height:480
visible: false visible: false
function open()
{
visible = true;
}
function close()
{
visible = false;
}
property alias focus : titleField.focus
property alias transactionTitle : titleField.text
property int transactionIndex property int transactionIndex
property alias transactionParams : paramsModel; property alias transactionParams : paramsModel;
property alias gas : gasField.text; property alias gas : gasField.text;
property alias gasPrice : gasPriceField.text; property alias gasPrice : gasPriceField.text;
property alias transactionValue : valueField.text; property alias transactionValue : valueField.text;
property alias functionId : functionComboBox.currentText; property alias functionId : functionComboBox.currentText;
property var model; property var itemParams;
signal accepted; signal accepted;
function reset(index, m) { function open(index, item) {
model = m;
var item = model.getItem(index);
transactionIndex = index; transactionIndex = index;
transactionTitle = item.title;
gas = item.gas; gas = item.gas;
gasPrice = item.gasPrice; gasPrice = item.gasPrice;
transactionValue = item.value; transactionValue = item.value;
var functionId = item.functionId; var functionId = item.functionId;
itemParams = item.parameters !== undefined ? item.parameters : {};
functionsModel.clear(); functionsModel.clear();
var functionIndex = -1; var functionIndex = -1;
var functions = model.getFunctions(); var functions = codeModel.code.contract.functions;
for (var f = 0; f < functions.length; f++) { for (var f = 0; f < functions.length; f++) {
functionsModel.append({ text: functions[f] }); functionsModel.append({ text: functions[f].name });
if (functions[f] === item.functionId) if (functions[f].name === item.functionId)
functionIndex = f; functionIndex = f;
} }
if (functionIndex == -1 && functionsModel.count > 0)
functionIndex = 0; //@todo suggest unused funtion
functionComboBox.currentIndex = functionIndex; functionComboBox.currentIndex = functionIndex;
loadParameters();
visible = true;
valueField.focus = true;
} }
function loadParameters() { function loadParameters() {
@ -57,13 +49,36 @@ Window {
return; return;
paramsModel.clear(); paramsModel.clear();
if (functionComboBox.currentIndex >= 0 && functionComboBox.currentIndex < functionsModel.count) { if (functionComboBox.currentIndex >= 0 && functionComboBox.currentIndex < functionsModel.count) {
var parameters = model.getParameters(transactionIndex, functionsModel.get(functionComboBox.currentIndex).text); var func = codeModel.code.contract.functions[functionComboBox.currentIndex];
var parameters = func.parameters;
for (var p = 0; p < parameters.length; p++) { for (var p = 0; p < parameters.length; p++) {
paramsModel.append({ name: parameters[p].name, type: parameters[p].type, value: parameters[p].value }); var pname = parameters[p].name;
paramsModel.append({ name: pname, type: parameters[p].type, value: itemParams[pname] !== undefined ? itemParams[pname] : "" });
} }
} }
} }
function close()
{
visible = false;
}
function getItem()
{
var item = {
functionId: transactionDialog.functionId,
gas: transactionDialog.gas,
gasPrice: transactionDialog.gasPrice,
value: transactionDialog.transactionValue,
parameters: {}
}
for (var p = 0; p < transactionDialog.transactionParams.count; p++) {
var parameter = transactionDialog.transactionParams.get(p);
item.parameters[parameter.name] = parameter.value;
}
return item;
}
GridLayout { GridLayout {
id: dialogContent id: dialogContent
columns: 2 columns: 2
@ -72,19 +87,9 @@ Window {
rowSpacing: 10 rowSpacing: 10
columnSpacing: 10 columnSpacing: 10
Label {
text: qsTr("Title")
}
TextField {
id: titleField
focus: true
Layout.fillWidth: true
}
Label { Label {
text: qsTr("Function") text: qsTr("Function")
} }
ComboBox { ComboBox {
id: functionComboBox id: functionComboBox
Layout.fillWidth: true Layout.fillWidth: true
@ -98,6 +103,7 @@ Window {
loadParameters(); loadParameters();
} }
} }
Label { Label {
text: qsTr("Value") text: qsTr("Value")
} }
@ -195,7 +201,8 @@ Window {
Connections { Connections {
target: loaderEditor.item target: loaderEditor.item
onTextChanged: { onTextChanged: {
paramsModel.setProperty(styleData.row, styleData.role, loaderEditor.item.text); if (styleData.role === "value" && styleData.row < paramsModel.count)
paramsModel.setProperty(styleData.row, styleData.role, loaderEditor.item.text);
} }
} }
sourceComponent: (styleData.selected) ? editor : null sourceComponent: (styleData.selected) ? editor : null

89
mix/qml/TransactionList.qml

@ -1,89 +0,0 @@
import QtQuick 2.2
import QtQuick.Controls.Styles 1.2
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
Rectangle {
color: "transparent"
id: transactionListContainer
focus: true
anchors.topMargin: 10
anchors.left: parent.left
height: parent.height
width: parent.width
ListView {
anchors.top: parent.top
height: parent.height
width: parent.width
id: transactionList
model: transactionListModel
delegate: renderDelegate
}
Button {
anchors.bottom: parent.bottom
text: qsTr("Add")
onClicked:
{
// Set next id here to work around Qt bug
// https://bugreports.qt-project.org/browse/QTBUG-41327
// Second call to signal handle would just edit the item that was just created, no harm done
transactionDialog.reset(transactionListModel.count, transactionListModel);
transactionDialog.open();
transactionDialog.focus = true;
}
}
TransactionDialog {
id: transactionDialog
onAccepted: {
transactionListModel.edit(transactionDialog);
}
}
Component {
id: renderDelegate
Item {
id: wrapperItem
height: 20
width: parent.width
RowLayout
{
anchors.fill: parent
Text {
//anchors.fill: parent
Layout.fillWidth: true
Layout.fillHeight: true
text: title
font.pointSize: 12
verticalAlignment: Text.AlignBottom
}
ToolButton {
text: qsTr("Edit");
Layout.fillHeight: true
onClicked: {
transactionDialog.reset(index, transactionListModel);
transactionDialog.open();
transactionDialog.focus = true;
}
}
ToolButton {
text: qsTr("Delete");
Layout.fillHeight: true
onClicked: {
}
}
ToolButton {
text: qsTr("Run");
Layout.fillHeight: true
onClicked: {
transactionListModel.runTransaction(index);
}
}
}
}
}
}

BIN
mix/qml/img/jumpintoback.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

BIN
mix/qml/img/jumpintobackdisabled.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 B

BIN
mix/qml/img/jumpintoforward.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 785 B

BIN
mix/qml/img/jumpintoforwarddisabled.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

BIN
mix/qml/img/jumpoutback.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 B

BIN
mix/qml/img/jumpoutbackdisabled.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

BIN
mix/qml/img/jumpoutforward.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 B

BIN
mix/qml/img/jumpoutforwarddisabled.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

BIN
mix/qml/img/jumpoverback.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

BIN
mix/qml/img/jumpoverbackdisabled.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 B

BIN
mix/qml/img/jumpoverforward.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

181
mix/qml/js/ErrorLocationFormater.js

@ -0,0 +1,181 @@
//humanReadableExecutionCode => contain human readable code.
//debugStates => contain all debug states.
//bytesCodeMapping => mapping between humanReadableExecutionCode and bytesCode.
//statesList => ListView
var currentSelectedState = null;
var jumpStartingPoint = null;
function init()
{
statesSlider.maximumValue = debugStates.length - 1;
statesList.model = humanReadableExecutionCode;
currentSelectedState = 0;
select(currentSelectedState);
//displayReturnValue();
jumpoutbackaction.state = "disabled";
jumpintobackaction.state = "disabled";
jumpintoforwardaction.state = "disabled"
jumpoutforwardaction.state = "disabled"
}
function moveSelection(incr)
{
if (currentSelectedState + incr >= 0)
{
if (currentSelectedState + incr < debugStates.length)
{
select(currentSelectedState + incr);
}
else
{
//endOfDebug();
}
statesSlider.value = currentSelectedState;
}
}
function select(stateIndex)
{
var codeLine = codeStr(stateIndex);
var state = debugStates[stateIndex];
highlightSelection(codeLine);
currentSelectedState = stateIndex;
completeCtxInformation(state);
//levelList.model = state.levels;
//levelList.update();
if (state.instruction === "JUMP")
jumpintoforwardaction.state = "";
else
jumpintoforwardaction.state = "disabled";
if (state.instruction === "JUMPDEST")
jumpintobackaction.state = "";
else
jumpintobackaction.state = "disabled";
}
function codeStr(stateIndex)
{
var state = debugStates[stateIndex];
return bytesCodeMapping.getValue(state.curPC);
}
function highlightSelection(index)
{
statesList.currentIndex = index;
}
function completeCtxInformation(state)
{
basicInfo.currentStep = state.step;
basicInfo.mem = state.newMemSize + " " + qsTr("words");
basicInfo.stepCost = state.gasCost;
basicInfo.gasSpent = debugStates[0].gas - state.gas;
// This is available in all editors.
stack.listModel = state.debugStack;
storage.listModel = state.debugStorage;
memoryDump.listModel = state.debugMemory;
callDataDump.listModel = state.debugCallData;
}
function endOfDebug()
{
var state = debugStates[debugStates.length - 1];
debugStorageTxt.text = "";
debugCallDataTxt.text = "";
debugStackTxt.text = "";
debugMemoryTxt.text = state.endOfDebug;
headerInfoLabel.text = "EXIT | GAS: " + state.gasLeft;
}
function displayReturnValue()
{
headerReturnList.model = contractCallReturnParameters;
headerReturnList.update();
}
function stepOutBack()
{
if (jumpStartingPoint != null)
{
select(jumpStartingPoint);
jumpStartingPoint = null;
jumpoutbackaction.state = "disabled";
jumpoutforwardaction.state = "disabled";
}
}
function stepIntoBack()
{
moveSelection(-1);
}
function stepOverBack()
{
var state = debugStates[currentSelectedState];
if (state.instruction === "JUMPDEST")
{
for (var k = currentSelectedState; k > 0; k--)
{
var line = bytesCodeMapping.getValue(debugStates[k].curPC);
if (line === statesList.currentIndex - 2)
{
select(k);
break;
}
}
}
else
moveSelection(-1);
}
function stepOverForward()
{
var state = debugStates[currentSelectedState];
if (state.instruction === "JUMP")
{
for (var k = currentSelectedState; k < debugStates.length; k++)
{
var line = bytesCodeMapping.getValue(debugStates[k].curPC);
if (line === statesList.currentIndex + 2)
{
select(k);
break;
}
}
}
else
moveSelection(1);
}
function stepIntoForward()
{
var state = debugStates[currentSelectedState];
if (state.instruction === "JUMP")
{
jumpStartingPoint = currentSelectedState;
moveSelection(1);
jumpoutbackaction.state = "";
jumpoutforwardaction.state = "";
}
}
function stepOutForward()
{
if (jumpStartingPoint != null)
{
stepOutBack();
stepOverForward();
jumpoutbackaction.state = "disabled";
jumpoutforwardaction.state = "disabled";
}
}
function jumpTo(value)
{
currentSelectedState = value;
select(currentSelectedState);
}

68
mix/qml/js/main.js

@ -0,0 +1,68 @@
//humanReadableExecutionCode => contain human readable code.
//debugStates => contain all debug states.
//bytesCodeMapping => mapping between humanReadableExecutionCode and bytesCode.
//statesList => ListView
var currentSelectedState = null;
function init()
{
currentSelectedState = 0;
select(currentSelectedState);
displayReturnValue();
}
function moveSelection(incr)
{
if (currentSelectedState + incr >= 0)
{
if (currentSelectedState + incr < debugStates.length)
{
select(currentSelectedState + incr);
}
else
{
endOfDebug();
}
}
}
function select(stateIndex)
{
var state = debugStates[stateIndex];
var codeStr = bytesCodeMapping.getValue(state.curPC);
highlightSelection(codeStr);
currentSelectedState = stateIndex;
completeCtxInformation(state);
levelList.model = state.levels;
levelList.update();
}
function highlightSelection(index)
{
statesList.currentIndex = index;
}
function completeCtxInformation(state)
{
debugStackTxt.text = state.debugStack;
debugStorageTxt.text = state.debugStorage;
debugMemoryTxt.text = state.debugMemory;
debugCallDataTxt.text = state.debugCallData;
headerInfoLabel.text = state.headerInfo
}
function endOfDebug()
{
var state = debugStates[debugStates.length - 1];
debugStorageTxt.text = "";
debugCallDataTxt.text = "";
debugStackTxt.text = "";
debugMemoryTxt.text = state.endOfDebug;
headerInfoLabel.text = "EXIT | GAS: " + state.gasLeft;
}
function displayReturnValue()
{
headerReturnList.model = contractCallReturnParameters;
headerReturnList.update();
}

21
mix/qml/main.qml

@ -3,7 +3,7 @@ import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1 import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQuick.Window 2.0 import QtQuick.Window 2.1
import CodeEditorExtensionManager 1.0 import CodeEditorExtensionManager 1.0
ApplicationWindow { ApplicationWindow {
@ -23,6 +23,11 @@ ApplicationWindow {
onTriggered: Qt.quit(); onTriggered: Qt.quit();
} }
} }
Menu {
title: qsTr("Debug")
MenuItem { action: debugRunAction }
MenuItem { action: debugResetStateAction }
}
} }
Component.onCompleted: { Component.onCompleted: {
setX(Screen.width / 2 - width / 2); setX(Screen.width / 2 - width / 2);
@ -41,4 +46,18 @@ ApplicationWindow {
objectName: "alertMessageDialog" objectName: "alertMessageDialog"
id: messageDialog id: messageDialog
} }
Action {
id: debugRunAction
text: "&Run"
shortcut: "F5"
onTriggered: debugModel.debugDeployment();
}
Action {
id: debugResetStateAction
text: "Reset &State"
shortcut: "F6"
onTriggered: debugModel.resetState();
}
} }

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) constant 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()
} }

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

Loading…
Cancel
Save