Browse Source

Merge pull request #1771 from debris/v8console

v8 javascript console
cl-refactor
Gav Wood 10 years ago
parent
commit
d3ee4263bd
  1. 13
      CMakeLists.txt
  2. 10
      cmake/EthDependencies.cmake
  3. 49
      cmake/Findv8.cmake
  4. 8
      eth/CMakeLists.txt
  5. 36
      eth/main.cpp
  6. 30
      libjsconsole/CMakeLists.txt
  7. 86
      libjsconsole/JSConsole.cpp
  8. 53
      libjsconsole/JSConsole.h
  9. 54
      libjsconsole/JSV8Connector.cpp
  10. 50
      libjsconsole/JSV8Connector.h
  11. 36
      libjsengine/CMakeLists.txt
  12. 11
      libjsengine/Common.js
  13. 36
      libjsengine/JSEngine.cpp
  14. 60
      libjsengine/JSEngine.h
  15. 23
      libjsengine/JSPrinter.cpp
  16. 41
      libjsengine/JSPrinter.h
  17. 8
      libjsengine/JSResources.cmake
  18. 187
      libjsengine/JSV8Engine.cpp
  19. 61
      libjsengine/JSV8Engine.h
  20. 49
      libjsengine/JSV8Printer.cpp
  21. 43
      libjsengine/JSV8Printer.h
  22. 84
      libjsengine/JSV8RPC.cpp
  23. 47
      libjsengine/JSV8RPC.h
  24. 91
      libjsengine/PrettyPrint.js
  25. 17
      test/CMakeLists.txt
  26. 5
      test/libjsengine/CMakeLists.txt
  27. 71
      test/libjsengine/JSV8Engine.cpp

13
CMakeLists.txt

@ -40,6 +40,7 @@ option(GUI "Build GUI components (AlethZero, Mix)" ON)
option(TESTS "Build the tests." ON)
option(EVMJIT "Build just-in-time compiler for EVM code (requires LLVM)" OFF)
option(ETHASHCL "Build in support for GPU mining via OpenCL" OFF)
option(JSCONSOLE "Build in javascript console" OFF)
# propagates CMake configuration options to the compiler
function(configureProject)
@ -193,9 +194,14 @@ eth_format_option(GUI)
eth_format_option(TESTS)
eth_format_option(TOOLS)
eth_format_option(ETHASHCL)
eth_format_option(JSCONSOLE)
eth_format_option_on_decent_platform(SERPENT)
eth_format_option_on_decent_platform(NCURSES)
if (JSCONSOLE)
set(JSONRPC ON)
endif()
if (GUI)
set(JSONRPC ON)
endif()
@ -284,6 +290,7 @@ message("-- GUI Build GUI components ${GUI}")
message("-- NCURSES Build NCurses components ${NCURSES}")
message("-- TESTS Build tests ${TESTS}")
message("-- ETHASHCL Build OpenCL components (experimental!) ${ETHASHCL}")
message("-- JSCONSOLE Build with javascript console ${JSCONSOLE}")
message("-- EVMJIT Build LLVM-based JIT EVM (experimental!) ${EVMJIT}")
message("------------------------------------------------------------------------")
message("")
@ -328,6 +335,11 @@ if (JSONRPC)
add_subdirectory(libweb3jsonrpc)
endif()
if (JSCONSOLE)
add_subdirectory(libjsengine)
add_subdirectory(libjsconsole)
endif()
add_subdirectory(secp256k1)
add_subdirectory(libp2p)
add_subdirectory(libdevcrypto)
@ -384,6 +396,7 @@ if (GUI)
endif()
#unset(TARGET_PLATFORM CACHE)
if (WIN32)

10
cmake/EthDependencies.cmake

@ -31,7 +31,8 @@ endif()
# homebrew installs qts in opt
if (APPLE)
set (CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "/usr/local/opt/qt5")
set (CMAKE_PREFIX_PATH "/usr/local/opt/qt5" ${CMAKE_PREFIX_PATH})
set (CMAKE_PREFIX_PATH "/usr/local/opt/v8-315" ${CMAKE_PREFIX_PATH})
endif()
find_program(CTEST_COMMAND ctest)
@ -47,6 +48,13 @@ find_package (LevelDB REQUIRED)
message(" - LevelDB header: ${LEVELDB_INCLUDE_DIRS}")
message(" - LevelDB lib: ${LEVELDB_LIBRARIES}")
if (JSCONSOLE)
find_package (v8 REQUIRED)
message(" - v8 header: ${V8_INCLUDE_DIRS}")
message(" - v8 lib : ${V8_LIBRARIES}")
add_definitions(-DETH_JSCONSOLE)
endif()
# TODO the Jsoncpp package does not yet check for correct version number
find_package (Jsoncpp 0.60 REQUIRED)
message(" - Jsoncpp header: ${JSONCPP_INCLUDE_DIRS}")

49
cmake/Findv8.cmake

@ -0,0 +1,49 @@
# Find v8
#
# Find the v8 includes and library
#
# if you nee to add a custom library search path, do it via via CMAKE_PREFIX_PATH
#
# This module defines
# V8_INCLUDE_DIRS, where to find header, etc.
# V8_LIBRARIES, the libraries needed to use v8.
# V8_FOUND, If false, do not try to use v8.
# only look in default directories
find_path(
V8_INCLUDE_DIR
NAMES v8.h
DOC "v8 include dir"
)
find_library(
V8_LIBRARY
NAMES v8
DOC "v8 library"
)
set(V8_INCLUDE_DIRS ${V8_INCLUDE_DIR})
set(V8_LIBRARIES ${V8_LIBRARY})
# debug library on windows
# same naming convention as in qt (appending debug library with d)
# boost is using the same "hack" as us with "optimized" and "debug"
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
find_library(
V8_LIBRARY_DEBUG
NAMES v8d
DOC "v8 debug library"
)
set(V8_LIBRARIES optimized ${V8_LIBRARIES} debug ${V8_LIBRARY_DEBUG})
endif()
# handle the QUIETLY and REQUIRED arguments and set V8_FOUND to TRUE
# if all listed variables are TRUE, hide their existence from configuration view
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(v8 DEFAULT_MSG
V8_INCLUDE_DIR V8_LIBRARY)
mark_as_advanced (V8_INCLUDE_DIR V8_LIBRARY)

8
eth/CMakeLists.txt

@ -7,6 +7,10 @@ include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
if (JSCONSOLE)
include_directories(${V8_INCLUDE_DIRS})
endif()
set(EXECUTABLE eth)
file(GLOB HEADERS "*.h")
@ -33,6 +37,10 @@ endif()
target_link_libraries(${EXECUTABLE} webthree)
target_link_libraries(${EXECUTABLE} ethash)
if (JSCONSOLE)
target_link_libraries(${EXECUTABLE} jsconsole)
endif()
if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW)
eth_copy_dlls("${EXECUTABLE}" MHD_DLLS)
endif()

36
eth/main.cpp

@ -38,6 +38,9 @@
#include <libevm/VMFactory.h>
#include <libethereum/All.h>
#include <libwebthree/WebThree.h>
#if ETH_JSCONSOLE || !ETH_TRUE
#include <libjsconsole/JSConsole.h>
#endif
#if ETH_READLINE || !ETH_TRUE
#include <readline/readline.h>
#include <readline/history.h>
@ -180,6 +183,9 @@ void help()
<< " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (default: 8)." << endl
<< " -V,--version Show the version and exit." << endl
<< " -h,--help Show this help message and exit." << endl
#if ETH_JSCONSOLE || !ETH_TRUE
<< " --console Use interactive javascript console" << endl
#endif
;
exit(0);
}
@ -407,6 +413,13 @@ void doFarm(MinerType _m, string const& _remote, unsigned _recheckPeriod)
exit(0);
}
void stopMiningAfterXBlocks(eth::Client* _c, unsigned _start, unsigned _mining)
{
if (_c->isMining() && _c->blockChain().details().number - _start == _mining)
_c->stopMining();
this_thread::sleep_for(chrono::milliseconds(100));
}
int main(int argc, char** argv)
{
#if 0
@ -543,6 +556,9 @@ int main(int argc, char** argv)
unsigned benchmarkTrial = 3;
unsigned benchmarkTrials = 5;
// javascript console
bool useConsole = false;
/// Farm params
string farmURL = "http://127.0.0.1:8080";
unsigned farmRecheckPeriod = 500;
@ -894,6 +910,10 @@ int main(int argc, char** argv)
jsonrpc = jsonrpc == -1 ? SensibleHttpPort : jsonrpc;
else if (arg == "--json-rpc-port" && i + 1 < argc)
jsonrpc = atoi(argv[++i]);
#endif
#if ETH_JSCONSOLE
else if (arg == "--console")
useConsole = true;
#endif
else if ((arg == "-v" || arg == "--verbosity") && i + 1 < argc)
g_logVerbosity = atoi(argv[++i]);
@ -1631,12 +1651,20 @@ int main(int argc, char** argv)
unsigned n =c->blockChain().details().number;
if (mining)
c->startMining();
while (!g_exit)
if (useConsole)
{
if ( c->isMining() &&c->blockChain().details().number - n == mining)
c->stopMining();
this_thread::sleep_for(chrono::milliseconds(100));
#if ETH_JSCONSOLE
JSConsole console(web3, vector<KeyPair>({sigKey}));
while (!g_exit)
{
console.repl();
stopMiningAfterXBlocks(c, n, mining);
}
#endif
}
else
while (!g_exit)
stopMiningAfterXBlocks(c, n, mining);
}
else
while (!g_exit)

30
libjsconsole/CMakeLists.txt

@ -0,0 +1,30 @@
cmake_policy(SET CMP0015 NEW)
# this policy was introduced in cmake 3.0
# remove if, once 3.0 will be used on unix
if (${CMAKE_MAJOR_VERSION} GREATER 2)
# old policy do not use MACOSX_RPATH
cmake_policy(SET CMP0042 OLD)
endif()
set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST)
include_directories(BEFORE ${V8_INCLUDE_DIRS})
include_directories(BEFORE ..)
include_directories(${READLINE_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
set(EXECUTABLE jsconsole)
file(GLOB HEADERS "*.h")
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
target_link_libraries(${EXECUTABLE} jsengine)
target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} ${READLINE_LIBRARIES})
target_link_libraries(${EXECUTABLE} web3jsonrpc)
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

86
libjsconsole/JSConsole.cpp

@ -0,0 +1,86 @@
/*
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 JSConsole.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#include <iostream>
#include <libdevcore/Log.h>
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include "JSConsole.h"
#include "JSV8Connector.h"
#include "libjsconsole/JSConsoleResources.hpp"
// TODO! make readline optional!
#include <readline/readline.h>
#include <readline/history.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
JSConsole::JSConsole(WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts):
m_engine(),
m_printer(m_engine)
{
m_jsonrpcConnector.reset(new JSV8Connector(m_engine));
m_jsonrpcServer.reset(new WebThreeStubServer(*m_jsonrpcConnector.get(), _web3, _accounts));
}
JSConsole::~JSConsole() {}
void JSConsole::repl() const
{
string cmd = "";
g_logPost = [](std::string const& a, char const*) { cout << "\r \r" << a << endl << flush; rl_forced_update_display(); };
bool isEmpty = true;
int openBrackets = 0;
do {
char* buff = readline(promptForIndentionLevel(openBrackets).c_str());
isEmpty = !(buff && *buff);
if (!isEmpty)
{
cmd += string(buff);
cmd += " ";
free(buff);
int open = count(cmd.begin(), cmd.end(), '{');
open += count(cmd.begin(), cmd.end(), '(');
int closed = count(cmd.begin(), cmd.end(), '}');
closed += count(cmd.begin(), cmd.end(), ')');
openBrackets = open - closed;
}
} while (openBrackets > 0);
if (!isEmpty)
{
add_history(cmd.c_str());
auto value = m_engine.eval(cmd.c_str());
string result = m_printer.prettyPrint(value).cstr();
cout << result << endl;
}
}
std::string JSConsole::promptForIndentionLevel(int _i) const
{
if (_i == 0)
return "> ";
return string((_i + 1) * 2, ' ');
}

53
libjsconsole/JSConsole.h

@ -0,0 +1,53 @@
/*
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 JSConsole.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#pragma once
#include <libjsengine/JSV8Engine.h>
#include <libjsengine/JSV8Printer.h>
class WebThreeStubServer;
namespace jsonrpc { class AbstractServerConnector; }
namespace dev
{
namespace eth
{
class JSConsole
{
public:
JSConsole(WebThreeDirect& _web3, std::vector<dev::KeyPair> const& _accounts);
~JSConsole();
void repl() const;
private:
std::string promptForIndentionLevel(int _i) const;
JSV8Engine m_engine;
JSV8Printer m_printer;
std::unique_ptr<WebThreeStubServer> m_jsonrpcServer;
std::unique_ptr<jsonrpc::AbstractServerConnector> m_jsonrpcConnector;
};
}
}

54
libjsconsole/JSV8Connector.cpp

@ -0,0 +1,54 @@
/*
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 JSV8Connector.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#include "JSV8Connector.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
bool JSV8Connector::StartListening()
{
return true;
}
bool JSV8Connector::StopListening()
{
return true;
}
bool JSV8Connector::SendResponse(std::string const& _response, void* _addInfo)
{
(void)_addInfo;
m_lastResponse = _response.c_str();
return true;
}
void JSV8Connector::onSend(char const* payload)
{
OnRequest(payload, NULL);
}
JSV8Connector::~JSV8Connector()
{
StopListening();
}

50
libjsconsole/JSV8Connector.h

@ -0,0 +1,50 @@
/*
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 JSV8Connector.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#pragma once
#include <jsonrpccpp/server/abstractserverconnector.h>
#include <libjsengine/JSV8RPC.h>
namespace dev
{
namespace eth
{
class JSV8Connector: public jsonrpc::AbstractServerConnector, public JSV8RPC
{
public:
JSV8Connector(JSV8Engine const& _engine): JSV8RPC(_engine) {}
virtual ~JSV8Connector();
// implement AbstractServerConnector interface
bool StartListening();
bool StopListening();
bool SendResponse(std::string const& _response, void* _addInfo = nullptr);
// implement JSV8RPC interface
void onSend(char const* payload);
};
}
}

36
libjsengine/CMakeLists.txt

@ -0,0 +1,36 @@
cmake_policy(SET CMP0015 NEW)
# this policy was introduced in cmake 3.0
# remove if, once 3.0 will be used on unix
if (${CMAKE_MAJOR_VERSION} GREATER 2)
# old policy do not use MACOSX_RPATH
cmake_policy(SET CMP0042 OLD)
endif()
set(CMAKE_AUTOMOC OFF)
aux_source_directory(. SRC_LIST)
include_directories(BEFORE ${V8_INCLUDE_DIRS})
include_directories(BEFORE ..)
set(EXECUTABLE jsengine)
file(GLOB HEADERS "*.h")
include(EthUtils)
eth_add_resources("${CMAKE_CURRENT_SOURCE_DIR}/JSResources.cmake" "JSRES")
message(STATUS "HERE!!! ${JSRES}")
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS} ${JSRES})
# macos brew version of v8 needs to be compiled with libstdc++
# it also needs to be dynamic library
# xcode needs libstdc++ to be explicitly set as it's attribute
if (APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libstdc++")
set_property(TARGET ${EXECUTABLE} PROPERTY XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libstdc++")
endif()
target_link_libraries(${EXECUTABLE} ${V8_LIBRARIES})
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )

11
libjsengine/Common.js

@ -0,0 +1,11 @@
console = {};
console.log = function () {
};
console.warn = function () {
};
console.error = function () {
};
setTimeout = function () {
};

36
libjsengine/JSEngine.cpp

@ -0,0 +1,36 @@
/*
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 JSEngine.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#include <string.h>
#include <stdlib.h>
#include "JSEngine.h"
using namespace dev;
using namespace dev::eth;
JSString::JSString(char const* _cstr): m_cstr(strdup(_cstr)) {}
JSString::~JSString()
{
if (m_cstr)
free(m_cstr);
}

60
libjsengine/JSEngine.h

@ -0,0 +1,60 @@
/*
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 JSEngine.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#pragma once
#include <exception>
namespace dev
{
namespace eth
{
class JSException: public std::exception {};
class JSPrintException: public JSException { char const* what() const noexcept { return "Cannot print expression!"; } };
class JSString
{
public:
JSString(char const* _cstr);
~JSString();
char const* cstr() const { return m_cstr; }
private:
char* m_cstr;
};
class JSValue
{
public:
virtual JSString toString() const = 0;
};
template <typename T>
class JSEngine
{
public:
// should be used to evalute javascript expression
virtual T eval(char const* _cstr) const = 0;
};
}
}

23
libjsengine/JSPrinter.cpp

@ -0,0 +1,23 @@
/*
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 JSPrinter.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#include "JSPrinter.h"

41
libjsengine/JSPrinter.h

@ -0,0 +1,41 @@
/*
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 JSPrinter.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#pragma once
#include "JSEngine.h"
namespace dev
{
namespace eth
{
template <typename T>
class JSPrinter
{
public:
virtual JSString print(T const& _value) const { return _value.toString(); }
virtual JSString prettyPrint(T const& _value) const { return print(_value); }
};
}
}

8
libjsengine/JSResources.cmake

@ -0,0 +1,8 @@
set(web3 "${CMAKE_CURRENT_LIST_DIR}/../libjsqrc/ethereumjs/dist/web3.js")
set(pretty_print "${CMAKE_CURRENT_LIST_DIR}/PrettyPrint.js")
set(common "${CMAKE_CURRENT_LIST_DIR}/Common.js")
set(ETH_RESOURCE_NAME "JSEngineResources")
set(ETH_RESOURCE_LOCATION "${CMAKE_CURRENT_BINARY_DIR}")
set(ETH_RESOURCES "web3" "pretty_print" "common")

187
libjsengine/JSV8Engine.cpp

@ -0,0 +1,187 @@
/*
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 JSV8Engine.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#include <memory>
#include "JSV8Engine.h"
#include "libjsengine/JSEngineResources.hpp"
using namespace std;
using namespace dev;
using namespace dev::eth;
namespace dev
{
namespace eth
{
static char const* toCString(v8::String::Utf8Value const& _value)
{
if (*_value)
return *_value;
throw JSPrintException();
}
// from: https://github.com/v8/v8-git-mirror/blob/master/samples/shell.cc
// v3.15 from: https://chromium.googlesource.com/v8/v8.git/+/3.14.5.9/samples/shell.cc
void reportException(v8::TryCatch* _tryCatch)
{
v8::HandleScope handle_scope;
v8::String::Utf8Value exception(_tryCatch->Exception());
char const* exceptionString = toCString(exception);
v8::Handle<v8::Message> message = _tryCatch->Message();
// V8 didn't provide any extra information about this error; just
// print the exception.
if (message.IsEmpty())
printf("%s\n", exceptionString);
else
{
// Print (filename):(line number): (message).
v8::String::Utf8Value filename(message->GetScriptResourceName());
char const* filenameString = toCString(filename);
int linenum = message->GetLineNumber();
printf("%s:%i: %s\n", filenameString, linenum, exceptionString);
// Print line of source code.
v8::String::Utf8Value sourceline(message->GetSourceLine());
char const* sourcelineString = toCString(sourceline);
printf("%s\n", sourcelineString);
// Print wavy underline (GetUnderline is deprecated).
int start = message->GetStartColumn();
for (int i = 0; i < start; i++)
printf(" ");
int end = message->GetEndColumn();
for (int i = start; i < end; i++)
printf("^");
printf("\n");
v8::String::Utf8Value stackTrace(_tryCatch->StackTrace());
if (stackTrace.length() > 0)
{
char const* stackTraceString = toCString(stackTrace);
printf("%s\n", stackTraceString);
}
}
}
class JSV8Env
{
public:
~JSV8Env()
{
v8::V8::Dispose();
}
};
class JSV8Scope
{
public:
JSV8Scope():
m_handleScope(),
m_context(v8::Context::New(NULL, v8::ObjectTemplate::New())),
m_contextScope(m_context)
{
m_context->Enter();
}
~JSV8Scope()
{
m_context->Exit();
m_context.Dispose();
}
v8::Persistent <v8::Context> const& context() const { return m_context; }
private:
v8::HandleScope m_handleScope;
v8::Persistent <v8::Context> m_context;
v8::Context::Scope m_contextScope;
};
}
}
JSV8Env JSV8Engine::s_env = JSV8Env();
JSString JSV8Value::toString() const
{
if (m_value.IsEmpty())
return "";
else if (m_value->IsUndefined())
return "undefined";
v8::String::Utf8Value str(m_value);
return toCString(str);
}
JSV8Engine::JSV8Engine(): m_scope(new JSV8Scope())
{
JSEngineResources resources;
string common = resources.loadResourceAsString("common");
string web3 = resources.loadResourceAsString("web3");
eval(common.c_str());
eval(web3.c_str());
eval("web3 = require('web3');");
}
JSV8Engine::~JSV8Engine()
{
delete m_scope;
}
JSV8Value JSV8Engine::eval(char const* _cstr) const
{
v8::HandleScope handleScope;
v8::TryCatch tryCatch;
v8::Local<v8::String> source = v8::String::New(_cstr);
v8::Local<v8::String> name(v8::String::New("(shell)"));
v8::ScriptOrigin origin(name);
v8::Handle<v8::Script> script = v8::Script::Compile(source, &origin);
// Make sure to wrap the exception in a new handle because
// the handle returned from the TryCatch is destroyed
if (script.IsEmpty())
{
reportException(&tryCatch);
return v8::Exception::Error(v8::Local<v8::String>::New(tryCatch.Message()->Get()));
}
auto result = script->Run();
if (result.IsEmpty())
{
reportException(&tryCatch);
return v8::Exception::Error(v8::Local<v8::String>::New(tryCatch.Message()->Get()));
}
return result;
}
v8::Handle<v8::Context> const& JSV8Engine::context() const
{
return m_scope->context();
}

61
libjsengine/JSV8Engine.h

@ -0,0 +1,61 @@
/*
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 JSV8Engine.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#pragma once
#include <v8.h>
#include "JSEngine.h"
namespace dev
{
namespace eth
{
class JSV8Env;
class JSV8Scope;
class JSV8Value: public JSValue
{
public:
JSV8Value(v8::Handle<v8::Value> _value): m_value(_value) {}
JSString toString() const;
v8::Handle<v8::Value> const& value() const { return m_value; }
private:
v8::Handle<v8::Value> m_value;
};
class JSV8Engine: public JSEngine<JSV8Value>
{
public:
JSV8Engine();
virtual ~JSV8Engine();
JSV8Value eval(char const* _cstr) const;
v8::Handle<v8::Context> const& context() const;
private:
static JSV8Env s_env;
JSV8Scope* m_scope;
};
}
}

49
libjsengine/JSV8Printer.cpp

@ -0,0 +1,49 @@
/*
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 JSV8Printer.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#include <string>
#include "JSV8Printer.h"
#include "libjsengine/JSEngineResources.hpp"
using namespace std;
using namespace dev;
using namespace eth;
JSV8Printer::JSV8Printer(JSV8Engine const& _engine): m_engine(_engine)
{
JSEngineResources resources;
string prettyPrint = resources.loadResourceAsString("pretty_print");
m_engine.eval(prettyPrint.c_str());
}
JSString JSV8Printer::prettyPrint(JSV8Value const& _value) const
{
v8::HandleScope handleScope;
v8::Local<v8::String> pp = v8::String::New("prettyPrint");
v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(m_engine.context()->Global()->Get(pp));
v8::Local<v8::Value> values[1] = {v8::Local<v8::Value>::New(_value.value())};
v8::Local<v8::Value> res = func->Call(func, 1, values);
v8::String::Utf8Value str(res);
if (*str)
return *str;
throw JSPrintException();
}

43
libjsengine/JSV8Printer.h

@ -0,0 +1,43 @@
/*
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 JSV8Printer.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#pragma once
#include "JSPrinter.h"
#include "JSV8Engine.h"
namespace dev
{
namespace eth
{
class JSV8Printer: public JSPrinter<JSV8Value>
{
public:
JSV8Printer(JSV8Engine const& _engine);
JSString prettyPrint(JSV8Value const& _value) const;
private:
JSV8Engine const& m_engine;
};
}
}

84
libjsengine/JSV8RPC.cpp

@ -0,0 +1,84 @@
/*
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 JSV8RPC.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#include "JSV8RPC.h"
using namespace dev;
using namespace dev::eth;
namespace dev
{
namespace eth
{
v8::Handle<v8::Value> JSV8RPCSend(v8::Arguments const& _args)
{
v8::Local<v8::String> JSON = v8::String::New("JSON");
v8::Local<v8::String> parse = v8::String::New("parse");
v8::Local<v8::String> stringify = v8::String::New("stringify");
v8::Handle<v8::Object> jsonObject = v8::Handle<v8::Object>::Cast(v8::Context::GetCurrent()->Global()->Get(JSON));
v8::Handle<v8::Function> parseFunc = v8::Handle<v8::Function>::Cast(jsonObject->Get(parse));
v8::Handle<v8::Function> stringifyFunc = v8::Handle<v8::Function>::Cast(jsonObject->Get(stringify));
v8::Local<v8::Object> self = _args.Holder();
v8::Local<v8::External> wrap = v8::Local<v8::External>::Cast(self->GetInternalField(0));
JSV8RPC* that = static_cast<JSV8RPC*>(wrap->Value());
v8::Local<v8::Value> vals[1] = {_args[0]->ToObject()};
v8::Local<v8::Value> stringifiedArg = stringifyFunc->Call(stringifyFunc, 1, vals);
v8::String::Utf8Value str(stringifiedArg);
that->onSend(*str);
v8::Local<v8::Value> values[1] = {v8::String::New(that->lastResponse())};
return parseFunc->Call(parseFunc, 1, values);
}
}
}
JSV8RPC::JSV8RPC(JSV8Engine const& _engine): m_engine(_engine)
{
v8::HandleScope scope;
v8::Local<v8::ObjectTemplate> rpcTemplate = v8::ObjectTemplate::New();
rpcTemplate->SetInternalFieldCount(1);
rpcTemplate->Set(v8::String::New("send"),
v8::FunctionTemplate::New(JSV8RPCSend));
rpcTemplate->Set(v8::String::New("sendAsync"),
v8::FunctionTemplate::New(JSV8RPCSend));
v8::Local<v8::Object> obj = rpcTemplate->NewInstance();
obj->SetInternalField(0, v8::External::New(this));
v8::Local<v8::String> web3 = v8::String::New("web3");
v8::Local<v8::String> setProvider = v8::String::New("setProvider");
v8::Handle<v8::Object> web3object = v8::Handle<v8::Object>::Cast(m_engine.context()->Global()->Get(web3));
v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(web3object->Get(setProvider));
v8::Local<v8::Value> values[1] = {obj};
func->Call(func, 1, values);
m_lastResponse = R"(
{
"id": 1,
"jsonrpc": "2.0",
"error": "Uninitalized JSV8RPC!"
}
)";
}

47
libjsengine/JSV8RPC.h

@ -0,0 +1,47 @@
/*
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 JSV8RPC.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2015
* Ethereum client.
*/
#pragma once
#include <libjsengine/JSV8Engine.h>
namespace dev
{
namespace eth
{
class JSV8RPC
{
public:
JSV8RPC(JSV8Engine const& _engine);
virtual void onSend(char const* _payload) = 0;
char const* lastResponse() const { return m_lastResponse; }
private:
JSV8Engine const& m_engine;
protected:
char const* m_lastResponse;
};
}
}

91
libjsengine/PrettyPrint.js

@ -0,0 +1,91 @@
var prettyPrint = (function () {
function pp(object, indent) {
try {
JSON.stringify(object)
} catch(e) {
return pp(e, indent);
}
var str = "";
if(object instanceof Array) {
str += "[";
for(var i = 0, l = object.length; i < l; i++) {
str += pp(object[i], indent);
if(i < l-1) {
str += ", ";
}
}
str += " ]";
} else if (object instanceof Error) {
str += "\033[31m" + "Error:\033[0m " + object.message;
} else if (object === null) {
str += "\033[1m\033[30m" + "null";
} else if(typeof(object) === "undefined") {
str += "\033[1m\033[30m" + object;
} else if (isBigNumber(object)) {
str += "\033[32m'" + object.toString(10) + "'";
} else if(typeof(object) === "object") {
str += "{\n";
indent += " ";
var last = getFields(object).pop()
getFields(object).forEach(function (k) {
str += indent + k + ": ";
try {
str += pp(object[k], indent);
} catch (e) {
str += pp(e, indent);
}
if(k !== last) {
str += ",";
}
str += "\n";
});
str += indent.substr(2, indent.length) + "}";
} else if(typeof(object) === "string") {
str += "\033[32m'" + object + "'";
} else if(typeof(object) === "number") {
str += "\033[31m" + object;
} else if(typeof(object) === "function") {
str += "\033[35m[Function]";
} else {
str += object;
}
str += "\033[0m";
return str;
}
var redundantFields = [
'valueOf',
'toString',
'toLocaleString',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor',
'__defineGetter__',
'__defineSetter__',
'__lookupGetter__',
'__lookupSetter__',
'__proto__'
];
var getFields = function (object) {
var result = Object.getOwnPropertyNames(object);
if (object.constructor && object.constructor.prototype) {
result = result.concat(Object.getOwnPropertyNames(object.constructor.prototype));
}
return result.filter(function (field) {
return redundantFields.indexOf(field) === -1;
});
};
var isBigNumber = function (object) {
return (!!object.constructor && object.constructor.name === 'BigNumber') ||
(typeof BigNumber !== 'undefined' && object instanceof BigNumber)
};
function prettyPrintInner(/* */) {
var args = arguments;
var ret = "";
for(var i = 0, l = args.length; i < l; i++) {
ret += pp(args[i], "") + "\n";
}
return ret;
};
return prettyPrintInner;
})();

17
test/CMakeLists.txt

@ -25,6 +25,11 @@ add_subdirectory(libethereum)
add_subdirectory(libevm)
add_subdirectory(libnatspec)
add_subdirectory(libp2p)
if (JSCONSOLE)
add_subdirectory(libjsengine)
endif()
if (SOLIDITY)
add_subdirectory(libsolidity)
endif ()
@ -41,6 +46,10 @@ include_directories(${Boost_INCLUDE_DIRS})
include_directories(${CRYPTOPP_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})
if (JSCONSOLE)
include_directories(${V8_INCLUDE_DIRS})
endif()
# search for test names and create ctest tests
enable_testing()
foreach(file ${SRC_LIST})
@ -65,14 +74,22 @@ target_link_libraries(testeth ${CURL_LIBRARIES})
target_link_libraries(testeth ethereum)
target_link_libraries(testeth ethcore)
target_link_libraries(testeth secp256k1)
if (JSCONSOLE)
target_link_libraries(testeth jsengine)
endif()
if (SOLIDITY)
target_link_libraries(testeth solidity)
endif ()
target_link_libraries(testeth testutils)
if (GUI AND NOT JUSTTESTS)
target_link_libraries(testeth webthree)
target_link_libraries(testeth natspec)
endif()
if (JSONRPC)
target_link_libraries(testeth web3jsonrpc)
target_link_libraries(testeth ${JSON_RPC_CPP_CLIENT_LIBRARIES})

5
test/libjsengine/CMakeLists.txt

@ -0,0 +1,5 @@
cmake_policy(SET CMP0015 NEW)
aux_source_directory(. SRCS)
add_sources(${SRCS})

71
test/libjsengine/JSV8Engine.cpp

@ -0,0 +1,71 @@
//
// Created by Marek Kotewicz on 27/04/15.
//
#include <boost/test/unit_test.hpp>
#include <libjsengine/JSV8Engine.h>
#include <libjsengine/JSV8Printer.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
BOOST_AUTO_TEST_SUITE(jsv8engine)
BOOST_AUTO_TEST_CASE(evalInteger)
{
JSV8Engine engine;
JSV8Printer printer(engine);
auto value = engine.eval("1 + 1");
string result = printer.print(value).cstr();
BOOST_CHECK_EQUAL(result, "2");
}
BOOST_AUTO_TEST_CASE(evalString)
{
JSV8Engine engine;
JSV8Printer printer(engine);
auto value = engine.eval("'hello ' + 'world'");
string result = printer.print(value).cstr();
BOOST_CHECK_EQUAL(result, "hello world");
}
BOOST_AUTO_TEST_CASE(evalEmpty)
{
JSV8Engine engine;
JSV8Printer printer(engine);
auto value = engine.eval("");
string result = printer.print(value).cstr();
BOOST_CHECK_EQUAL(result, "undefined");
}
BOOST_AUTO_TEST_CASE(evalAssignment)
{
JSV8Engine engine;
JSV8Printer printer(engine);
auto value = engine.eval("x = 5");
string result = printer.print(value).cstr();
BOOST_CHECK_EQUAL(result, "5");
}
BOOST_AUTO_TEST_CASE(evalIncorrectExpression)
{
JSV8Engine engine;
JSV8Printer printer(engine);
auto value = engine.eval("[");
string result = printer.print(value).cstr();
BOOST_CHECK_EQUAL(result, "Error: Uncaught SyntaxError: Unexpected end of input");
}
BOOST_AUTO_TEST_CASE(evalNull)
{
JSV8Engine engine;
JSV8Printer printer(engine);
auto value = engine.eval("null");
string result = printer.print(value).cstr();
string prettyResult = printer.prettyPrint(value).cstr();
BOOST_CHECK_EQUAL(result, "null");
BOOST_CHECK_EQUAL(prettyResult.find("null") != std::string::npos, true);
}
BOOST_AUTO_TEST_SUITE_END()
Loading…
Cancel
Save