Browse Source

Merge branch 'develop' into p2p

cl-refactor
subtly 10 years ago
parent
commit
c1cca6b12d
  1. 1
      .gitignore
  2. 4
      CMakeLists.txt
  3. 10
      alethzero/Main.ui
  4. 89
      alethzero/MainWin.cpp
  5. 1
      evmjit/.gitignore
  6. 24
      evmjit/CMakeLists.txt
  7. 2
      evmjit/libevmjit-cpp/Env.cpp
  8. 8
      evmjit/libevmjit/BasicBlock.h
  9. 11
      evmjit/libevmjit/CMakeLists.txt
  10. 14
      evmjit/libevmjit/Cache.cpp
  11. 29
      evmjit/libevmjit/Compiler.cpp
  12. 34
      evmjit/libevmjit/ExecutionEngine.cpp
  13. 11
      evmjit/libevmjit/Ext.cpp
  14. 2
      evmjit/libevmjit/Runtime.h
  15. 34
      evmjit/libevmjit/interface.cpp
  16. 24
      evmjit/libevmjit/interface.h
  17. 4
      libdevcore/CommonData.h
  18. 2
      libdevcore/Exceptions.h
  19. 11
      libethcore/Exceptions.cpp
  20. 3
      libethereum/Account.h
  21. 3
      libethereum/BlockChain.cpp
  22. 2
      libethereum/Client.cpp
  23. 4
      libethereum/Executive.cpp
  24. 2
      libethereum/State.cpp
  25. 10
      libethereum/Transaction.cpp
  26. 11
      libethereum/Transaction.h
  27. 6
      libethereum/TransactionQueue.cpp
  28. 2
      libevm/VM.cpp
  29. 2
      libjsqrc/bignumber.min.js
  30. 8
      libjsqrc/ethereumjs/README.md
  31. 7
      libjsqrc/ethereumjs/bower.json
  32. 900
      libjsqrc/ethereumjs/dist/ethereum.js
  33. 22
      libjsqrc/ethereumjs/dist/ethereum.js.map
  34. 2
      libjsqrc/ethereumjs/dist/ethereum.min.js
  35. 1
      libjsqrc/ethereumjs/example/balance.html
  36. 3
      libjsqrc/ethereumjs/example/contract.html
  37. 4
      libjsqrc/ethereumjs/gulpfile.js
  38. 5
      libjsqrc/ethereumjs/index.js
  39. 370
      libjsqrc/ethereumjs/lib/abi.js
  40. 18
      libjsqrc/ethereumjs/lib/autoprovider.js
  41. 29
      libjsqrc/ethereumjs/lib/contract.js
  42. 86
      libjsqrc/ethereumjs/lib/filter.js
  43. 32
      libjsqrc/ethereumjs/lib/httprpc.js
  44. 119
      libjsqrc/ethereumjs/lib/providermanager.js
  45. 12
      libjsqrc/ethereumjs/lib/qt.js
  46. 187
      libjsqrc/ethereumjs/lib/web3.js
  47. 24
      libjsqrc/ethereumjs/lib/websocket.js
  48. 5
      libjsqrc/ethereumjs/package.json
  49. 830
      libjsqrc/ethereumjs/test/abi.parsers.js
  50. 10
      libjsqrc/ethereumjs/test/db.methods.js
  51. 52
      libjsqrc/ethereumjs/test/eth.methods.js
  52. 2
      libjsqrc/ethereumjs/test/mocha.opts
  53. 12
      libjsqrc/ethereumjs/test/shh.methods.js
  54. 8
      libjsqrc/ethereumjs/test/utils.js
  55. 14
      libjsqrc/ethereumjs/test/web3.methods.js
  56. 1
      libjsqrc/js.qrc
  57. 1
      liblll/CodeFragment.cpp
  58. 1
      liblll/CodeFragment.h
  59. 1
      libqwebthree/QWebThree.h
  60. 68
      libsolidity/AST.cpp
  61. 64
      libsolidity/AST.h
  62. 1
      libsolidity/ASTForward.h
  63. 471
      libsolidity/ASTJsonConverter.cpp
  64. 135
      libsolidity/ASTJsonConverter.h
  65. 22
      libsolidity/AST_accept.h
  66. 53
      libsolidity/CallGraph.cpp
  67. 15
      libsolidity/CallGraph.h
  68. 122
      libsolidity/Compiler.cpp
  69. 18
      libsolidity/Compiler.h
  70. 11
      libsolidity/CompilerContext.cpp
  71. 4
      libsolidity/CompilerContext.h
  72. 47
      libsolidity/CompilerStack.cpp
  73. 4
      libsolidity/CompilerStack.h
  74. 5
      libsolidity/DeclarationContainer.h
  75. 130
      libsolidity/ExpressionCompiler.cpp
  76. 10
      libsolidity/ExpressionCompiler.h
  77. 12
      libsolidity/GlobalContext.cpp
  78. 2
      libsolidity/GlobalContext.h
  79. 10
      libsolidity/InterfaceHandler.cpp
  80. 116
      libsolidity/NameAndTypeResolver.cpp
  81. 19
      libsolidity/NameAndTypeResolver.h
  82. 41
      libsolidity/Parser.cpp
  83. 3
      libsolidity/Parser.h
  84. 5
      libsolidity/Token.h
  85. 59
      libsolidity/Types.cpp
  86. 21
      libsolidity/Types.h
  87. 5
      libsolidity/grammar.txt
  88. 32
      mix/AppContext.cpp
  89. 2
      mix/AppContext.h
  90. 2
      mix/AssemblyDebuggerControl.cpp
  91. 15
      mix/CMakeLists.txt
  92. 63
      mix/ClientModel.cpp
  93. 43
      mix/ClientModel.h
  94. 14
      mix/CodeModel.cpp
  95. 6
      mix/CodeModel.h
  96. 25
      mix/DebuggingStateWrapper.cpp
  97. 15
      mix/DebuggingStateWrapper.h
  98. 31
      mix/Exceptions.cpp
  99. 45
      mix/Exceptions.h
  100. 6
      mix/FileIo.cpp

1
.gitignore

@ -65,7 +65,6 @@ DerivedData
project.pbxproj
evmjit
doc/html
*.autosave
node_modules/

4
CMakeLists.txt

@ -124,9 +124,7 @@ include(EthExecutableHelper)
createBuildInfo()
if (EVMJIT)
# Workaround for Ubuntu broken LLVM package
link_directories(/usr/lib/llvm-3.5/lib)
set(EVMJIT_CPP TRUE) # include CPP-JIT connector
add_subdirectory(evmjit)
endif()

10
alethzero/Main.ui

@ -95,8 +95,8 @@
<widget class="QLineEdit" name="urlEdit"/>
</item>
<item>
<widget class="QWebView" name="webView" native="true">
<property name="url" stdset="0">
<widget class="QWebView" name="webView">
<property name="url">
<url>
<string>about:blank</string>
</url>
@ -1212,8 +1212,8 @@
<number>0</number>
</property>
<item>
<widget class="QWebView" name="jsConsole" native="true">
<property name="url" stdset="0">
<widget class="QWebView" name="jsConsole">
<property name="url">
<url>
<string>about:blank</string>
</url>
@ -2035,7 +2035,7 @@ font-size: 14pt</string>
</action>
<action name="importKeyFile">
<property name="enabled">
<bool>false</bool>
<bool>true</bool>
</property>
<property name="text">
<string>Claim Ether Presale &amp;Wallet...</string>

89
alethzero/MainWin.cpp

@ -60,6 +60,11 @@ using namespace dev::p2p;
using namespace dev::eth;
namespace js = json_spirit;
#define Small "font-size: small; "
#define Mono "font-family: Ubuntu Mono, Monospace, Lucida Console, Courier New; font-weight: bold; "
#define Div(S) "<div style=\"" S "\">"
#define Span(S) "<span style=\"" S "\">"
static void initUnits(QComboBox* _b)
{
for (auto n = (unsigned)units().size(); n-- != 0; )
@ -447,7 +452,7 @@ void Main::eval(QString const& _js)
else
s = "<span style=\"color: #888\">unknown type</span>";
m_consoleHistory.push_back(qMakePair(_js, s));
s = "<html><body style=\"font-family: Monospace, Ubuntu Mono, Lucida Console, Courier New; margin: 0; font-size: 12pt\"><div style=\"position: absolute; bottom: 0; border: 0px; margin: 0px; width: 100%\">";
s = "<html><body style=\"margin: 0;\">" Div(Mono "position: absolute; bottom: 0; border: 0px; margin: 0px; width: 100%");
for (auto const& i: m_consoleHistory)
s += "<div style=\"border-bottom: 1 solid #eee; width: 100%\"><span style=\"float: left; width: 1em; color: #888; font-weight: bold\">&gt;</span><span style=\"color: #35d\">" + i.first.toHtmlEscaped() + "</span></div>"
"<div style=\"border-bottom: 1 solid #eee; width: 100%\"><span style=\"float: left; width: 1em\">&nbsp;</span><span>" + i.second + "</span></div>";
@ -750,7 +755,7 @@ void Main::on_importKey_triggered()
void Main::on_importKeyFile_triggered()
{
QString s = QFileDialog::getOpenFileName(this, "Import Account", QDir::homePath(), "JSON Files (*.json);;All Files (*)");
QString s = QFileDialog::getOpenFileName(this, "Claim Account Contents", QDir::homePath(), "JSON Files (*.json);;All Files (*)");
try
{
js::mValue val;
@ -780,8 +785,12 @@ void Main::on_importKeyFile_triggered()
if (std::find(m_myKeys.begin(), m_myKeys.end(), k) == m_myKeys.end())
{
m_myKeys.append(k);
keysChanged();
if (m_myKeys.empty())
{
m_myKeys.push_back(KeyPair::create());
keysChanged();
}
ethereum()->transact(k.sec(), ethereum()->balanceAt(k.address()) - gasPrice() * c_txGas, m_myKeys.back().address(), {}, c_txGas, gasPrice());
}
else
QMessageBox::warning(this, "Already Have Key", "Could not import the secret key: we already own this account.");
@ -1087,7 +1096,7 @@ void Main::refreshBlockChain()
auto b = bc.block(h);
for (auto const& i: RLP(b)[1])
{
Transaction t(i.data());
Transaction t(i.data(), CheckSignature::Sender);
if (bm || transactionMatch(filter, t))
{
QString s = t.receiveAddress() ?
@ -1273,12 +1282,12 @@ void Main::on_transactionQueue_currentItemChanged()
if (tx.data().size())
s << dev::memDump(tx.data(), 16, true);
}
s << "<div>Hex: <span style=\"font-family: Monospace,Lucida Console,Courier,Courier New,sans-serif; font-size: small\">" << toHex(tx.rlp()) << "</span></div>";
s << "<div>Hex: " Span(Mono) << toHex(tx.rlp()) << "</span></div>";
s << "<hr/>";
s << "<div>Log Bloom: " << receipt.bloom() << "</div>";
auto r = receipt.rlp();
s << "<div>Receipt: " << toString(RLP(r)) << "</div>";
s << "<div>Receipt-Hex: <span style=\"font-family: Monospace,Lucida Console,Courier,Courier New,sans-serif; font-size: small\">" << toHex(receipt.rlp()) << "</span></div>";
s << "<div>Receipt-Hex: " Span(Mono) << toHex(receipt.rlp()) << "</span></div>";
s << renderDiff(ethereum()->diff(i, 0));
// s << "Pre: " << fs.rootHash() << "<br/>";
// s << "Post: <b>" << ts.rootHash() << "</b>";
@ -1369,13 +1378,13 @@ void Main::on_blocks_currentItemChanged()
for (auto const& i: block[1])
s << "<br/>" << sha3(i.data()).abridged();// << ": <b>" << i[1].toHash<h256>() << "</b> [<b>" << i[2].toInt<u256>() << "</b> used]";
s << "<br/>Post: <b>" << info.stateRoot << "</b>";
s << "<br/>Dump: <span style=\"font-family: Monospace,Lucida Console,Courier,Courier New,sans-serif; font-size: small\">" << toHex(block[0].data()) << "</span>";
s << "<div>Receipts-Hex: <span style=\"font-family: Monospace,Lucida Console,Courier,Courier New,sans-serif; font-size: small\">" << toHex(ethereum()->blockChain().receipts(h).rlp()) << "</span></div>";
s << "<br/>Dump: " Span(Mono) << toHex(block[0].data()) << "</span>";
s << "<div>Receipts-Hex: " Span(Mono) << toHex(ethereum()->blockChain().receipts(h).rlp()) << "</span></div>";
}
else
{
unsigned txi = item->data(Qt::UserRole + 1).toInt();
Transaction tx(block[1][txi].data());
Transaction tx(block[1][txi].data(), CheckSignature::Sender);
auto ss = tx.safeSender();
h256 th = sha3(rlpList(ss, tx.nonce()));
TransactionReceipt receipt = ethereum()->blockChain().receipts(h).receipts[txi];
@ -1404,12 +1413,12 @@ void Main::on_blocks_currentItemChanged()
if (tx.data().size())
s << dev::memDump(tx.data(), 16, true);
}
s << "<div>Hex: <span style=\"font-family: Monospace,Lucida Console,Courier,Courier New,sans-serif; font-size: small\">" << toHex(block[1][txi].data()) << "</span></div>";
s << "<div>Hex: " Span(Mono) << toHex(block[1][txi].data()) << "</span></div>";
s << "<hr/>";
s << "<div>Log Bloom: " << receipt.bloom() << "</div>";
auto r = receipt.rlp();
s << "<div>Receipt: " << toString(RLP(r)) << "</div>";
s << "<div>Receipt-Hex: <span style=\"font-family: Monospace,Lucida Console,Courier,Courier New,sans-serif; font-size: small\">" << toHex(receipt.rlp()) << "</span></div>";
s << "<div>Receipt-Hex: " Span(Mono) << toHex(receipt.rlp()) << "</span></div>";
s << renderDiff(ethereum()->diff(txi, h));
ui->debugCurrent->setEnabled(true);
ui->debugDumpState->setEnabled(true);
@ -1528,6 +1537,7 @@ void Main::on_contracts_currentItemChanged()
for (auto const& i: storage)
s << "@" << showbase << hex << prettyU256(i.first).toStdString() << "&nbsp;&nbsp;&nbsp;&nbsp;" << showbase << hex << prettyU256(i.second).toStdString() << "<br/>";
s << "<h4>Body Code</h4>" << disassemble(ethereum()->codeAt(address));
s << Div(Mono) << toHex(ethereum()->codeAt(address)) << "</div>";
ui->contractInfo->appendHtml(QString::fromStdString(s.str()));
}
catch (dev::InvalidTrie)
@ -1637,13 +1647,18 @@ static shh::Topic topicFromText(QString _s)
return ret;
}
bool Main::sourceIsSolidity(string const& _source)
{
// TODO: Improve this heuristic
return (_source.substr(0, 8) == "contract" || _source.substr(0, 5) == "//sol");
}
static bool sourceIsSerpent(string const& _source)
{
// TODO: Improve this heuristic
return (_source.substr(0, 5) == "//ser");
}
string const Main::getFunctionHashes(dev::solidity::CompilerStack const &_compiler,
string const& _contractName)
{
@ -1678,6 +1693,7 @@ void Main::on_data_textChanged()
dev::solidity::CompilerStack compiler;
try
{
// compiler.addSources(dev::solidity::StandardSources);
m_data = compiler.compile(src, m_enableOptimizer);
solidity = "<h4>Solidity</h4>";
solidity += "<pre>" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped() + "</pre>";
@ -1695,23 +1711,23 @@ void Main::on_data_textChanged()
solidity = "<h4>Solidity</h4><pre>Uncaught exception.</pre>";
}
}
else
else if (sourceIsSerpent(src))
{
m_data = compileLLL(src, m_enableOptimizer, &errors);
if (errors.size())
try
{
try
{
m_data = dev::asBytes(::compile(src));
for (auto& i: errors)
i = "(LLL " + i + ")";
}
catch (string err)
{
errors.push_back("Serpent " + err);
}
m_data = dev::asBytes(::compile(src));
for (auto& i: errors)
i = "(LLL " + i + ")";
}
else
catch (string err)
{
errors.push_back("Serpent " + err);
}
}
else
{
m_data = compileLLL(src, m_enableOptimizer, &errors);
if (errors.empty())
{
auto asmcode = compileLLLToAsm(src, false);
lll = "<h4>Pre</h4><pre>" + QString::fromStdString(asmcode).toHtmlEscaped() + "</pre>";
@ -1729,7 +1745,7 @@ void Main::on_data_textChanged()
for (auto const& i: errors)
errs.append("<div style=\"border-left: 6px solid #c00; margin-top: 2px\">" + QString::fromStdString(i).toHtmlEscaped() + "</div>");
}
ui->code->setHtml(errs + lll + solidity + "<h4>Code</h4>" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped());
ui->code->setHtml(errs + lll + solidity + "<h4>Code</h4>" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped() + "<h4>Hex</h4>" Div(Mono) + QString::fromStdString(toHex(m_data)) + "</div>");
ui->gas->setMinimum((qint64)Client::txGas(m_data, 0));
if (!ui->gas->isEnabled())
ui->gas->setValue(m_backupGas);
@ -1962,9 +1978,24 @@ void Main::on_debug_clicked()
}
}
bool beginsWith(Address _a, bytes const& _b)
{
for (unsigned i = 0; i < min<unsigned>(20, _b.size()); ++i)
if (_a[i] != _b[i])
return false;
return true;
}
void Main::on_create_triggered()
{
m_myKeys.append(KeyPair::create());
bool ok = true;
QString s = QInputDialog::getText(this, "Special Beginning?", "If you want a special key, enter some hex digits that it should begin with.\nNOTE: The more you enter, the longer generation will take.", QLineEdit::Normal, QString(), &ok);
if (!ok)
return;
KeyPair p;
while (!beginsWith(p.address(), asBytes(s.toStdString())))
p = KeyPair::create();
m_myKeys.append(p);
keysChanged();
}

1
evmjit/.gitignore

@ -0,0 +1 @@
/build/

24
evmjit/CMakeLists.txt

@ -4,8 +4,18 @@ project(evmjit)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
else()
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wextra -fPIC")
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
# Do not allow unresovled symbols in shared library (default on linux)
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined")
endif()
# LLVM
if(LLVM_DIR) # local LLVM build
if(LLVM_DIR OR APPLE) # local LLVM build
find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
@ -18,11 +28,19 @@ else()
execute_process(COMMAND llvm-config-3.5 --includedir OUTPUT_VARIABLE LLVM_INCLUDE_DIRS)
message(STATUS "LLVM include dirs: ${LLVM_INCLUDE_DIRS}")
set(LLVM_LIBS "-lLLVMBitWriter -lLLVMX86CodeGen -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMCodeGen -lLLVMScalarOpts -lLLVMInstCombine -lLLVMTransformUtils -lLLVMipa -lLLVMAnalysis -lLLVMX86AsmParser -lLLVMX86Desc -lLLVMX86Info -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMMCJIT -lLLVMTarget -lLLVMRuntimeDyld -lLLVMObject -lLLVMMCParser -lLLVMBitReader -lLLVMExecutionEngine -lLLVMMC -lLLVMCore -lLLVMSupport -lz -lpthread -lffi -ltinfo -ldl -lm")
add_definitions(-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS)
link_directories(/usr/lib/llvm-3.5/lib)
endif()
# Boost
find_package(Boost REQUIRED)
add_subdirectory(libevmjit)
add_subdirectory(libevmjit-cpp)
add_subdirectory(evmcc)
if(EVMJIT_CPP)
add_subdirectory(libevmjit-cpp)
endif()
if(EVMJIT_TOOLS)
add_subdirectory(evmcc)
endif()

2
evmjit/libevmjit-cpp/Env.cpp

@ -89,7 +89,7 @@ extern "C"
*o_hash = hash;
}
EXPORT byte const* env_getExtCode(ExtVMFace* _env, h256* _addr256, uint64_t* o_size)
EXPORT byte const* env_extcode(ExtVMFace* _env, h256* _addr256, uint64_t* o_size)
{
auto addr = right160(*_addr256);
auto& code = _env->codeAt(addr);

8
evmjit/libevmjit/BasicBlock.h

@ -57,7 +57,7 @@ public:
explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest);
BasicBlock(const BasicBlock&) = delete;
void operator=(const BasicBlock&) = delete;
BasicBlock& operator=(const BasicBlock&) = delete;
llvm::BasicBlock* llvm() { return m_llvmBB; }
@ -66,6 +66,9 @@ public:
bool isJumpDest() const { return m_isJumpDest; }
llvm::Value* getJumpTarget() const { return m_jumpTarget; }
void setJumpTarget(llvm::Value* _jumpTarget) { m_jumpTarget = _jumpTarget; }
LocalStack& localStack() { return m_stack; }
/// Optimization: propagates values between local stacks in basic blocks
@ -112,6 +115,9 @@ private:
/// Is the basic block a valid jump destination.
/// JUMPDEST is the first instruction of the basic block.
bool const m_isJumpDest = false;
/// If block finishes with dynamic jump target index is stored here
llvm::Value* m_jumpTarget = nullptr;
};
}

11
evmjit/libevmjit/CMakeLists.txt

@ -2,23 +2,24 @@ set(TARGET_NAME evmjit)
file(GLOB SOURCES "*.cpp")
file(GLOB HEADERS "*.h")
set(INTERFACE_HEADERS interface.h)
source_group("" FILES ${HEADERS})
source_group("" FILES ${SOURCES})
if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Disable rtti for Cache as LLVM has no rtti
set_source_files_properties(Cache.cpp PROPERTIES COMPILE_FLAGS -fno-rtti)
endif ()
endif()
add_library(${TARGET_NAME} ${SOURCES} ${HEADERS})
add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS})
set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs")
include_directories(${LLVM_INCLUDE_DIRS})
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${LLVM_LIBS})
target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS})
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
#install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
#install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )
install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib)
install(FILES ${INTERFACE_HEADERS} DESTINATION include/${TARGET_NAME})

14
evmjit/libevmjit/Cache.cpp

@ -36,6 +36,20 @@ std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
llvm::sys::path::system_temp_directory(false, cachePath);
llvm::sys::path::append(cachePath, "evm_objs", id);
#if defined(__GNUC__) && !defined(NDEBUG)
llvm::sys::fs::file_status st;
auto err = llvm::sys::fs::status(cachePath.str(), st);
if (err)
return nullptr;
auto mtime = st.getLastModificationTime().toEpochTime();
std::tm tm;
strptime(__DATE__ __TIME__, " %b %d %Y %H:%M:%S", &tm);
auto btime = (uint64_t)std::mktime(&tm);
if (btime > mtime)
return nullptr;
#endif
if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false))
lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer());
else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory))

29
evmjit/libevmjit/Compiler.cpp

@ -100,7 +100,7 @@ llvm::BasicBlock* Compiler::getJumpTableBlock()
m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, m_builder, true));
InsertPointGuard g{m_builder};
m_builder.SetInsertPoint(m_jumpTableBlock->llvm());
auto dest = m_jumpTableBlock->localStack().pop();
auto dest = m_builder.CreatePHI(Type::Word, 8, "target");
auto switchInstr = m_builder.CreateSwitch(dest, getBadJumpBlock());
for (auto&& p : m_basicBlocks)
{
@ -165,6 +165,26 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::str
removeDeadBlocks();
// Link jump table target index
if (m_jumpTableBlock)
{
auto phi = llvm::cast<llvm::PHINode>(&m_jumpTableBlock->llvm()->getInstList().front());
for (auto predIt = llvm::pred_begin(m_jumpTableBlock->llvm()); predIt != llvm::pred_end(m_jumpTableBlock->llvm()); ++predIt)
{
BasicBlock* pred = nullptr;
for (auto&& p : m_basicBlocks)
{
if (p.second.llvm() == *predIt)
{
pred = &p.second;
break;
}
}
phi->addIncoming(pred->getJumpTarget(), pred->llvm());
}
}
dumpCFGifRequired("blocks-init.dot");
if (m_options.optimizeStack)
@ -394,7 +414,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
value = Endianness::toBE(m_builder, value);
auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes");
auto byte = m_builder.CreateExtractElement(bytes, byteNum, "byte");
auto safeByteNum = m_builder.CreateZExt(m_builder.CreateTrunc(byteNum, m_builder.getIntNTy(5)), Type::lowPrecision); // Trim index, large values can crash
auto byte = m_builder.CreateExtractElement(bytes, safeByteNum, "byte");
value = m_builder.CreateZExt(byte, Type::Word);
auto byteNumValid = m_builder.CreateICmpULT(byteNum, Constant::get(32));
@ -564,7 +585,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
}
else
{
stack.push(target);
_basicBlock.setJumpTarget(target);
m_builder.CreateBr(getJumpTableBlock());
}
}
@ -580,7 +601,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
}
else
{
stack.push(target);
_basicBlock.setJumpTarget(target);
m_builder.CreateCondBr(cond, getJumpTableBlock(), _nextBasicBlock);
}
}

34
evmjit/libevmjit/ExecutionEngine.cpp

@ -19,7 +19,12 @@
#include "Compiler.h"
#include "Cache.h"
extern "C" void env_sha3(dev::eth::jit::byte const* _begin, uint64_t _size, std::array<dev::eth::jit::byte, 32>* o_hash);
#if defined(NDEBUG)
#define DEBUG_ENV_OPTION(name) false
#else
#include <cstdlib>
#define DEBUG_ENV_OPTION(name) (std::getenv(#name) != nullptr)
#endif
namespace dev
{
@ -49,14 +54,17 @@ ReturnCode runEntryFunc(EntryFuncPtr _mainFunc, Runtime* _runtime)
std::string codeHash(bytes const& _code)
{
std::array<dev::eth::jit::byte, 32> binHash;
env_sha3(_code.data(), _code.size(), &binHash);
std::ostringstream os;
for (auto i: binHash)
os << std::hex << std::setfill('0') << std::setw(2) << (int)(std::make_unsigned<decltype(i)>::type)i;
return os.str();
uint32_t hash = 0;
for (auto b : _code)
{
hash += b;
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return std::to_string(hash);
}
}
@ -64,6 +72,8 @@ std::string codeHash(bytes const& _code)
ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _env)
{
static std::unique_ptr<llvm::ExecutionEngine> ee; // TODO: Use Managed Objects from LLVM?
static auto debugDumpModule = DEBUG_ENV_OPTION(EVMJIT_DUMP_MODULE);
static bool objectCacheEnabled = !DEBUG_ENV_OPTION(EVMJIT_CACHE_OFF);
auto mainFuncName = codeHash(_code);
EntryFuncPtr entryFuncPtr{};
@ -74,14 +84,14 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en
}
else
{
bool objectCacheEnabled = true;
auto objectCache = objectCacheEnabled ? Cache::getObjectCache() : nullptr;
std::unique_ptr<llvm::Module> module;
if (objectCache)
module = Cache::getObject(mainFuncName);
if (!module)
module = Compiler({}).compile(_code, mainFuncName);
//module->dump();
if (debugDumpModule)
module->dump();
if (!ee)
{
llvm::InitializeNativeTarget();
@ -92,7 +102,7 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en
builder.setUseMCJIT(true);
std::unique_ptr<llvm::SectionMemoryManager> memoryManager(new llvm::SectionMemoryManager);
builder.setMCJITMemoryManager(memoryManager.get());
builder.setOptLevel(llvm::CodeGenOpt::Default);
builder.setOptLevel(llvm::CodeGenOpt::None);
auto triple = llvm::Triple(llvm::sys::getProcessTriple());
if (triple.getOS() == llvm::Triple::OSType::Win32)

11
evmjit/libevmjit/Ext.cpp

@ -5,9 +5,6 @@
#include <llvm/IR/TypeBuilder.h>
#include <llvm/IR/IntrinsicInst.h>
//#include <libdevcrypto/SHA3.h>
//#include <libevm/FeeStructure.h>
#include "RuntimeManager.h"
#include "Memory.h"
#include "Type.h"
@ -23,12 +20,9 @@ namespace jit
Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan):
RuntimeHelper(_runtimeManager),
m_memoryMan(_memoryMan)
#ifdef __MSCVER
,
m_funcs({}), // The only std::array initialization that works in both Visual Studio & GCC
m_argAllocas({})
#endif
{
m_funcs = decltype(m_funcs)();
m_argAllocas = decltype(m_argAllocas)();
m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size");
}
@ -74,7 +68,6 @@ llvm::Value* Ext::getArgAlloca()
getBuilder().SetInsertPoint(getMainFunction()->front().getFirstNonPHI());
a = getBuilder().CreateAlloca(Type::Word, nullptr, "arg");
}
return a;
}

2
evmjit/libevmjit/Runtime.h

@ -34,7 +34,7 @@ public:
Runtime(RuntimeData* _data, Env* _env);
Runtime(const Runtime&) = delete;
void operator=(const Runtime&) = delete;
Runtime& operator=(const Runtime&) = delete;
StackImpl& getStack() { return m_stack; }
MemoryImpl& getMemory() { return m_memory; }

34
evmjit/libevmjit/interface.cpp

@ -0,0 +1,34 @@
#include "interface.h"
#include <cstdio>
#include "ExecutionEngine.h"
extern "C"
{
evmjit_result evmjit_run(void* _data, void* _env)
{
using namespace dev::eth::jit;
auto data = static_cast<RuntimeData*>(_data);
ExecutionEngine engine;
auto codePtr = data->code;
auto codeSize = data->elems[RuntimeData::CodeSize].a;
bytes bytecode;
bytecode.insert(bytecode.end(), codePtr, codePtr + codeSize);
auto returnCode = engine.run(bytecode, data, static_cast<Env*>(_env));
evmjit_result result = {static_cast<int32_t>(returnCode), 0, nullptr};
if (returnCode == ReturnCode::Return && !engine.returnData.empty())
{
// TODO: Optimized returning data. Allocating memory on client side by callback function might be a good idea
result.returnDataSize = engine.returnData.size();
result.returnData = std::malloc(result.returnDataSize);
std::memcpy(result.returnData, engine.returnData.data(), result.returnDataSize);
}
return result;
}
}

24
evmjit/libevmjit/interface.c → evmjit/libevmjit/interface.h

@ -1,5 +1,20 @@
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct evmjit_result
{
int32_t returnCode;
uint64_t returnDataSize;
void* returnData;
} evmjit_result;
evmjit_result evmjit_run(void* _data, void* _env);
// JIT object opaque type
typedef struct evm_jit evm_jit;
@ -9,6 +24,11 @@ typedef int evm_jit_return_code;
// Host-endian 256-bit integer type
typedef struct i256 i256;
struct i256
{
char b[33];
};
// Big-endian right aligned 256-bit hash
typedef struct h256 h256;
@ -28,3 +48,7 @@ evm_jit_return_code evm_jit_execute(evm_jit* _jit);
void evm_jit_get_return_data(evm_jit* _jit, char* _return_data_offset, size_t* _return_data_size);
void evm_jit_destroy(evm_jit* _jit);
#ifdef __cplusplus
}
#endif

4
libdevcore/CommonData.h

@ -124,6 +124,10 @@ inline bytes toCompactBigEndian(_T _val, unsigned _min = 0)
toBigEndian(_val, ret);
return ret;
}
inline bytes toCompactBigEndian(byte _val, unsigned _min = 0)
{
return (_min || _val) ? bytes{ _val } : bytes{};
}
/// Convenience function for toBigEndian.
/// @returns a string just big enough to represent @a _val.

2
libdevcore/Exceptions.h

@ -31,7 +31,7 @@
namespace dev
{
// base class for all exceptions
struct Exception: virtual std::exception, virtual boost::exception {};
struct Exception: virtual std::exception, virtual boost::exception { mutable std::string m_message; };
struct BadHexCharacter: virtual Exception {};
struct RLPException: virtual Exception {};

11
libethcore/Exceptions.cpp

@ -27,14 +27,13 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
#if ALL_COMPILERS_ARE_CPP11_COMPLIANT
#if ALL_COMPILERS_ARE_CPP11
#define ETH_RETURN_STRING(S) thread_local static string s_what; s_what = S; return s_what.c_str();
#elsif USE_BOOST_TLS
static boost::thread_specific_ptr<string> g_exceptionMessage;
#define ETH_RETURN_STRING(S) if (!g_exceptionMessage.get()); g_exceptionMessage.reset(new string); *g_exceptionMessage.get() = S; return g_exceptionMessage.get()->c_str();
#else
#define ETH_RETURN_STRING(S) \
static boost::thread_specific_ptr<string> s_what; \
if (!s_what.get()) \
s_what.reset(new string); \
*s_what = S; return s_what->c_str();
#define ETH_RETURN_STRING(S) m_message = S; return m_message.c_str();
#endif
const char* InvalidBlockFormat::what() const noexcept { ETH_RETURN_STRING("Invalid block format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"); }

3
libethereum/Account.h

@ -140,7 +140,8 @@ public:
h256 codeHash() const { assert(!isFreshCode()); return m_codeHash; }
/// Sets the code of the account. Must only be called when isFreshCode() returns true.
void setCode(bytesConstRef _code) { assert(isFreshCode()); m_codeCache = _code.toBytes(); }
void setCode(bytes&& _code) { assert(isFreshCode()); m_codeCache = _code; }
void setCode(bytes const& _code) { assert(isFreshCode()); m_codeCache = _code; }
/// @returns true if the account's code is available through code().
bool codeCacheValid() const { return m_codeHash == EmptySHA3 || m_codeHash == c_contractConceptionCodeHash || m_codeCache.size(); }

3
libethereum/BlockChain.cpp

@ -28,6 +28,7 @@
#include <libethcore/Exceptions.h>
#include <libethcore/ProofOfWork.h>
#include <libethcore/BlockInfo.h>
#include <liblll/Compiler.h>
#include "State.h"
#include "Defaults.h"
using namespace std;
@ -56,6 +57,7 @@ std::map<Address, Account> const& dev::eth::genesisState()
{
static std::map<Address, Account> s_ret;
if (s_ret.empty())
{
// Initialise.
for (auto i: vector<string>({
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
@ -68,6 +70,7 @@ std::map<Address, Account> const& dev::eth::genesisState()
"e4157b34ea9615cfbde6b4fda419828124b70c78"
}))
s_ret[Address(fromHex(i))] = Account(u256(1) << 200, Account::NormalCreation);
}
return s_ret;
}

2
libethereum/Client.cpp

@ -642,7 +642,7 @@ Transaction Client::transaction(h256 _blockHash, unsigned _i) const
{
auto bl = m_bc.block(_blockHash);
RLP b(bl);
return Transaction(b[1][_i].data());
return Transaction(b[1][_i].data(), CheckSignature::Range);
}
BlockInfo Client::uncle(h256 _blockHash, unsigned _i) const

4
libethereum/Executive.cpp

@ -53,7 +53,7 @@ void Executive::accrueSubState(SubState& _parentContext)
bool Executive::setup(bytesConstRef _rlp)
{
// Entry point for a user-executed transaction.
m_t = Transaction(_rlp);
m_t = Transaction(_rlp, CheckSignature::Sender);
// Avoid invalid transactions.
auto nonceReq = m_s.transactionsFrom(m_t.sender());
@ -197,7 +197,7 @@ bool Executive::go(OnOpFunc const& _onOp)
m_endGas -= m_out.size() * c_createDataGas;
else
m_out.reset();
m_s.m_cache[m_newAddress].setCode(m_out);
m_s.m_cache[m_newAddress].setCode(m_out.toBytes());
}
}
catch (StepsDone const&)

2
libethereum/State.cpp

@ -395,7 +395,7 @@ bool State::cull(TransactionQueue& _tq) const
{
try
{
Transaction t(i.second);
Transaction t(i.second, CheckSignature::Sender);
if (t.nonce() <= transactionsFrom(t.sender()))
{
_tq.drop(i.first);

10
libethereum/Transaction.cpp

@ -30,7 +30,7 @@ using namespace dev::eth;
#define ETH_ADDRESS_DEBUG 0
Transaction::Transaction(bytesConstRef _rlpData, bool _checkSender)
Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig)
{
int field = 0;
RLP rlp(_rlpData);
@ -46,8 +46,14 @@ Transaction::Transaction(bytesConstRef _rlpData, bool _checkSender)
byte v = rlp[field = 6].toInt<byte>() - 27;
h256 r = rlp[field = 7].toInt<u256>();
h256 s = rlp[field = 8].toInt<u256>();
if (rlp.itemCount() > 9)
BOOST_THROW_EXCEPTION(BadRLP() << errinfo_comment("to many fields in the transaction RLP"));
m_vrs = SignatureStruct{ r, s, v };
if (_checkSender)
if (_checkSig >= CheckSignature::Range && !m_vrs.isValid())
BOOST_THROW_EXCEPTION(InvalidSignature());
if (_checkSig == CheckSignature::Sender)
m_sender = sender();
}
catch (Exception& _e)

11
libethereum/Transaction.h

@ -37,6 +37,13 @@ enum IncludeSignature
WithSignature = 1, ///< Do include a signature.
};
enum class CheckSignature
{
None,
Range,
Sender
};
/// Encodes a transaction, ready to be exported to or freshly imported from RLP.
class Transaction
{
@ -57,10 +64,10 @@ public:
Transaction(u256 _value, u256 _gasPrice, u256 _gas, bytes const& _data): m_type(ContractCreation), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {}
/// Constructs a transaction from the given RLP.
explicit Transaction(bytesConstRef _rlp, bool _checkSender = false);
explicit Transaction(bytesConstRef _rlp, CheckSignature _checkSig);
/// Constructs a transaction from the given RLP.
explicit Transaction(bytes const& _rlp, bool _checkSender = false): Transaction(&_rlp, _checkSender) {}
explicit Transaction(bytes const& _rlp, CheckSignature _checkSig): Transaction(&_rlp, _checkSig) {}
/// Checks equality of transactions.

6
libethereum/TransactionQueue.cpp

@ -42,7 +42,7 @@ bool TransactionQueue::import(bytesConstRef _transactionRLP)
// Check validity of _transactionRLP as a transaction. To do this we just deserialise and attempt to determine the sender.
// If it doesn't work, the signature is bad.
// The transaction's nonce may yet be invalid (or, it could be "valid" but we may be missing a marginally older transaction).
Transaction t(_transactionRLP, true);
Transaction t(_transactionRLP, CheckSignature::Sender);
UpgradeGuard ul(l);
// If valid, append to blocks.
@ -69,14 +69,14 @@ void TransactionQueue::setFuture(std::pair<h256, bytes> const& _t)
if (m_current.count(_t.first))
{
m_current.erase(_t.first);
m_unknown.insert(make_pair(Transaction(_t.second).sender(), _t));
m_unknown.insert(make_pair(Transaction(_t.second, CheckSignature::Sender).sender(), _t));
}
}
void TransactionQueue::noteGood(std::pair<h256, bytes> const& _t)
{
WriteGuard l(m_lock);
auto r = m_unknown.equal_range(Transaction(_t.second).sender());
auto r = m_unknown.equal_range(Transaction(_t.second, CheckSignature::Sender).sender());
for (auto it = r.first; it != r.second; ++it)
m_current.insert(it->second);
m_unknown.erase(r.first, r.second);

2
libevm/VM.cpp

@ -38,10 +38,12 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
if (m_jumpDests.empty())
for (unsigned i = 0; i < _ext.code.size(); ++i)
{
if (_ext.code[i] == (byte)Instruction::JUMPDEST)
m_jumpDests.insert(i);
else if (_ext.code[i] >= (byte)Instruction::PUSH1 && _ext.code[i] <= (byte)Instruction::PUSH32)
i += _ext.code[i] - (unsigned)Instruction::PUSH1 + 1;
}
u256 nextPC = m_curPC + 1;
auto osteps = _steps;
for (bool stopped = false; !stopped && _steps--; m_curPC = nextPC, nextPC = m_curPC + 1)

2
libjsqrc/bignumber.min.js

File diff suppressed because one or more lines are too long

8
libjsqrc/ethereumjs/README.md

@ -66,16 +66,16 @@ sudo apt-get install npm
sudo apt-get install nodejs-legacy
```
## Building
### Building (gulp)
```bash (gulp)
```bash
npm run-script build
```
### Testing
### Testing (mocha)
```bash (mocha)
```bash
npm test
```

7
libjsqrc/ethereumjs/bower.json

@ -1,11 +1,12 @@
{
"name": "ethereum.js",
"namespace": "ethereum",
"version": "0.0.3",
"version": "0.0.8",
"description": "Ethereum Compatible JavaScript API",
"main": ["./dist/ethereum.js", "./dist/ethereum.min.js"],
"dependencies": {
"es6-promise": "#master"
"es6-promise": "#master",
"bignumber.js": ">=2.0.0"
},
"repository": {
"type": "git",
@ -48,4 +49,4 @@
"index.js",
"**/*.txt"
]
}
}

900
libjsqrc/ethereumjs/dist/ethereum.js

File diff suppressed because it is too large

22
libjsqrc/ethereumjs/dist/ethereum.js.map

File diff suppressed because one or more lines are too long

2
libjsqrc/ethereumjs/dist/ethereum.min.js

File diff suppressed because one or more lines are too long

1
libjsqrc/ethereumjs/example/balance.html

@ -3,6 +3,7 @@
<head>
<script type="text/javascript" src="js/es6-promise/promise.min.js"></script>
<script type="text/javascript" src="js/bignumber.js/bignumber.min.js"></script>
<script type="text/javascript" src="../dist/ethereum.js"></script>
<script type="text/javascript">

3
libjsqrc/ethereumjs/example/contract.html

@ -3,6 +3,7 @@
<head>
<script type="text/javascript" src="js/es6-promise/promise.min.js"></script>
<script type="text/javascript" src="js/bignumber.js/bignumber.min.js"></script>
<script type="text/javascript" src="../dist/ethereum.js"></script>
<script type="text/javascript">
@ -43,7 +44,7 @@
// create contract
web3.eth.transact({code: web3.eth.solidity(source)}).then(function (address) {
contract = web3.contract(address, desc);
contract = web3.eth.contract(address, desc);
document.getElementById('call').style.visibility = 'visible';
});
}

4
libjsqrc/ethereumjs/gulpfile.js

@ -90,7 +90,7 @@ gulp.task('uglify', ['build'], function(){
return uglifyFile('ethereum');
});
gulp.task('uglify', ['buildDev'], function(){
gulp.task('uglifyDev', ['buildDev'], function(){
return uglifyFile('ethereum');
});
@ -99,6 +99,6 @@ gulp.task('watch', function() {
});
gulp.task('release', ['bower', 'lint', 'build', 'uglify']);
gulp.task('dev', ['bower', 'lint', 'buildDev', 'uglify']);
gulp.task('dev', ['bower', 'lint', 'buildDev', 'uglifyDev']);
gulp.task('default', ['dev']);

5
libjsqrc/ethereumjs/index.js

@ -1,8 +1,11 @@
var web3 = require('./lib/web3');
var ProviderManager = require('./lib/providermanager');
web3.provider = new ProviderManager();
web3.filter = require('./lib/filter');
web3.providers.WebSocketProvider = require('./lib/websocket');
web3.providers.HttpRpcProvider = require('./lib/httprpc');
web3.providers.QtProvider = require('./lib/qt');
web3.providers.AutoProvider = require('./lib/autoprovider');
web3.contract = require('./lib/contract');
web3.eth.contract = require('./lib/contract');
module.exports = web3;

370
libjsqrc/ethereumjs/lib/abi.js

@ -23,18 +23,19 @@
// TODO: is these line is supposed to be here?
if (process.env.NODE_ENV !== 'build') {
var web3 = require('./web3'); // jshint ignore:line
var BigNumber = require('bignumber.js'); // jshint ignore:line
}
// TODO: make these be actually accurate instead of falling back onto JS's doubles.
var hexToDec = function (hex) {
return parseInt(hex, 16).toString();
};
var web3 = require('./web3'); // jshint ignore:line
var decToHex = function (dec) {
return parseInt(dec).toString(16);
};
BigNumber.config({ ROUNDING_MODE: BigNumber.ROUND_DOWN });
var ETH_PADDING = 32;
/// Finds first index of array element matching pattern
/// @param array
/// @param callback pattern
/// @returns index of element
var findIndex = function (array, callback) {
var end = false;
var i = 0;
@ -44,106 +45,114 @@ var findIndex = function (array, callback) {
return end ? i - 1 : -1;
};
/// @returns a function that is used as a pattern for 'findIndex'
var findMethodIndex = function (json, methodName) {
return findIndex(json, function (method) {
return method.name === methodName;
});
};
var padLeft = function (string, chars) {
return new Array(chars - string.length + 1).join("0") + string;
/// @param string string to be padded
/// @param number of characters that result string should have
/// @param sign, by default 0
/// @returns right aligned string
var padLeft = function (string, chars, sign) {
return new Array(chars - string.length + 1).join(sign ? sign : "0") + string;
};
var calcBitPadding = function (type, expected) {
var value = type.slice(expected.length);
if (value === "") {
return 32;
}
return parseInt(value) / 8;
/// @param expected type prefix (string)
/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false
var prefixedType = function (prefix) {
return function (type) {
return type.indexOf(prefix) === 0;
};
};
var calcBytePadding = function (type, expected) {
var value = type.slice(expected.length);
if (value === "") {
return 32;
}
return parseInt(value);
/// @param expected type name (string)
/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false
var namedType = function (name) {
return function (type) {
return name === type;
};
};
var calcRealPadding = function (type, expected) {
var value = type.slice(expected.length);
if (value === "") {
return 32;
}
var sizes = value.split('x');
for (var padding = 0, i = 0; i < sizes; i++) {
padding += (sizes[i] / 8);
}
return padding;
var arrayType = function (type) {
return type.slice(-2) === '[]';
};
var setupInputTypes = function () {
// convert from int, decimal-string, prefixed hex string whatever into a bare hex string.
var formatStandard = function (value) {
if (typeof value === "number")
return value.toString(16);
else if (typeof value === "string" && value.indexOf('0x') === 0)
return value.substr(2);
// else if (typeof value === "string")
// return web3.toHex(value);
else
return (+value).toString(16);
};
var prefixedType = function (prefix, calcPadding) {
return function (type, value) {
var expected = prefix;
if (type.indexOf(expected) !== 0) {
return false;
}
var padding = calcPadding(type, expected);
if (padding > 32)
return false; // not allowed to be so big.
padding = 32; // override as per the new ABI.
if (prefix === "string")
return web3.fromAscii(value, padding).substr(2);
return padLeft(formatStandard(value), padding * 2);
};
};
/// Formats input value to byte representation of int
/// If value is negative, return it's two's complement
/// If the value is floating point, round it down
/// @returns right-aligned byte representation of int
var formatInputInt = function (value) {
var padding = ETH_PADDING * 2;
if (value instanceof BigNumber || typeof value === 'number') {
if (typeof value === 'number')
value = new BigNumber(value);
value = value.round();
if (value.lessThan(0))
value = new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(value).plus(1);
value = value.toString(16);
}
else if (value.indexOf('0x') === 0)
value = value.substr(2);
else if (typeof value === 'string')
value = formatInputInt(new BigNumber(value));
else
value = (+value).toString(16);
return padLeft(value, padding);
};
var namedType = function (name, padding, formatter) {
return function (type, value) {
if (type !== name) {
return false;
}
/// Formats input value to byte representation of string
/// @returns left-algined byte representation of string
var formatInputString = function (value) {
return web3.fromAscii(value, ETH_PADDING).substr(2);
};
padding = 32; //override as per the new ABI.
/// Formats input value to byte representation of bool
/// @returns right-aligned byte representation bool
var formatInputBool = function (value) {
return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0');
};
return padLeft(formatter ? formatter(value) : value, padding * 2);
};
};
/// Formats input value to byte representation of real
/// Values are multiplied by 2^m and encoded as integers
/// @returns byte representation of real
var formatInputReal = function (value) {
return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128)));
};
var formatBool = function (value) {
return value ? '01' : '00';
};
var dynamicTypeBytes = function (type, value) {
// TODO: decide what to do with array of strings
if (arrayType(type) || prefixedType('string')(type))
return formatInputInt(value.length);
return "";
};
/// Setups input formatters for solidity types
/// @returns an array of input formatters
var setupInputTypes = function () {
return [
prefixedType('uint', calcBitPadding),
prefixedType('int', calcBitPadding),
prefixedType('hash', calcBitPadding),
prefixedType('string', calcBytePadding),
prefixedType('real', calcRealPadding),
prefixedType('ureal', calcRealPadding),
namedType('address', 20, formatStandard),
namedType('bool', 1, formatBool),
{ type: prefixedType('uint'), format: formatInputInt },
{ type: prefixedType('int'), format: formatInputInt },
{ type: prefixedType('hash'), format: formatInputInt },
{ type: prefixedType('string'), format: formatInputString },
{ type: prefixedType('real'), format: formatInputReal },
{ type: prefixedType('ureal'), format: formatInputReal },
{ type: namedType('address'), format: formatInputInt },
{ type: namedType('bool'), format: formatInputBool }
];
};
var inputTypes = setupInputTypes();
/// Formats input params to bytes
/// @param contract json abi
/// @param name of the method that we want to use
/// @param array of params that will be formatted to bytes
/// @returns bytes representation of input params
var toAbiInput = function (json, methodName, params) {
var bytes = "";
var index = findMethodIndex(json, methodName);
@ -153,74 +162,120 @@ var toAbiInput = function (json, methodName, params) {
}
var method = json[index];
var padding = ETH_PADDING * 2;
/// first we iterate in search for dynamic
method.inputs.forEach(function (input, index) {
bytes += dynamicTypeBytes(input.type, params[index]);
});
for (var i = 0; i < method.inputs.length; i++) {
var found = false;
for (var j = 0; j < inputTypes.length && !found; j++) {
found = inputTypes[j](method.inputs[i].type, params[i]);
method.inputs.forEach(function (input, i) {
var typeMatch = false;
for (var j = 0; j < inputTypes.length && !typeMatch; j++) {
typeMatch = inputTypes[j].type(method.inputs[i].type, params[i]);
}
if (!found) {
console.error('unsupported json type: ' + method.inputs[i].type);
if (!typeMatch) {
console.error('input parser does not support type: ' + method.inputs[i].type);
}
bytes += found;
}
var formatter = inputTypes[j - 1].format;
var toAppend = "";
if (arrayType(method.inputs[i].type))
toAppend = params[i].reduce(function (acc, curr) {
return acc + formatter(curr);
}, "");
else
toAppend = formatter(params[i]);
bytes += toAppend;
});
return bytes;
};
var setupOutputTypes = function () {
/// Check if input value is negative
/// @param value is hex format
/// @returns true if it is negative, otherwise false
var signedIsNegative = function (value) {
return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1';
};
var prefixedType = function (prefix, calcPadding) {
return function (type) {
var expected = prefix;
if (type.indexOf(expected) !== 0) {
return -1;
}
/// Formats input right-aligned input bytes to int
/// @returns right-aligned input bytes formatted to int
var formatOutputInt = function (value) {
// check if it's negative number
// it it is, return two's complement
if (signedIsNegative(value)) {
return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1);
}
return new BigNumber(value, 16);
};
var padding = calcPadding(type, expected);
if (padding > 32)
return -1; // not allowed to be so big.
padding = 32; // override as per the new ABI.
return padding * 2;
};
};
/// Formats big right-aligned input bytes to uint
/// @returns right-aligned input bytes formatted to uint
var formatOutputUInt = function (value) {
return new BigNumber(value, 16);
};
var namedType = function (name, padding) {
return function (type) {
padding = 32; // override as per the new ABI.
return name === type ? padding * 2 : -1;
};
};
/// @returns input bytes formatted to real
var formatOutputReal = function (value) {
return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128));
};
var formatInt = function (value) {
return value.length <= 8 ? +parseInt(value, 16) : hexToDec(value);
};
/// @returns input bytes formatted to ureal
var formatOutputUReal = function (value) {
return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128));
};
var formatHash = function (value) {
return "0x" + value;
};
/// @returns right-aligned input bytes formatted to hex
var formatOutputHash = function (value) {
return "0x" + value;
};
var formatBool = function (value) {
return value === '1' ? true : false;
};
/// @returns right-aligned input bytes formatted to bool
var formatOutputBool = function (value) {
return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false;
};
var formatString = function (value) {
return web3.toAscii(value);
};
/// @returns left-aligned input bytes formatted to ascii string
var formatOutputString = function (value) {
return web3.toAscii(value);
};
/// @returns right-aligned input bytes formatted to address
var formatOutputAddress = function (value) {
return "0x" + value.slice(value.length - 40, value.length);
};
var dynamicBytesLength = function (type) {
if (arrayType(type) || prefixedType('string')(type))
return ETH_PADDING * 2;
return 0;
};
/// Setups output formaters for solidity types
/// @returns an array of output formatters
var setupOutputTypes = function () {
return [
{ padding: prefixedType('uint', calcBitPadding), format: formatInt },
{ padding: prefixedType('int', calcBitPadding), format: formatInt },
{ padding: prefixedType('hash', calcBitPadding), format: formatHash },
{ padding: prefixedType('string', calcBytePadding), format: formatString },
{ padding: prefixedType('real', calcRealPadding), format: formatInt },
{ padding: prefixedType('ureal', calcRealPadding), format: formatInt },
{ padding: namedType('address', 20) },
{ padding: namedType('bool', 1), format: formatBool }
{ type: prefixedType('uint'), format: formatOutputUInt },
{ type: prefixedType('int'), format: formatOutputInt },
{ type: prefixedType('hash'), format: formatOutputHash },
{ type: prefixedType('string'), format: formatOutputString },
{ type: prefixedType('real'), format: formatOutputReal },
{ type: prefixedType('ureal'), format: formatOutputUReal },
{ type: namedType('address'), format: formatOutputAddress },
{ type: namedType('bool'), format: formatOutputBool }
];
};
var outputTypes = setupOutputTypes();
/// Formats output bytes back to param list
/// @param contract json abi
/// @param name of the method that we want to use
/// @param bytes representtion of output
/// @returns array of output params
var fromAbiOutput = function (json, methodName, output) {
var index = findMethodIndex(json, methodName);
@ -232,25 +287,51 @@ var fromAbiOutput = function (json, methodName, output) {
var result = [];
var method = json[index];
for (var i = 0; i < method.outputs.length; i++) {
var padding = -1;
for (var j = 0; j < outputTypes.length && padding === -1; j++) {
padding = outputTypes[j].padding(method.outputs[i].type);
var padding = ETH_PADDING * 2;
var dynamicPartLength = method.outputs.reduce(function (acc, curr) {
return acc + dynamicBytesLength(curr.type);
}, 0);
var dynamicPart = output.slice(0, dynamicPartLength);
output = output.slice(dynamicPartLength);
method.outputs.forEach(function (out, i) {
var typeMatch = false;
for (var j = 0; j < outputTypes.length && !typeMatch; j++) {
typeMatch = outputTypes[j].type(method.outputs[i].type);
}
if (padding === -1) {
// not found output parsing
continue;
if (!typeMatch) {
console.error('output parser does not support type: ' + method.outputs[i].type);
}
var res = output.slice(0, padding);
var formatter = outputTypes[j - 1].format;
result.push(formatter ? formatter(res) : ("0x" + res));
output = output.slice(padding);
}
if (arrayType(method.outputs[i].type)) {
var size = formatOutputUInt(dynamicPart.slice(0, padding));
dynamicPart = dynamicPart.slice(padding);
var array = [];
for (var k = 0; k < size; k++) {
array.push(formatter(output.slice(0, padding)));
output = output.slice(padding);
}
result.push(array);
}
else if (prefixedType('string')(method.outputs[i].type)) {
dynamicPart = dynamicPart.slice(padding);
result.push(formatter(output.slice(0, padding)));
output = output.slice(padding);
} else {
result.push(formatter(output.slice(0, padding)));
output = output.slice(padding);
}
});
return result;
};
/// @param json abi for contract
/// @returns input parser object for given json abi
var inputParser = function (json) {
var parser = {};
json.forEach(function (method) {
@ -263,6 +344,8 @@ var inputParser = function (json) {
return parser;
};
/// @param json abi for contract
/// @returns output parser for given json abi
var outputParser = function (json) {
var parser = {};
json.forEach(function (method) {
@ -274,6 +357,9 @@ var outputParser = function (json) {
return parser;
};
/// @param json abi for contract
/// @param method name for which we want to get method signature
/// @returns (promise) contract method signature for method with given name
var methodSignature = function (json, name) {
var method = json[findMethodIndex(json, name)];
var result = name + '(';

18
libjsqrc/ethereumjs/lib/autoprovider.js

@ -27,12 +27,21 @@
* if it fails, it uses HttpRpcProvider
*/
// TODO: is these line is supposed to be here?
var web3 = require('./web3'); // jshint ignore:line
if (process.env.NODE_ENV !== 'build') {
var WebSocket = require('ws'); // jshint ignore:line
var web3 = require('./web3'); // jshint ignore:line
}
/**
* AutoProvider object prototype is implementing 'provider protocol'
* Automatically tries to setup correct provider(Qt, WebSockets or HttpRpc)
* First it checkes if we are ethereum browser (if navigator.qt object is available)
* if yes, we are using QtProvider
* if no, we check if it is possible to establish websockets connection with ethereum (ws://localhost:40404/eth is default)
* if it's not possible, we are using httprpc provider (http://localhost:8080)
* The constructor allows you to specify uris on which we are trying to connect over http or websockets
* You can do that by passing objects with fields httrpc and websockets
*/
var AutoProvider = function (userOptions) {
if (web3.haveProvider()) {
return;
@ -63,7 +72,7 @@ var AutoProvider = function (userOptions) {
self.poll = self.provider.poll.bind(self.provider);
}
self.sendQueue.forEach(function (payload) {
self.provider(payload);
self.provider.send(payload);
});
self.onmessageQueue.forEach(function (handler) {
self.provider.onmessage = handler;
@ -81,6 +90,8 @@ var AutoProvider = function (userOptions) {
};
};
/// Sends message forward to the provider, that is being used
/// if provider is not yet set, enqueues the message
AutoProvider.prototype.send = function (payload) {
if (this.provider) {
this.provider.send(payload);
@ -89,6 +100,7 @@ AutoProvider.prototype.send = function (payload) {
this.sendQueue.push(payload);
};
/// On incoming message sends the message to the provider that is currently being used
Object.defineProperty(AutoProvider.prototype, 'onmessage', {
set: function (handler) {
if (this.provider) {

29
libjsqrc/ethereumjs/lib/contract.js

@ -20,16 +20,32 @@
* @date 2014
*/
// TODO: is these line is supposed to be here?
if (process.env.NODE_ENV !== 'build') {
var web3 = require('./web3'); // jshint ignore:line
}
var web3 = require('./web3'); // jshint ignore:line
var abi = require('./abi');
// method signature length in bytes
/// method signature length in bytes
var ETH_METHOD_SIGNATURE_LENGTH = 4;
/**
* This method should be called when we want to call / transact some solidity method from javascript
* it returns an object which has same methods available as solidity contract description
* usage example:
*
* var abi = [{
* name: 'myMethod',
* inputs: [{ name: 'a', type: 'string' }],
* outputs: [{name: 'd', type: 'string' }]
* }]; // contract abi
*
* var myContract = web3.eth.contract('0x0123123121', abi); // creation of contract object
*
* myContract.myMethod('this is test string param for call').call(); // myMethod call
* myContract.myMethod('this is test string param for transact').transact() // myMethod transact
*
* @param address - address of the contract, which should be called
* @param desc - abi json description of the contract, which is being created
* @returns contract object
*/
var contract = function (address, desc) {
var inputParser = abi.inputParser(desc);
var outputParser = abi.outputParser(desc);
@ -70,3 +86,4 @@ var contract = function (address, desc) {
};
module.exports = contract;

86
libjsqrc/ethereumjs/lib/filter.js

@ -0,0 +1,86 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file filter.js
* @authors:
* Jeffrey Wilcke <jeff@ethdev.com>
* Marek Kotewicz <marek@ethdev.com>
* Marian Oancea <marian@ethdev.com>
* Gav Wood <g@ethdev.com>
* @date 2014
*/
var web3 = require('./web3'); // jshint ignore:line
/// should be used when we want to watch something
/// it's using inner polling mechanism and is notified about changes
var Filter = function(options, impl) {
this.impl = impl;
this.callbacks = [];
var self = this;
this.promise = impl.newFilter(options);
this.promise.then(function (id) {
self.id = id;
web3.on(impl.changed, id, self.trigger.bind(self));
web3.provider.startPolling({call: impl.changed, args: [id]}, id);
});
};
/// alias for changed*
Filter.prototype.arrived = function(callback) {
this.changed(callback);
};
/// gets called when there is new eth/shh message
Filter.prototype.changed = function(callback) {
var self = this;
this.promise.then(function(id) {
self.callbacks.push(callback);
});
};
/// trigger calling new message from people
Filter.prototype.trigger = function(messages) {
for(var i = 0; i < this.callbacks.length; i++) {
this.callbacks[i].call(this, messages);
}
};
/// should be called to uninstall current filter
Filter.prototype.uninstall = function() {
var self = this;
this.promise.then(function (id) {
self.impl.uninstallFilter(id);
web3.provider.stopPolling(id);
web3.off(impl.changed, id);
});
};
/// should be called to manually trigger getting latest messages from the client
Filter.prototype.messages = function() {
var self = this;
return this.promise.then(function (id) {
return self.impl.getMessages(id);
});
};
/// alias for messages
Filter.prototype.logs = function () {
return this.messages();
};
module.exports = Filter;

32
libjsqrc/ethereumjs/lib/httprpc.js

@ -26,11 +26,21 @@ if (process.env.NODE_ENV !== 'build') {
var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line
}
/**
* HttpRpcProvider object prototype is implementing 'provider protocol'
* Should be used when we want to connect to ethereum backend over http && jsonrpc
* It's compatible with cpp client
* The contructor allows to specify host uri
* This provider is using in-browser polling mechanism
*/
var HttpRpcProvider = function (host) {
this.handlers = [];
this.host = host;
};
/// Transforms inner message to proper jsonrpc object
/// @param inner message object
/// @returns jsonrpc object
function formatJsonRpcObject(object) {
return {
jsonrpc: '2.0',
@ -40,6 +50,9 @@ function formatJsonRpcObject(object) {
};
}
/// Transforms jsonrpc object to inner message
/// @param incoming jsonrpc message
/// @returns inner message object
function formatJsonRpcMessage(message) {
var object = JSON.parse(message);
@ -50,6 +63,10 @@ function formatJsonRpcMessage(message) {
};
}
/// Prototype object method
/// Asynchronously sends request to server
/// @param payload is inner message object
/// @param cb is callback which is being called when response is comes back
HttpRpcProvider.prototype.sendRequest = function (payload, cb) {
var data = formatJsonRpcObject(payload);
@ -63,6 +80,11 @@ HttpRpcProvider.prototype.sendRequest = function (payload, cb) {
};
};
/// Prototype object method
/// Should be called when we want to send single api request to server
/// Asynchronous
/// On response it passes message to handlers
/// @param payload is inner message object
HttpRpcProvider.prototype.send = function (payload) {
var self = this;
this.sendRequest(payload, function (request) {
@ -72,6 +94,13 @@ HttpRpcProvider.prototype.send = function (payload) {
});
};
/// Prototype object method
/// Should be called only for polling requests
/// Asynchronous
/// On response it passege message to handlers, but only if message's result is true or not empty array
/// Otherwise response is being silently ignored
/// @param payload is inner message object
/// @id is id of poll that we are calling
HttpRpcProvider.prototype.poll = function (payload, id) {
var self = this;
this.sendRequest(payload, function (request) {
@ -85,6 +114,8 @@ HttpRpcProvider.prototype.poll = function (payload, id) {
});
};
/// Prototype object property
/// Should be used to set message handlers for this provider
Object.defineProperty(HttpRpcProvider.prototype, "onmessage", {
set: function (handler) {
this.handlers.push(handler);
@ -92,3 +123,4 @@ Object.defineProperty(HttpRpcProvider.prototype, "onmessage", {
});
module.exports = HttpRpcProvider;

119
libjsqrc/ethereumjs/lib/providermanager.js

@ -0,0 +1,119 @@
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file providermanager.js
* @authors:
* Jeffrey Wilcke <jeff@ethdev.com>
* Marek Kotewicz <marek@ethdev.com>
* Marian Oancea <marian@ethdev.com>
* Gav Wood <g@ethdev.com>
* @date 2014
*/
var web3 = require('./web3'); // jshint ignore:line
/**
* Provider manager object prototype
* It's responsible for passing messages to providers
* If no provider is set it's responsible for queuing requests
* It's also responsible for polling the ethereum node for incoming messages
* Default poll timeout is 12 seconds
* If we are running ethereum.js inside ethereum browser, there are backend based tools responsible for polling,
* and provider manager polling mechanism is not used
*/
var ProviderManager = function() {
this.queued = [];
this.polls = [];
this.ready = false;
this.provider = undefined;
this.id = 1;
var self = this;
var poll = function () {
if (self.provider && self.provider.poll) {
self.polls.forEach(function (data) {
data.data._id = self.id;
self.id++;
self.provider.poll(data.data, data.id);
});
}
setTimeout(poll, 12000);
};
poll();
};
/// sends outgoing requests, if provider is not available, enqueue the request
ProviderManager.prototype.send = function(data, cb) {
data._id = this.id;
if (cb) {
web3._callbacks[data._id] = cb;
}
data.args = data.args || [];
this.id++;
if(this.provider !== undefined) {
this.provider.send(data);
} else {
console.warn("provider is not set");
this.queued.push(data);
}
};
/// setups provider, which will be used for sending messages
ProviderManager.prototype.set = function(provider) {
if(this.provider !== undefined && this.provider.unload !== undefined) {
this.provider.unload();
}
this.provider = provider;
this.ready = true;
};
/// resends queued messages
ProviderManager.prototype.sendQueued = function() {
for(var i = 0; this.queued.length; i++) {
// Resend
this.send(this.queued[i]);
}
};
/// @returns true if the provider i properly set
ProviderManager.prototype.installed = function() {
return this.provider !== undefined;
};
/// this method is only used, when we do not have native qt bindings and have to do polling on our own
/// should be callled, on start watching for eth/shh changes
ProviderManager.prototype.startPolling = function (data, pollId) {
if (!this.provider || !this.provider.poll) {
return;
}
this.polls.push({data: data, id: pollId});
};
/// should be called to stop polling for certain watch changes
ProviderManager.prototype.stopPolling = function (pollId) {
for (var i = this.polls.length; i--;) {
var poll = this.polls[i];
if (poll.id === pollId) {
this.polls.splice(i, 1);
}
}
};
module.exports = ProviderManager;

12
libjsqrc/ethereumjs/lib/qt.js

@ -21,6 +21,11 @@
* @date 2014
*/
/**
* QtProvider object prototype is implementing 'provider protocol'
* Should be used inside ethereum browser. It's compatible with cpp and go clients.
* It uses navigator.qt object to pass the messages to native bindings
*/
var QtProvider = function() {
this.handlers = [];
@ -32,10 +37,17 @@ var QtProvider = function() {
};
};
/// Prototype object method
/// Should be called when we want to send single api request to native bindings
/// Asynchronous
/// Response will be received by navigator.qt.onmessage method and passed to handlers
/// @param payload is inner message object
QtProvider.prototype.send = function(payload) {
navigator.qt.postMessage(JSON.stringify(payload));
};
/// Prototype object property
/// Should be used to set message handlers for this provider
Object.defineProperty(QtProvider.prototype, "onmessage", {
set: function(handler) {
this.handlers.push(handler);

187
libjsqrc/ethereumjs/lib/web3.js

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file main.js
/** @file web3.js
* @authors:
* Jeffrey Wilcke <jeff@ethdev.com>
* Marek Kotewicz <marek@ethdev.com>
@ -23,6 +23,9 @@
* @date 2014
*/
/// Recursively resolves all promises in given object and replaces the resolved values with promises
/// @param any object/array/promise/anything else..
/// @returns (resolves) object with replaced promises with their result
function flattenPromise (obj) {
if (obj instanceof Promise) {
return Promise.resolve(obj);
@ -62,12 +65,14 @@ function flattenPromise (obj) {
return Promise.resolve(obj);
}
/// @returns an array of objects describing web3 api methods
var web3Methods = function () {
return [
{ name: 'sha3', call: 'web3_sha3' }
];
};
/// @returns an array of objects describing web3.eth api methods
var ethMethods = function () {
var blockCall = function (args) {
return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber";
@ -101,6 +106,7 @@ var ethMethods = function () {
return methods;
};
/// @returns an array of objects describing web3.eth api properties
var ethProperties = function () {
return [
{ name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' },
@ -115,6 +121,7 @@ var ethProperties = function () {
];
};
/// @returns an array of objects describing web3.db api methods
var dbMethods = function () {
return [
{ name: 'put', call: 'db_put' },
@ -124,6 +131,7 @@ var dbMethods = function () {
];
};
/// @returns an array of objects describing web3.shh api methods
var shhMethods = function () {
return [
{ name: 'post', call: 'shh_post' },
@ -134,6 +142,7 @@ var shhMethods = function () {
];
};
/// @returns an array of objects describing web3.eth.watch api methods
var ethWatchMethods = function () {
var newFilter = function (args) {
return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter';
@ -146,6 +155,7 @@ var ethWatchMethods = function () {
];
};
/// @returns an array of objects describing web3.shh.watch api methods
var shhWatchMethods = function () {
return [
{ name: 'newFilter', call: 'shh_newFilter' },
@ -154,6 +164,8 @@ var shhWatchMethods = function () {
];
};
/// creates methods in a given object based on method description on input
/// setups api calls for these methods
var setupMethods = function (obj, methods) {
methods.forEach(function (method) {
obj[method.name] = function () {
@ -177,6 +189,8 @@ var setupMethods = function (obj, methods) {
});
};
/// creates properties in a given object based on properties description on input
/// setups api calls for these properties
var setupProperties = function (obj, properties) {
properties.forEach(function (property) {
var proto = {};
@ -221,7 +235,7 @@ var decToHex = function (dec) {
return parseInt(dec).toString(16);
};
/// setups web3 object, and it's in-browser executed methods
var web3 = {
_callbacks: {},
_events: {},
@ -237,6 +251,7 @@ var web3 = {
return hex;
},
/// @returns ascii string representation of hex value prefixed with 0x
toAscii: function(hex) {
// Find termination
var str = "";
@ -244,17 +259,18 @@ var web3 = {
if (hex.substring(0, 2) === '0x')
i = 2;
for(; i < l; i+=2) {
var code = hex.charCodeAt(i);
var code = parseInt(hex.substr(i, 2), 16);
if(code === 0) {
break;
}
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
str += String.fromCharCode(code);
}
return str;
},
/// @returns hex representation (prefixed by 0x) of ascii string
fromAscii: function(str, pad) {
pad = pad === undefined ? 0 : pad;
var hex = this.toHex(str);
@ -263,14 +279,17 @@ var web3 = {
return "0x" + hex;
},
/// @returns decimal representaton of hex value prefixed by 0x
toDecimal: function (val) {
return hexToDec(val.substring(2));
},
/// @returns hex representation (prefixed by 0x) of decimal value
fromDecimal: function (val) {
return "0x" + decToHex(val);
},
/// used to transform value/string to eth string
toEth: function(str) {
var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;
var unit = 0;
@ -294,24 +313,24 @@ var web3 = {
return s + ' ' + units[unit];
},
/// eth object prototype
eth: {
prototype: Object(), // jshint ignore:line
watch: function (params) {
return new Filter(params, ethWatch);
return new web3.filter(params, ethWatch);
}
},
db: {
prototype: Object() // jshint ignore:line
},
/// db object prototype
db: {},
/// shh object prototype
shh: {
prototype: Object(), // jshint ignore:line
watch: function (params) {
return new Filter(params, shhWatch);
return new web3.filter(params, shhWatch);
}
},
/// used by filter to register callback with given id
on: function(event, id, cb) {
if(web3._events[event] === undefined) {
web3._events[event] = {};
@ -321,6 +340,7 @@ var web3 = {
return this;
},
/// used by filter to unregister callback with given id
off: function(event, id) {
if(web3._events[event] !== undefined) {
delete web3._events[event][id];
@ -329,6 +349,7 @@ var web3 = {
return this;
},
/// used to trigger callback registered by filter
trigger: function(event, id, data) {
var callbacks = web3._events[event];
if (!callbacks || !callbacks[id]) {
@ -336,9 +357,15 @@ var web3 = {
}
var cb = callbacks[id];
cb(data);
},
/// @returns true if provider is installed
haveProvider: function() {
return !!web3.provider.provider;
}
};
/// setups all api methods
setupMethods(web3, web3Methods());
setupMethods(web3.eth, ethMethods());
setupProperties(web3.eth, ethProperties());
@ -348,87 +375,14 @@ setupMethods(web3.shh, shhMethods());
var ethWatch = {
changed: 'eth_changed'
};
setupMethods(ethWatch, ethWatchMethods());
var shhWatch = {
changed: 'shh_changed'
};
setupMethods(shhWatch, shhWatchMethods());
var ProviderManager = function() {
this.queued = [];
this.polls = [];
this.ready = false;
this.provider = undefined;
this.id = 1;
var self = this;
var poll = function () {
if (self.provider && self.provider.poll) {
self.polls.forEach(function (data) {
data.data._id = self.id;
self.id++;
self.provider.poll(data.data, data.id);
});
}
setTimeout(poll, 12000);
};
poll();
};
ProviderManager.prototype.send = function(data, cb) {
data._id = this.id;
if (cb) {
web3._callbacks[data._id] = cb;
}
data.args = data.args || [];
this.id++;
if(this.provider !== undefined) {
this.provider.send(data);
} else {
console.warn("provider is not set");
this.queued.push(data);
}
};
ProviderManager.prototype.set = function(provider) {
if(this.provider !== undefined && this.provider.unload !== undefined) {
this.provider.unload();
}
this.provider = provider;
this.ready = true;
};
ProviderManager.prototype.sendQueued = function() {
for(var i = 0; this.queued.length; i++) {
// Resend
this.send(this.queued[i]);
}
};
ProviderManager.prototype.installed = function() {
return this.provider !== undefined;
};
ProviderManager.prototype.startPolling = function (data, pollId) {
if (!this.provider || !this.provider.poll) {
return;
}
this.polls.push({data: data, id: pollId});
};
ProviderManager.prototype.stopPolling = function (pollId) {
for (var i = this.polls.length; i--;) {
var poll = this.polls[i];
if (poll.id === pollId) {
this.polls.splice(i, 1);
}
}
};
web3.provider = new ProviderManager();
setupMethods(shhWatch, shhWatchMethods());
web3.setProvider = function(provider) {
provider.onmessage = messageHandler;
@ -436,60 +390,7 @@ web3.setProvider = function(provider) {
web3.provider.sendQueued();
};
web3.haveProvider = function() {
return !!web3.provider.provider;
};
var Filter = function(options, impl) {
this.impl = impl;
this.callbacks = [];
var self = this;
this.promise = impl.newFilter(options);
this.promise.then(function (id) {
self.id = id;
web3.on(impl.changed, id, self.trigger.bind(self));
web3.provider.startPolling({call: impl.changed, args: [id]}, id);
});
};
Filter.prototype.arrived = function(callback) {
this.changed(callback);
};
Filter.prototype.changed = function(callback) {
var self = this;
this.promise.then(function(id) {
self.callbacks.push(callback);
});
};
Filter.prototype.trigger = function(messages) {
for(var i = 0; i < this.callbacks.length; i++) {
this.callbacks[i].call(this, messages);
}
};
Filter.prototype.uninstall = function() {
var self = this;
this.promise.then(function (id) {
self.impl.uninstallFilter(id);
web3.provider.stopPolling(id);
web3.off(impl.changed, id);
});
};
Filter.prototype.messages = function() {
var self = this;
return this.promise.then(function (id) {
return self.impl.getMessages(id);
});
};
Filter.prototype.logs = function () {
return this.messages();
};
/// callled when there is new incoming message
function messageHandler(data) {
if(data._event !== undefined) {
web3.trigger(data._event, data._id, data.data);
@ -505,5 +406,5 @@ function messageHandler(data) {
}
}
if (typeof(module) !== "undefined")
module.exports = web3;
module.exports = web3;

24
libjsqrc/ethereumjs/lib/websocket.js

@ -27,9 +27,17 @@ if (process.env.NODE_ENV !== 'build') {
var WebSocket = require('ws'); // jshint ignore:line
}
/**
* WebSocketProvider object prototype is implementing 'provider protocol'
* Should be used when we want to connect to ethereum backend over websockets
* It's compatible with go client
* The constructor allows to specify host uri
*/
var WebSocketProvider = function(host) {
// onmessage handlers
this.handlers = [];
// queue will be filled with messages if send is invoked before the ws is ready
this.queued = [];
this.ready = false;
@ -46,15 +54,20 @@ var WebSocketProvider = function(host) {
this.ws.onopen = function() {
self.ready = true;
for(var i = 0; i < self.queued.length; i++) {
for (var i = 0; i < self.queued.length; i++) {
// Resend
self.send(self.queued[i]);
}
};
};
/// Prototype object method
/// Should be called when we want to send single api request to server
/// Asynchronous, it's using websockets
/// Response for the call will be received by ws.onmessage
/// @param payload is inner message object
WebSocketProvider.prototype.send = function(payload) {
if(this.ready) {
if (this.ready) {
var data = JSON.stringify(payload);
this.ws.send(data);
@ -63,13 +76,20 @@ WebSocketProvider.prototype.send = function(payload) {
}
};
/// Prototype object method
/// Should be called to add handlers
WebSocketProvider.prototype.onMessage = function(handler) {
this.handlers.push(handler);
};
/// Prototype object method
/// Should be called to close websockets connection
WebSocketProvider.prototype.unload = function() {
this.ws.close();
};
/// Prototype object property
/// Should be used to set message handlers for this provider
Object.defineProperty(WebSocketProvider.prototype, "onmessage", {
set: function(provider) { this.onMessage(provider); }
});

5
libjsqrc/ethereumjs/package.json

@ -1,7 +1,7 @@
{
"name": "ethereum.js",
"namespace": "ethereum",
"version": "0.0.7",
"version": "0.0.8",
"description": "Ethereum Compatible JavaScript API",
"main": "./index.js",
"directories": {
@ -10,7 +10,8 @@
"dependencies": {
"es6-promise": "*",
"ws": "*",
"xmlhttprequest": "*"
"xmlhttprequest": "*",
"bignumber.js": ">=2.0.0"
},
"devDependencies": {
"bower": ">=1.3.0",

830
libjsqrc/ethereumjs/test/abi.parsers.js

@ -1,37 +1,827 @@
var assert = require('assert');
var BigNumber = require('bignumber.js');
var abi = require('../lib/abi.js');
var clone = function (object) { return JSON.parse(JSON.stringify(object)); };
var description = [{
"name": "test",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
describe('abi', function() {
describe('inputParser', function() {
it('should parse ...', function() {
var desc = [{
"name": "multiply",
"inputs": [
{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
it('should parse input uint', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: "uint" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a");
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003");
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003");
});
it('should parse input uint128', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: "uint128" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a");
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003");
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003");
});
it('should parse input uint256', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: "uint256" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a");
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003");
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003");
});
it('should parse input int', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: "int" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a");
assert.equal(parser.test(-1), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
assert.equal(parser.test(-2), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe");
assert.equal(parser.test(-16), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0");
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003");
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003");
});
it('should parse input int128', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: "int128" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a");
assert.equal(parser.test(-1), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
assert.equal(parser.test(-2), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe");
assert.equal(parser.test(-16), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0");
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003");
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003");
});
it('should parse input int256', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: "int256" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a");
assert.equal(parser.test(-1), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
assert.equal(parser.test(-2), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe");
assert.equal(parser.test(-16), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0");
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
);
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003");
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000");
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003");
});
it('should parse input bool', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: 'bool' }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test(true), "0000000000000000000000000000000000000000000000000000000000000001");
assert.equal(parser.test(false), "0000000000000000000000000000000000000000000000000000000000000000");
});
it('should parse input hash', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: "hash" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test("0x407d73d8a49eeb85d32cf465507dd71d507100c1"), "000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1");
});
it('should parse input hash256', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: "hash256" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test("0x407d73d8a49eeb85d32cf465507dd71d507100c1"), "000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1");
});
it('should parse input hash160', function() {
// given
var d = clone(description);
d[0].inputs = [
{ type: "hash160" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test("0x407d73d8a49eeb85d32cf465507dd71d507100c1"), "000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1");
});
it('should parse input address', function () {
// given
var d = clone(description);
d[0].inputs = [
{ type: "address" }
];
// when
var parser = abi.inputParser(d)
// then
assert.equal(parser.test("0x407d73d8a49eeb85d32cf465507dd71d507100c1"), "000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1");
});
it('should parse input string', function () {
// given
var d = clone(description);
d[0].inputs = [
{ type: "string" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(
parser.test('hello'),
"000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000"
);
assert.equal(
parser.test('world'),
"0000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000"
);
});
it('should use proper method name', function () {
// given
var d = clone(description);
d[0].name = 'helloworld';
d[0].inputs = [
{ type: "int" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.helloworld(1), "0000000000000000000000000000000000000000000000000000000000000001");
});
it('should parse multiple methods', function () {
// given
var d = [{
name: "test",
inputs: [{ type: "int" }],
outputs: [{ type: "int" }]
},{
name: "test2",
inputs: [{ type: "string" }],
outputs: [{ type: "string" }]
}];
var iParser = abi.inputParser(desc);
assert.equal(iParser.multiply(1), "0x000000000000000000000000000000000000000000000000000000000000000001");
// when
var parser = abi.inputParser(d);
//then
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001");
assert.equal(
parser.test2('hello'),
"000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000"
);
});
});
it('should parse input array of ints', function () {
// given
var d = clone(description);
d[0].inputs = [
{ type: "int[]" }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(
parser.test([5, 6]),
"0000000000000000000000000000000000000000000000000000000000000002" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"0000000000000000000000000000000000000000000000000000000000000006"
);
});
it('should parse input real', function () {
// given
var d = clone(description);
d[0].inputs = [
{ type: 'real' }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test([1]), "0000000000000000000000000000000100000000000000000000000000000000");
assert.equal(parser.test([2.125]), "0000000000000000000000000000000220000000000000000000000000000000");
assert.equal(parser.test([8.5]), "0000000000000000000000000000000880000000000000000000000000000000");
assert.equal(parser.test([-1]), "ffffffffffffffffffffffffffffffff00000000000000000000000000000000");
});
it('should parse input ureal', function () {
// given
var d = clone(description);
d[0].inputs = [
{ type: 'ureal' }
];
// when
var parser = abi.inputParser(d);
// then
assert.equal(parser.test([1]), "0000000000000000000000000000000100000000000000000000000000000000");
assert.equal(parser.test([2.125]), "0000000000000000000000000000000220000000000000000000000000000000");
assert.equal(parser.test([8.5]), "0000000000000000000000000000000880000000000000000000000000000000");
});
});
describe('outputParser', function() {
it('parse ...', function() {
it('should parse output string', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: "string" }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"68656c6c6f000000000000000000000000000000000000000000000000000000")[0],
'hello'
);
assert.equal(
parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"776f726c64000000000000000000000000000000000000000000000000000000")[0],
'world'
);
});
it('should parse output uint', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'uint' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0].toString(10),
new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).toString(10)
);
assert.equal(
parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0].toString(10),
new BigNumber("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0", 16).toString(10)
);
});
it('should parse output uint256', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'uint256' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0].toString(10),
new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).toString(10)
);
assert.equal(
parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0].toString(10),
new BigNumber("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0", 16).toString(10)
);
});
it('should parse output uint128', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'uint128' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0].toString(10),
new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).toString(10)
);
assert.equal(
parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0].toString(10),
new BigNumber("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0", 16).toString(10)
);
});
it('should parse output int', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0], -1);
assert.equal(parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0], -16);
});
it('should parse output int256', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int256' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0], -1);
assert.equal(parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0], -16);
});
it('should parse output int128', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int128' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test("0x000000000000000000000000000000000000000000000000000000000000000a")[0], 10);
assert.equal(parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[0], -1);
assert.equal(parser.test("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0")[0], -16);
});
it('should parse output hash', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'hash' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
"0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1"
);
});
it('should parse output hash256', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'hash256' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
"0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1"
);
});
it('should parse output hash160', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'hash160' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
"0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1"
);
// TODO shouldnt' the expected hash be shorter?
});
it('should parse output address', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'address' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1")[0],
"0x407d73d8a49eeb85d32cf465507dd71d507100c1"
);
});
it('should parse output bool', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'bool' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000001")[0], true);
assert.equal(parser.test("0x0000000000000000000000000000000000000000000000000000000000000000")[0], false);
});
it('should parse output real', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'real' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000100000000000000000000000000000000")[0], 1);
assert.equal(parser.test("0x0000000000000000000000000000000220000000000000000000000000000000")[0], 2.125);
assert.equal(parser.test("0x0000000000000000000000000000000880000000000000000000000000000000")[0], 8.5);
assert.equal(parser.test("0xffffffffffffffffffffffffffffffff00000000000000000000000000000000")[0], -1);
});
it('should parse output ureal', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'ureal' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x0000000000000000000000000000000100000000000000000000000000000000")[0], 1);
assert.equal(parser.test("0x0000000000000000000000000000000220000000000000000000000000000000")[0], 2.125);
assert.equal(parser.test("0x0000000000000000000000000000000880000000000000000000000000000000")[0], 8.5);
});
it('should parse multiple output strings', function() {
// given
var d = clone(description);
d[0].outputs = [
{ type: "string" },
{ type: "string" }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(
parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"68656c6c6f000000000000000000000000000000000000000000000000000000" +
"776f726c64000000000000000000000000000000000000000000000000000000")[0],
'hello'
);
assert.equal(
parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"68656c6c6f000000000000000000000000000000000000000000000000000000" +
"776f726c64000000000000000000000000000000000000000000000000000000")[1],
'world'
);
});
it('should use proper method name', function () {
// given
var d = clone(description);
d[0].name = 'helloworld';
d[0].outputs = [
{ type: "int" }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.helloworld("0x0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
});
it('should parse multiple methods', function () {
// given
var d = [{
name: "test",
inputs: [{ type: "int" }],
outputs: [{ type: "int" }]
},{
name: "test2",
inputs: [{ type: "string" }],
outputs: [{ type: "string" }]
}];
// when
var parser = abi.outputParser(d);
//then
assert.equal(parser.test("0000000000000000000000000000000000000000000000000000000000000001")[0], 1);
assert.equal(parser.test2("0x" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"68656c6c6f000000000000000000000000000000000000000000000000000000")[0],
"hello"
);
});
it('should parse output array', function () {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int[]' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000002" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"0000000000000000000000000000000000000000000000000000000000000006")[0][0],
5
);
assert.equal(parser.test("0x" +
"0000000000000000000000000000000000000000000000000000000000000002" +
"0000000000000000000000000000000000000000000000000000000000000005" +
"0000000000000000000000000000000000000000000000000000000000000006")[0][1],
6
);
});
});
});

10
libjsqrc/ethereumjs/test/db.methods.js

@ -7,12 +7,10 @@ web3.setProvider(new web3.providers.WebSocketProvider('http://localhost:8080'));
describe('web3', function() {
describe('db', function() {
it('should have all methods implemented', function() {
u.methodExists(web3.db, 'put');
u.methodExists(web3.db, 'get');
u.methodExists(web3.db, 'putString');
u.methodExists(web3.db, 'getString');
});
u.methodExists(web3.db, 'put');
u.methodExists(web3.db, 'get');
u.methodExists(web3.db, 'putString');
u.methodExists(web3.db, 'getString');
});
});

52
libjsqrc/ethereumjs/test/eth.methods.js

@ -7,35 +7,31 @@ web3.setProvider(new web3.providers.WebSocketProvider('http://localhost:8080'));
describe('web3', function() {
describe('eth', function() {
it('should have all methods implemented', function() {
u.methodExists(web3.eth, 'balanceAt');
u.methodExists(web3.eth, 'stateAt');
u.methodExists(web3.eth, 'storageAt');
u.methodExists(web3.eth, 'countAt');
u.methodExists(web3.eth, 'codeAt');
u.methodExists(web3.eth, 'transact');
u.methodExists(web3.eth, 'call');
u.methodExists(web3.eth, 'block');
u.methodExists(web3.eth, 'transaction');
u.methodExists(web3.eth, 'uncle');
u.methodExists(web3.eth, 'compilers');
u.methodExists(web3.eth, 'lll');
u.methodExists(web3.eth, 'solidity');
u.methodExists(web3.eth, 'serpent');
u.methodExists(web3.eth, 'logs');
});
u.methodExists(web3.eth, 'balanceAt');
u.methodExists(web3.eth, 'stateAt');
u.methodExists(web3.eth, 'storageAt');
u.methodExists(web3.eth, 'countAt');
u.methodExists(web3.eth, 'codeAt');
u.methodExists(web3.eth, 'transact');
u.methodExists(web3.eth, 'call');
u.methodExists(web3.eth, 'block');
u.methodExists(web3.eth, 'transaction');
u.methodExists(web3.eth, 'uncle');
u.methodExists(web3.eth, 'compilers');
u.methodExists(web3.eth, 'lll');
u.methodExists(web3.eth, 'solidity');
u.methodExists(web3.eth, 'serpent');
u.methodExists(web3.eth, 'logs');
it('should have all properties implemented', function () {
u.propertyExists(web3.eth, 'coinbase');
u.propertyExists(web3.eth, 'listening');
u.propertyExists(web3.eth, 'mining');
u.propertyExists(web3.eth, 'gasPrice');
u.propertyExists(web3.eth, 'account');
u.propertyExists(web3.eth, 'accounts');
u.propertyExists(web3.eth, 'peerCount');
u.propertyExists(web3.eth, 'defaultBlock');
u.propertyExists(web3.eth, 'number');
});
u.propertyExists(web3.eth, 'coinbase');
u.propertyExists(web3.eth, 'listening');
u.propertyExists(web3.eth, 'mining');
u.propertyExists(web3.eth, 'gasPrice');
u.propertyExists(web3.eth, 'account');
u.propertyExists(web3.eth, 'accounts');
u.propertyExists(web3.eth, 'peerCount');
u.propertyExists(web3.eth, 'defaultBlock');
u.propertyExists(web3.eth, 'number');
});
});

2
libjsqrc/ethereumjs/test/mocha.opts

@ -1,2 +1,2 @@
--reporter Spec
--reporter spec

12
libjsqrc/ethereumjs/test/shh.methods.js

@ -7,13 +7,11 @@ web3.setProvider(new web3.providers.WebSocketProvider('http://localhost:8080'));
describe('web3', function() {
describe('shh', function() {
it('should have all methods implemented', function() {
u.methodExists(web3.shh, 'post');
u.methodExists(web3.shh, 'newIdentity');
u.methodExists(web3.shh, 'haveIdentity');
u.methodExists(web3.shh, 'newGroup');
u.methodExists(web3.shh, 'addToGroup');
});
u.methodExists(web3.shh, 'post');
u.methodExists(web3.shh, 'newIdentity');
u.methodExists(web3.shh, 'haveIdentity');
u.methodExists(web3.shh, 'newGroup');
u.methodExists(web3.shh, 'addToGroup');
});
});

8
libjsqrc/ethereumjs/test/utils.js

@ -1,11 +1,15 @@
var assert = require('assert');
var methodExists = function (object, method) {
assert.equal('function', typeof object[method], 'method ' + method + ' is not implemented');
it('should have method ' + method + ' implemented', function() {
assert.equal('function', typeof object[method], 'method ' + method + ' is not implemented');
});
};
var propertyExists = function (object, property) {
assert.equal('object', typeof object[property], 'property ' + property + ' is not implemented');
it('should have property ' + property + ' implemented', function() {
assert.equal('object', typeof object[property], 'property ' + property + ' is not implemented');
});
};
module.exports = {

14
libjsqrc/ethereumjs/test/web3.methods.js

@ -6,13 +6,11 @@ var u = require('./utils.js');
web3.setProvider(new web3.providers.WebSocketProvider('http://localhost:8080')); // TODO: create some mock provider
describe('web3', function() {
it('should have all methods implemented', function() {
u.methodExists(web3, 'sha3');
u.methodExists(web3, 'toAscii');
u.methodExists(web3, 'fromAscii');
u.methodExists(web3, 'toFixed');
u.methodExists(web3, 'fromFixed');
u.methodExists(web3, 'offset');
});
u.methodExists(web3, 'sha3');
u.methodExists(web3, 'toAscii');
u.methodExists(web3, 'fromAscii');
u.methodExists(web3, 'toFixed');
u.methodExists(web3, 'fromFixed');
u.methodExists(web3, 'offset');
});

1
libjsqrc/js.qrc

@ -1,6 +1,7 @@
<RCC>
<qresource prefix="/js">
<file>es6-promise-2.0.0.js</file>
<file>bignumber.min.js</file>
<file>setup.js</file>
<file alias="webthree.js">ethereumjs/dist/ethereum.js</file>
</qresource>

1
liblll/CodeFragment.cpp

@ -266,7 +266,6 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
}
++ii;
}
}
else if (us == "LIT")
{

1
liblll/CodeFragment.h

@ -61,3 +61,4 @@ static const CodeFragment NullCodeFragment;
}
}

1
libqwebthree/QWebThree.h

@ -85,6 +85,7 @@ private:
_frame->addToJavaScriptWindowObject("_web3", qweb, QWebFrame::ScriptOwnership); \
_frame->addToJavaScriptWindowObject("env", _env, QWebFrame::QtOwnership); \
_frame->evaluateJavaScript(contentsOfQResource(":/js/es6-promise-2.0.0.js")); \
_frame->evaluateJavaScript(contentsOfQResource(":/js/bignumber.min.js")); \
_frame->evaluateJavaScript(contentsOfQResource(":/js/webthree.js")); \
_frame->evaluateJavaScript(contentsOfQResource(":/js/setup.js")); \
}

68
libsolidity/AST.cpp

@ -43,6 +43,11 @@ TypeError ASTNode::createTypeError(string const& _description) const
void ContractDefinition::checkTypeRequirements()
{
for (ASTPointer<InheritanceSpecifier> const& base: getBaseContracts())
base->checkTypeRequirements();
checkIllegalOverrides();
FunctionDefinition const* constructor = getConstructor();
if (constructor && !constructor->getReturnParameters().empty())
BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError(
@ -52,7 +57,6 @@ void ContractDefinition::checkTypeRequirements()
function->checkTypeRequirements();
// check for hash collisions in function signatures
vector<pair<FixedHash<4>, FunctionDefinition const*>> exportedFunctionList = getInterfaceFunctionList();
set<FixedHash<4>> hashes;
for (auto const& hashAndFunction: getInterfaceFunctionList())
{
@ -78,22 +82,63 @@ map<FixedHash<4>, FunctionDefinition const*> ContractDefinition::getInterfaceFun
FunctionDefinition const* ContractDefinition::getConstructor() const
{
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
if (f->getName() == getName())
if (f->isConstructor())
return f.get();
return nullptr;
}
vector<pair<FixedHash<4>, FunctionDefinition const*>> ContractDefinition::getInterfaceFunctionList() const
void ContractDefinition::checkIllegalOverrides() const
{
vector<pair<FixedHash<4>, FunctionDefinition const*>> exportedFunctions;
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
if (f->isPublic() && f->getName() != getName())
map<string, FunctionDefinition const*> functions;
// We search from derived to base, so the stored item causes the error.
for (ContractDefinition const* contract: getLinearizedBaseContracts())
for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
{
FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
exportedFunctions.push_back(make_pair(hash, f.get()));
if (function->isConstructor())
continue; // constructors can neither be overriden nor override anything
FunctionDefinition const*& override = functions[function->getName()];
if (!override)
override = function.get();
else if (override->isPublic() != function->isPublic() ||
override->isDeclaredConst() != function->isDeclaredConst() ||
FunctionType(*override) != FunctionType(*function))
BOOST_THROW_EXCEPTION(override->createTypeError("Override changes extended function signature."));
}
}
return exportedFunctions;
vector<pair<FixedHash<4>, FunctionDefinition const*>> const& ContractDefinition::getInterfaceFunctionList() const
{
if (!m_interfaceFunctionList)
{
set<string> functionsSeen;
m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionDefinition const*>>());
for (ContractDefinition const* contract: getLinearizedBaseContracts())
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions())
if (f->isPublic() && !f->isConstructor() && functionsSeen.count(f->getName()) == 0)
{
functionsSeen.insert(f->getName());
FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
m_interfaceFunctionList->push_back(make_pair(hash, f.get()));
}
}
return *m_interfaceFunctionList;
}
void InheritanceSpecifier::checkTypeRequirements()
{
m_baseName->checkTypeRequirements();
for (ASTPointer<Expression> const& argument: m_arguments)
argument->checkTypeRequirements();
ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(m_baseName->getReferencedDeclaration());
solAssert(base, "Base contract not available.");
TypePointers parameterTypes = ContractType(*base).getConstructorType()->getParameterTypes();
if (parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for constructor call."));
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in constructer call."));
}
void StructDefinition::checkMemberTypes() const
@ -346,7 +391,8 @@ void MemberAccess::checkTypeRequirements()
Type const& type = *m_expression->getType();
m_type = type.getMemberType(*m_memberName);
if (!m_type)
BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found in " + type.toString()));
BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not "
"visible in " + type.toString()));
//@todo later, this will not always be STORAGE
m_lvalue = type.getCategory() == Type::Category::STRUCT ? LValueType::STORAGE : LValueType::NONE;
}
@ -396,7 +442,7 @@ void Identifier::checkTypeRequirements()
ContractDefinition const* contractDef = dynamic_cast<ContractDefinition const*>(m_referencedDeclaration);
if (contractDef)
{
m_type = make_shared<TypeType>(make_shared<ContractType>(*contractDef));
m_type = make_shared<TypeType>(make_shared<ContractType>(*contractDef), m_currentContract);
return;
}
MagicVariableDeclaration const* magicVariable = dynamic_cast<MagicVariableDeclaration const*>(m_referencedDeclaration);

64
libsolidity/AST.h

@ -158,10 +158,12 @@ public:
ContractDefinition(Location const& _location,
ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions):
Declaration(_location, _name),
m_baseContracts(_baseContracts),
m_definedStructs(_definedStructs),
m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions),
@ -171,12 +173,13 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<InheritanceSpecifier>> const& getBaseContracts() const { return m_baseContracts; }
std::vector<ASTPointer<StructDefinition>> const& getDefinedStructs() const { return m_definedStructs; }
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; }
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; }
/// Checks that the constructor does not have a "returns" statement and calls
/// checkTypeRequirements on all its functions.
/// Checks that there are no illegal overrides, that the constructor does not have a "returns"
/// and calls checkTypeRequirements on all its functions.
void checkTypeRequirements();
/// @return A shared pointer of an ASTString.
@ -187,16 +190,47 @@ public:
/// as intended for use by the ABI.
std::map<FixedHash<4>, FunctionDefinition const*> getInterfaceFunctions() const;
/// List of all (direct and indirect) base contracts in order from derived to base, including
/// the contract itself. Available after name resolution
std::vector<ContractDefinition const*> const& getLinearizedBaseContracts() const { return m_linearizedBaseContracts; }
void setLinearizedBaseContracts(std::vector<ContractDefinition const*> const& _bases) { m_linearizedBaseContracts = _bases; }
/// Returns the constructor or nullptr if no constructor was specified
FunctionDefinition const* getConstructor() const;
private:
std::vector<std::pair<FixedHash<4>, FunctionDefinition const*>> getInterfaceFunctionList() const;
void checkIllegalOverrides() const;
std::vector<std::pair<FixedHash<4>, FunctionDefinition const*>> const& getInterfaceFunctionList() const;
std::vector<ASTPointer<InheritanceSpecifier>> m_baseContracts;
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
ASTPointer<ASTString> m_documentation;
std::vector<ContractDefinition const*> m_linearizedBaseContracts;
mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionDefinition const*>>> m_interfaceFunctionList;
};
class InheritanceSpecifier: public ASTNode
{
public:
InheritanceSpecifier(Location const& _location, ASTPointer<Identifier> const& _baseName,
std::vector<ASTPointer<Expression>> _arguments):
ASTNode(_location), m_baseName(_baseName), m_arguments(_arguments) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
ASTPointer<Identifier> const& getName() const { return m_baseName; }
std::vector<ASTPointer<Expression>> const& getArguments() const { return m_arguments; }
void checkTypeRequirements();
private:
ASTPointer<Identifier> m_baseName;
std::vector<ASTPointer<Expression>> m_arguments;
};
class StructDefinition: public Declaration
@ -247,12 +281,13 @@ class FunctionDefinition: public Declaration
public:
FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name,
bool _isPublic,
bool _isConstructor,
ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
bool _isDeclaredConst,
ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body):
Declaration(_location, _name), m_isPublic(_isPublic),
Declaration(_location, _name), m_isPublic(_isPublic), m_isConstructor(_isConstructor),
m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst),
m_returnParameters(_returnParameters),
@ -264,6 +299,7 @@ public:
virtual void accept(ASTConstVisitor& _visitor) const override;
bool isPublic() const { return m_isPublic; }
bool isConstructor() const { return m_isConstructor; }
bool isDeclaredConst() const { return m_isDeclaredConst; }
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
ParameterList const& getParameterList() const { return *m_parameters; }
@ -287,6 +323,7 @@ public:
private:
bool m_isPublic;
bool m_isConstructor;
ASTPointer<ParameterList> m_parameters;
bool m_isDeclaredConst;
ASTPointer<ParameterList> m_returnParameters;
@ -581,7 +618,7 @@ public:
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; }
void setFunctionReturnParameters(ParameterList const& _parameters) { m_returnParameters = &_parameters; }
ParameterList const& getFunctionReturnParameters() const
{
solAssert(m_returnParameters, "");
@ -593,7 +630,7 @@ private:
ASTPointer<Expression> m_expression; ///< value to return, optional
/// Pointer to the parameter list of the function, filled by the @ref NameAndTypeResolver.
ParameterList* m_returnParameters;
ParameterList const* m_returnParameters;
};
/**
@ -870,21 +907,30 @@ class Identifier: public PrimaryExpression
{
public:
Identifier(Location const& _location, ASTPointer<ASTString> const& _name):
PrimaryExpression(_location), m_name(_name), m_referencedDeclaration(nullptr) {}
PrimaryExpression(_location), m_name(_name) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
ASTString const& getName() const { return *m_name; }
void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
void setReferencedDeclaration(Declaration const& _referencedDeclaration,
ContractDefinition const* _currentContract = nullptr)
{
m_referencedDeclaration = &_referencedDeclaration;
m_currentContract = _currentContract;
}
Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; }
ContractDefinition const* getCurrentContract() const { return m_currentContract; }
private:
ASTPointer<ASTString> m_name;
/// Declaration the name refers to.
Declaration const* m_referencedDeclaration;
Declaration const* m_referencedDeclaration = nullptr;
/// Stores a reference to the current contract. This is needed because types of base contracts
/// change depending on the context.
ContractDefinition const* m_currentContract = nullptr;
};
/**

1
libsolidity/ASTForward.h

@ -38,6 +38,7 @@ class SourceUnit;
class ImportDirective;
class Declaration;
class ContractDefinition;
class InheritanceSpecifier;
class StructDefinition;
class ParameterList;
class FunctionDefinition;

471
libsolidity/ASTJsonConverter.cpp

@ -0,0 +1,471 @@
/*
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/>.
*/
/**
* @author Lefteris <lefteris@ethdev.com>
* @date 2015
* Converts the AST into json format
*/
#include <libsolidity/ASTJsonConverter.h>
#include <libsolidity/AST.h>
using namespace std;
namespace dev
{
namespace solidity
{
void ASTJsonConverter::addKeyValue(Json::Value& _obj, string const& _key, string const& _val)
{
// special handling for booleans
if (_key == "const" || _key == "public" || _key == "local" ||
_key == "lvalue" || _key == "local_lvalue" || _key == "prefix")
_obj[_key] = (_val == "1") ? true : false;
else
// else simply add it as a string
_obj[_key] = _val;
}
void ASTJsonConverter::addJsonNode(string const& _nodeName,
initializer_list<pair<string const, string const>> _list,
bool _hasChildren = false)
{
Json::Value node;
node["name"] = _nodeName;
if (_list.size() != 0)
{
Json::Value attrs;
for (auto& e: _list)
addKeyValue(attrs, e.first, e.second);
node["attributes"] = attrs;
}
m_jsonNodePtrs.top()->append(node);
if (_hasChildren)
{
Json::Value& addedNode = (*m_jsonNodePtrs.top())[m_jsonNodePtrs.top()->size() - 1];
Json::Value children(Json::arrayValue);
addedNode["children"] = children;
m_jsonNodePtrs.push(&addedNode["children"]);
}
}
ASTJsonConverter::ASTJsonConverter(ASTNode const& _ast): m_ast(&_ast)
{
Json::Value children(Json::arrayValue);
m_astJson["name"] = "root";
m_astJson["children"] = children;
m_jsonNodePtrs.push(&m_astJson["children"]);
}
void ASTJsonConverter::print(ostream& _stream)
{
m_ast->accept(*this);
_stream << m_astJson;
}
bool ASTJsonConverter::visit(ImportDirective const& _node)
{
addJsonNode("Import", { make_pair("file", _node.getIdentifier())});
return true;
}
bool ASTJsonConverter::visit(ContractDefinition const& _node)
{
addJsonNode("Contract", { make_pair("name", _node.getName()) }, true);
return true;
}
bool ASTJsonConverter::visit(StructDefinition const& _node)
{
addJsonNode("Struct", { make_pair("name", _node.getName()) }, true);
return true;
}
bool ASTJsonConverter::visit(ParameterList const&)
{
addJsonNode("ParameterList", {}, true);
return true;
}
bool ASTJsonConverter::visit(FunctionDefinition const& _node)
{
addJsonNode("Function",
{ make_pair("name", _node.getName()),
make_pair("public", boost::lexical_cast<std::string>(_node.isPublic())),
make_pair("const", boost::lexical_cast<std::string>(_node.isDeclaredConst())) },
true);
return true;
}
bool ASTJsonConverter::visit(VariableDeclaration const& _node)
{
addJsonNode("VariableDeclaration",
{ make_pair("name", _node.getName()),
make_pair("local", boost::lexical_cast<std::string>(_node.isLocalVariable()))},
true);
return true;
}
bool ASTJsonConverter::visit(TypeName const&)
{
return true;
}
bool ASTJsonConverter::visit(ElementaryTypeName const& _node)
{
addJsonNode("ElementaryTypeName", { make_pair("name", Token::toString(_node.getTypeName())) });
return true;
}
bool ASTJsonConverter::visit(UserDefinedTypeName const& _node)
{
addJsonNode("UserDefinedTypeName", { make_pair("name", _node.getName()) });
return true;
}
bool ASTJsonConverter::visit(Mapping const&)
{
addJsonNode("Mapping", {}, true);
return true;
}
bool ASTJsonConverter::visit(Statement const&)
{
addJsonNode("Statement", {}, true);
return true;
}
bool ASTJsonConverter::visit(Block const&)
{
addJsonNode("Block", {}, true);
return true;
}
bool ASTJsonConverter::visit(IfStatement const&)
{
addJsonNode("IfStatement", {}, true);
return true;
}
bool ASTJsonConverter::visit(BreakableStatement const&)
{
return true;
}
bool ASTJsonConverter::visit(WhileStatement const&)
{
addJsonNode("WhileStatement", {}, true);
return true;
}
bool ASTJsonConverter::visit(ForStatement const&)
{
addJsonNode("ForStatement", {}, true);
return true;
}
bool ASTJsonConverter::visit(Continue const&)
{
addJsonNode("Continue", {});
return true;
}
bool ASTJsonConverter::visit(Break const&)
{
addJsonNode("Break", {});
return true;
}
bool ASTJsonConverter::visit(Return const&)
{
addJsonNode("Return", {}, true);;
return true;
}
bool ASTJsonConverter::visit(VariableDefinition const&)
{
addJsonNode("VariableDefinition", {}, true);
return true;
}
bool ASTJsonConverter::visit(ExpressionStatement const&)
{
addJsonNode("ExpressionStatement", {}, true);
return true;
}
bool ASTJsonConverter::visit(Expression const& _node)
{
addJsonNode("Expression",
{ make_pair("type", getType(_node)),
make_pair("lvalue", boost::lexical_cast<std::string>(_node.isLValue())),
make_pair("local_lvalue", boost::lexical_cast<std::string>(_node.isLocalLValue())) },
true);
return true;
}
bool ASTJsonConverter::visit(Assignment const& _node)
{
addJsonNode("Assignment",
{ make_pair("operator", Token::toString(_node.getAssignmentOperator())),
make_pair("type", getType(_node)) },
true);
return true;
}
bool ASTJsonConverter::visit(UnaryOperation const& _node)
{
addJsonNode("UnaryOperation",
{ make_pair("prefix", boost::lexical_cast<std::string>(_node.isPrefixOperation())),
make_pair("operator", Token::toString(_node.getOperator())),
make_pair("type", getType(_node)) },
true);
return true;
}
bool ASTJsonConverter::visit(BinaryOperation const& _node)
{
addJsonNode("BinaryOperation",
{ make_pair("operator", Token::toString(_node.getOperator())),
make_pair("type", getType(_node))},
true);
return true;
}
bool ASTJsonConverter::visit(FunctionCall const& _node)
{
addJsonNode("FunctionCall",
{ make_pair("type_conversion", boost::lexical_cast<std::string>(_node.isTypeConversion())),
make_pair("type", getType(_node)) },
true);
return true;
}
bool ASTJsonConverter::visit(NewExpression const& _node)
{
addJsonNode("NewExpression", { make_pair("type", getType(_node)) }, true);
return true;
}
bool ASTJsonConverter::visit(MemberAccess const& _node)
{
addJsonNode("MemberAccess",
{ make_pair("member_name", _node.getMemberName()),
make_pair("type", getType(_node)) },
true);
return true;
}
bool ASTJsonConverter::visit(IndexAccess const& _node)
{
addJsonNode("IndexAccess", { make_pair("type", getType(_node)) }, true);
return true;
}
bool ASTJsonConverter::visit(PrimaryExpression const&)
{
return true;
}
bool ASTJsonConverter::visit(Identifier const& _node)
{
addJsonNode("Identifier",
{ make_pair("value", _node.getName()), make_pair("type", getType(_node)) });
return true;
}
bool ASTJsonConverter::visit(ElementaryTypeNameExpression const& _node)
{
addJsonNode("ElementaryTypenameExpression",
{ make_pair("value", Token::toString(_node.getTypeToken())), make_pair("type", getType(_node)) });
return true;
}
bool ASTJsonConverter::visit(Literal const& _node)
{
char const* tokenString = Token::toString(_node.getToken());
addJsonNode("Literal",
{ make_pair("string", (tokenString) ? tokenString : "null"),
make_pair("value", _node.getValue()),
make_pair("type", getType(_node)) });
return true;
}
void ASTJsonConverter::endVisit(ImportDirective const&)
{
}
void ASTJsonConverter::endVisit(ContractDefinition const&)
{
goUp();
}
void ASTJsonConverter::endVisit(StructDefinition const&)
{
goUp();
}
void ASTJsonConverter::endVisit(ParameterList const&)
{
goUp();
}
void ASTJsonConverter::endVisit(FunctionDefinition const&)
{
goUp();
}
void ASTJsonConverter::endVisit(VariableDeclaration const&)
{
}
void ASTJsonConverter::endVisit(TypeName const&)
{
}
void ASTJsonConverter::endVisit(ElementaryTypeName const&)
{
}
void ASTJsonConverter::endVisit(UserDefinedTypeName const&)
{
}
void ASTJsonConverter::endVisit(Mapping const&)
{
}
void ASTJsonConverter::endVisit(Statement const&)
{
goUp();
}
void ASTJsonConverter::endVisit(Block const&)
{
goUp();
}
void ASTJsonConverter::endVisit(IfStatement const&)
{
goUp();
}
void ASTJsonConverter::endVisit(BreakableStatement const&)
{
}
void ASTJsonConverter::endVisit(WhileStatement const&)
{
goUp();
}
void ASTJsonConverter::endVisit(ForStatement const&)
{
goUp();
}
void ASTJsonConverter::endVisit(Continue const&)
{
}
void ASTJsonConverter::endVisit(Break const&)
{
}
void ASTJsonConverter::endVisit(Return const&)
{
goUp();
}
void ASTJsonConverter::endVisit(VariableDefinition const&)
{
goUp();
}
void ASTJsonConverter::endVisit(ExpressionStatement const&)
{
goUp();
}
void ASTJsonConverter::endVisit(Expression const&)
{
goUp();
}
void ASTJsonConverter::endVisit(Assignment const&)
{
goUp();
}
void ASTJsonConverter::endVisit(UnaryOperation const&)
{
goUp();
}
void ASTJsonConverter::endVisit(BinaryOperation const&)
{
goUp();
}
void ASTJsonConverter::endVisit(FunctionCall const&)
{
goUp();
}
void ASTJsonConverter::endVisit(NewExpression const&)
{
goUp();
}
void ASTJsonConverter::endVisit(MemberAccess const&)
{
goUp();
}
void ASTJsonConverter::endVisit(IndexAccess const&)
{
goUp();
}
void ASTJsonConverter::endVisit(PrimaryExpression const&)
{
}
void ASTJsonConverter::endVisit(Identifier const&)
{
}
void ASTJsonConverter::endVisit(ElementaryTypeNameExpression const&)
{
}
void ASTJsonConverter::endVisit(Literal const&)
{
}
string ASTJsonConverter::getType(Expression const& _expression)
{
return (_expression.getType()) ? _expression.getType()->toString() : "Unknown";
}
}
}

135
libsolidity/ASTJsonConverter.h

@ -0,0 +1,135 @@
/*
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/>.
*/
/**
* @author Lefteris <lefteris@ethdev.com>
* @date 2015
* Converts the AST into json format
*/
#pragma once
#include <ostream>
#include <stack>
#include <libsolidity/ASTVisitor.h>
#include <libsolidity/Exceptions.h>
#include <libsolidity/Utils.h>
#include <jsoncpp/json/json.h>
namespace dev
{
namespace solidity
{
/**
* Converter of the AST into JSON format
*/
class ASTJsonConverter: public ASTConstVisitor
{
public:
/// Create a converter to JSON for the given abstract syntax tree.
ASTJsonConverter(ASTNode const& _ast);
/// Output the json representation of the AST to _stream.
void print(std::ostream& _stream);
bool visit(ImportDirective const& _node) override;
bool visit(ContractDefinition const& _node) override;
bool visit(StructDefinition const& _node) override;
bool visit(ParameterList const& _node) override;
bool visit(FunctionDefinition const& _node) override;
bool visit(VariableDeclaration const& _node) override;
bool visit(TypeName const& _node) override;
bool visit(ElementaryTypeName const& _node) override;
bool visit(UserDefinedTypeName const& _node) override;
bool visit(Mapping const& _node) override;
bool visit(Statement const& _node) override;
bool visit(Block const& _node) override;
bool visit(IfStatement const& _node) override;
bool visit(BreakableStatement const& _node) override;
bool visit(WhileStatement const& _node) override;
bool visit(ForStatement const& _node) override;
bool visit(Continue const& _node) override;
bool visit(Break const& _node) override;
bool visit(Return const& _node) override;
bool visit(VariableDefinition const& _node) override;
bool visit(ExpressionStatement const& _node) override;
bool visit(Expression const& _node) override;
bool visit(Assignment const& _node) override;
bool visit(UnaryOperation const& _node) override;
bool visit(BinaryOperation const& _node) override;
bool visit(FunctionCall const& _node) override;
bool visit(NewExpression const& _node) override;
bool visit(MemberAccess const& _node) override;
bool visit(IndexAccess const& _node) override;
bool visit(PrimaryExpression const& _node) override;
bool visit(Identifier const& _node) override;
bool visit(ElementaryTypeNameExpression const& _node) override;
bool visit(Literal const& _node) override;
void endVisit(ImportDirective const&) override;
void endVisit(ContractDefinition const&) override;
void endVisit(StructDefinition const&) override;
void endVisit(ParameterList const&) override;
void endVisit(FunctionDefinition const&) override;
void endVisit(VariableDeclaration const&) override;
void endVisit(TypeName const&) override;
void endVisit(ElementaryTypeName const&) override;
void endVisit(UserDefinedTypeName const&) override;
void endVisit(Mapping const&) override;
void endVisit(Statement const&) override;
void endVisit(Block const&) override;
void endVisit(IfStatement const&) override;
void endVisit(BreakableStatement const&) override;
void endVisit(WhileStatement const&) override;
void endVisit(ForStatement const&) override;
void endVisit(Continue const&) override;
void endVisit(Break const&) override;
void endVisit(Return const&) override;
void endVisit(VariableDefinition const&) override;
void endVisit(ExpressionStatement const&) override;
void endVisit(Expression const&) override;
void endVisit(Assignment const&) override;
void endVisit(UnaryOperation const&) override;
void endVisit(BinaryOperation const&) override;
void endVisit(FunctionCall const&) override;
void endVisit(NewExpression const&) override;
void endVisit(MemberAccess const&) override;
void endVisit(IndexAccess const&) override;
void endVisit(PrimaryExpression const&) override;
void endVisit(Identifier const&) override;
void endVisit(ElementaryTypeNameExpression const&) override;
void endVisit(Literal const&) override;
private:
void addKeyValue(Json::Value& _obj, std::string const& _key, std::string const& _val);
void addJsonNode(std::string const& _nodeName,
std::initializer_list<std::pair<std::string const, std::string const>> _list,
bool _hasChildren);
std::string getType(Expression const& _expression);
inline void goUp()
{
solAssert(!m_jsonNodePtrs.empty(), "Uneven json nodes stack. Internal error.");
m_jsonNodePtrs.pop();
};
Json::Value m_astJson;
std::stack<Json::Value*> m_jsonNodePtrs;
std::string m_source;
ASTNode const* m_ast;
};
}
}

22
libsolidity/AST_accept.h

@ -61,6 +61,7 @@ void ContractDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
listAccept(m_baseContracts, _visitor);
listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor);
listAccept(m_definedFunctions, _visitor);
@ -72,6 +73,7 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
listAccept(m_baseContracts, _visitor);
listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor);
listAccept(m_definedFunctions, _visitor);
@ -79,6 +81,26 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this);
}
void InheritanceSpecifier::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_baseName->accept(_visitor);
listAccept(m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
void InheritanceSpecifier::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_baseName->accept(_visitor);
listAccept(m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
void StructDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))

53
libsolidity/CallGraph.cpp

@ -31,17 +31,14 @@ namespace dev
namespace solidity
{
void CallGraph::addFunction(FunctionDefinition const& _function)
void CallGraph::addNode(ASTNode const& _node)
{
if (!m_functionsSeen.count(&_function))
{
m_functionsSeen.insert(&_function);
m_workQueue.push(&_function);
}
_node.accept(*this);
}
set<FunctionDefinition const*> const& CallGraph::getCalls()
{
computeCallGraph();
return m_functionsSeen;
}
@ -49,8 +46,7 @@ void CallGraph::computeCallGraph()
{
while (!m_workQueue.empty())
{
FunctionDefinition const* fun = m_workQueue.front();
fun->accept(*this);
m_workQueue.front()->accept(*this);
m_workQueue.pop();
}
}
@ -59,9 +55,50 @@ bool CallGraph::visit(Identifier const& _identifier)
{
FunctionDefinition const* fun = dynamic_cast<FunctionDefinition const*>(_identifier.getReferencedDeclaration());
if (fun)
{
if (m_overrideResolver)
fun = (*m_overrideResolver)(fun->getName());
solAssert(fun, "Error finding override for function " + fun->getName());
addFunction(*fun);
}
return true;
}
bool CallGraph::visit(FunctionDefinition const& _function)
{
addFunction(_function);
return true;
}
bool CallGraph::visit(MemberAccess const& _memberAccess)
{
// used for "BaseContract.baseContractFunction"
if (_memberAccess.getExpression().getType()->getCategory() == Type::Category::TYPE)
{
TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.getExpression().getType());
if (type.getMembers().getMemberType(_memberAccess.getMemberName()))
{
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*type.getActualType())
.getContractDefinition();
for (ASTPointer<FunctionDefinition> const& function: contract.getDefinedFunctions())
if (function->getName() == _memberAccess.getMemberName())
{
addFunction(*function);
return true;
}
}
}
return true;
}
void CallGraph::addFunction(FunctionDefinition const& _function)
{
if (!m_functionsSeen.count(&_function))
{
m_functionsSeen.insert(&_function);
m_workQueue.push(&_function);
}
}
}
}

15
libsolidity/CallGraph.h

@ -22,6 +22,7 @@
#include <set>
#include <queue>
#include <functional>
#include <boost/range/iterator_range.hpp>
#include <libsolidity/ASTVisitor.h>
@ -38,15 +39,23 @@ namespace solidity
class CallGraph: private ASTConstVisitor
{
public:
void addFunction(FunctionDefinition const& _function);
void computeCallGraph();
using OverrideResolver = std::function<FunctionDefinition const*(std::string const&)>;
CallGraph(OverrideResolver const& _overrideResolver): m_overrideResolver(&_overrideResolver) {}
void addNode(ASTNode const& _node);
std::set<FunctionDefinition const*> const& getCalls();
private:
void addFunctionToQueue(FunctionDefinition const& _function);
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(Identifier const& _identifier) override;
virtual bool visit(MemberAccess const& _memberAccess) override;
void computeCallGraph();
void addFunction(FunctionDefinition const& _function);
OverrideResolver const* m_overrideResolver;
std::set<FunctionDefinition const*> m_functionsSeen;
std::queue<FunctionDefinition const*> m_workQueue;
};

122
libsolidity/Compiler.cpp

@ -21,6 +21,7 @@
*/
#include <algorithm>
#include <boost/range/adaptor/reversed.hpp>
#include <libevmcore/Instruction.h>
#include <libevmcore/Assembly.h>
#include <libsolidity/AST.h>
@ -34,48 +35,103 @@ using namespace std;
namespace dev {
namespace solidity {
void Compiler::compileContract(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals,
void Compiler::compileContract(ContractDefinition const& _contract,
map<ContractDefinition const*, bytes const*> const& _contracts)
{
m_context = CompilerContext(); // clear it just in case
initializeContext(_contract, _magicGlobals, _contracts);
initializeContext(_contract, _contracts);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
if (function->getName() != _contract.getName()) // don't add the constructor here
m_context.addFunction(*function);
for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts())
for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
if (!function->isConstructor())
m_context.addFunction(*function);
appendFunctionSelector(_contract);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
if (function->getName() != _contract.getName()) // don't add the constructor here
function->accept(*this);
for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts())
for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
if (!function->isConstructor())
function->accept(*this);
// Swap the runtime context with the creation-time context
swap(m_context, m_runtimeContext);
initializeContext(_contract, _magicGlobals, _contracts);
initializeContext(_contract, _contracts);
packIntoContractCreator(_contract, m_runtimeContext);
}
void Compiler::initializeContext(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals,
void Compiler::initializeContext(ContractDefinition const& _contract,
map<ContractDefinition const*, bytes const*> const& _contracts)
{
m_context.setCompiledContracts(_contracts);
for (MagicVariableDeclaration const* variable: _magicGlobals)
m_context.addMagicGlobal(*variable);
registerStateVariables(_contract);
}
void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext)
{
// arguments for base constructors, filled in derived-to-base order
map<ContractDefinition const*, vector<ASTPointer<Expression>> const*> baseArguments;
set<FunctionDefinition const*> neededFunctions;
FunctionDefinition const* constructor = _contract.getConstructor();
if (constructor)
neededFunctions = getFunctionsNeededByConstructor(*constructor);
set<ASTNode const*> nodesUsedInConstructors;
// Determine the arguments that are used for the base constructors and also which functions
// are needed at compile time.
std::vector<ContractDefinition const*> const& bases = _contract.getLinearizedBaseContracts();
for (ContractDefinition const* contract: bases)
{
if (FunctionDefinition const* constructor = contract->getConstructor())
nodesUsedInConstructors.insert(constructor);
for (ASTPointer<InheritanceSpecifier> const& base: contract->getBaseContracts())
{
ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>(
base->getName()->getReferencedDeclaration());
solAssert(baseContract, "");
if (baseArguments.count(baseContract) == 0)
{
baseArguments[baseContract] = &base->getArguments();
for (ASTPointer<Expression> const& arg: base->getArguments())
nodesUsedInConstructors.insert(arg.get());
}
}
}
auto overrideResolver = [&](string const& _name) -> FunctionDefinition const*
{
for (ContractDefinition const* contract: bases)
for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
if (!function->isConstructor() && function->getName() == _name)
return function.get();
return nullptr;
};
neededFunctions = getFunctionsCalled(nodesUsedInConstructors, overrideResolver);
// First add all overrides (or the functions themselves if there is no override)
for (FunctionDefinition const* fun: neededFunctions)
m_context.addFunction(*fun);
{
FunctionDefinition const* override = nullptr;
if (!fun->isConstructor())
override = overrideResolver(fun->getName());
if (!!override && neededFunctions.count(override))
m_context.addFunction(*override);
}
// now add the rest
for (FunctionDefinition const* fun: neededFunctions)
if (fun->isConstructor() || overrideResolver(fun->getName()) != fun)
m_context.addFunction(*fun);
if (constructor)
appendConstructorCall(*constructor);
// Call constructors in base-to-derived order.
// The Constructor for the most derived contract is called later.
for (unsigned i = 1; i < bases.size(); i++)
{
ContractDefinition const* base = bases[bases.size() - i];
solAssert(base, "");
FunctionDefinition const* baseConstructor = base->getConstructor();
if (!baseConstructor)
continue;
solAssert(baseArguments[base], "");
appendBaseConstructorCall(*baseConstructor, *baseArguments[base]);
}
if (_contract.getConstructor())
appendConstructorCall(*_contract.getConstructor());
eth::AssemblyItem sub = m_context.addSubroutine(_runtimeContext.getAssembly());
// stack contains sub size
@ -88,6 +144,21 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp
fun->accept(*this);
}
void Compiler::appendBaseConstructorCall(FunctionDefinition const& _constructor,
vector<ASTPointer<Expression>> const& _arguments)
{
FunctionType constructorType(_constructor);
eth::AssemblyItem returnLabel = m_context.pushNewTag();
for (unsigned i = 0; i < _arguments.size(); ++i)
{
compileExpression(*_arguments[i]);
ExpressionCompiler::appendTypeConversion(m_context, *_arguments[i]->getType(),
*constructorType.getParameterTypes()[i]);
}
m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor));
m_context << returnLabel;
}
void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
{
eth::AssemblyItem returnTag = m_context.pushNewTag();
@ -107,11 +178,12 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
m_context << returnTag;
}
set<FunctionDefinition const*> Compiler::getFunctionsNeededByConstructor(FunctionDefinition const& _constructor)
set<FunctionDefinition const*> Compiler::getFunctionsCalled(set<ASTNode const*> const& _nodes,
function<FunctionDefinition const*(string const&)> const& _resolveOverrides)
{
CallGraph callgraph;
callgraph.addFunction(_constructor);
callgraph.computeCallGraph();
CallGraph callgraph(_resolveOverrides);
for (ASTNode const* node: _nodes)
callgraph.addNode(*node);
return callgraph.getCalls();
}
@ -193,9 +265,9 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
void Compiler::registerStateVariables(ContractDefinition const& _contract)
{
//@todo sort them?
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
m_context.addStateVariable(*variable);
for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.getLinearizedBaseContracts()))
for (ASTPointer<VariableDeclaration> const& variable: contract->getStateVariables())
m_context.addStateVariable(*variable);
}
bool Compiler::visit(FunctionDefinition const& _function)

18
libsolidity/Compiler.h

@ -21,6 +21,7 @@
*/
#include <ostream>
#include <functional>
#include <libsolidity/ASTVisitor.h>
#include <libsolidity/CompilerContext.h>
@ -32,23 +33,26 @@ class Compiler: private ASTConstVisitor
public:
explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_context(), m_returnTag(m_context.newTag()) {}
void compileContract(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals,
void compileContract(ContractDefinition const& _contract,
std::map<ContractDefinition const*, bytes const*> const& _contracts);
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); }
bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);}
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
private:
/// Registers the global objects and the non-function objects inside the contract with the context.
void initializeContext(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals,
/// Registers the non-function objects inside the contract with the context.
void initializeContext(ContractDefinition const& _contract,
std::map<ContractDefinition const*, bytes const*> const& _contracts);
/// Adds the code that is run at creation time. Should be run after exchanging the run-time context
/// with a new and initialized context.
/// adds the constructor code.
/// with a new and initialized context. Adds the constructor code.
void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext);
void appendBaseConstructorCall(FunctionDefinition const& _constructor,
std::vector<ASTPointer<Expression>> const& _arguments);
void appendConstructorCall(FunctionDefinition const& _constructor);
/// Recursively searches the call graph and returns all functions needed by the constructor (including itself).
std::set<FunctionDefinition const*> getFunctionsNeededByConstructor(FunctionDefinition const& _constructor);
/// Recursively searches the call graph and returns all functions referenced inside _nodes.
/// _resolveOverride is called to resolve virtual function overrides.
std::set<FunctionDefinition const*> getFunctionsCalled(std::set<ASTNode const*> const& _nodes,
std::function<FunctionDefinition const*(std::string const&)> const& _resolveOverride);
void appendFunctionSelector(ContractDefinition const& _contract);
/// Creates code that unpacks the arguments for the given function, from memory if
/// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes.

11
libsolidity/CompilerContext.cpp

@ -61,7 +61,9 @@ void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _decla
void CompilerContext::addFunction(FunctionDefinition const& _function)
{
m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag()));
eth::AssemblyItem tag(m_asm.newTag());
m_functionEntryLabels.insert(make_pair(&_function, tag));
m_virtualFunctionEntryLabels.insert(make_pair(_function.getName(), tag));
}
bytes const& CompilerContext::getCompiledContract(const ContractDefinition& _contract) const
@ -83,6 +85,13 @@ eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition cons
return res->second.tag();
}
eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefinition const& _function) const
{
auto res = m_virtualFunctionEntryLabels.find(_function.getName());
solAssert(res != m_virtualFunctionEntryLabels.end(), "Function entry label not found.");
return res->second.tag();
}
unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const
{
auto res = m_localVariables.find(&_declaration);

4
libsolidity/CompilerContext.h

@ -57,6 +57,8 @@ public:
bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration) != 0; }
eth::AssemblyItem getFunctionEntryLabel(FunctionDefinition const& _function) const;
/// @returns the entry label of the given function and takes overrides into account.
eth::AssemblyItem getVirtualFunctionEntryLabel(FunctionDefinition const& _function) const;
/// Returns the distance of the given local variable from the top of the local variable stack.
unsigned getBaseStackOffsetOfVariable(Declaration const& _declaration) const;
/// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns
@ -116,6 +118,8 @@ private:
unsigned m_localVariablesSize;
/// Labels pointing to the entry points of funcitons.
std::map<Declaration const*, eth::AssemblyItem> m_functionEntryLabels;
/// Labels pointing to the entry points of function overrides.
std::map<std::string, eth::AssemblyItem> m_virtualFunctionEntryLabels;
};
}

47
libsolidity/CompilerStack.cpp

@ -21,6 +21,7 @@
* Full-stack compiler that converts a source code string to bytecode.
*/
#include <boost/algorithm/string.hpp>
#include <libsolidity/AST.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
@ -113,10 +114,8 @@ void CompilerStack::compile(bool _optimize)
for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
m_globalContext->setCurrentContract(*contract);
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
compiler->compileContract(*contract, m_globalContext->getMagicVariables(),
contractBytecode);
compiler->compileContract(*contract, contractBytecode);
Contract& compiledContract = m_contracts[contract->getName()];
compiledContract.bytecode = compiler->getAssembledBytecode();
compiledContract.runtimeBytecode = compiler->getRuntimeBytecode();
@ -125,9 +124,49 @@ void CompilerStack::compile(bool _optimize)
}
}
string CompilerStack::expanded(string const& _sourceCode)
{
// TODO: populate some nicer way.
static const map<string, string> c_requires = {
{ "Config", "contract Config{function lookup(uint256 service)constant returns(address a){}function kill(){}function unregister(uint256 id){}function register(uint256 id,address service){}}" },
{ "owned", "contract owned{function owned(){owner = msg.sender;}address owner;}" },
{ "mortal", "#require owned\ncontract mortal is owned {function kill() { if (msg.sender == owner) suicide(owner); }}" },
{ "NameReg", "contract NameReg{function register(string32 name){}function addressOf(string32 name)constant returns(address addr){}function unregister(){}function nameOf(address addr)constant returns(string32 name){}}" },
{ "named", "#require Config NameReg\ncontract named is mortal, owned {function named(string32 name) {NameReg(Config().lookup(1)).register(name);}" },
{ "std", "#require owned mortal Config NameReg named" },
};
string sub;
set<string> got;
function<string(string const&)> localExpanded;
localExpanded = [&](string const& s) -> string
{
string ret = s;
for (size_t p = 0; p != string::npos;)
if ((p = ret.find("#require ")) != string::npos)
{
string n = ret.substr(p + 9, ret.find_first_of('\n', p + 9) - p - 9);
ret.replace(p, n.size() + 9, "");
vector<string> rs;
boost::split(rs, n, boost::is_any_of(" \t,"), boost::token_compress_on);
for (auto const& r: rs)
if (!got.count(r))
{
if (c_requires.count(r))
sub.append("\n" + localExpanded(c_requires.at(r)) + "\n");
got.insert(r);
}
}
// TODO: remove once we have genesis contracts.
else if ((p = ret.find("Config()")) != string::npos)
ret.replace(p, 8, "Config(0x661005d2720d855f1d9976f88bb10c1a3398c77f)");
return ret;
};
return sub + localExpanded(_sourceCode);
}
bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
{
parse(_sourceCode);
parse(expanded(_sourceCode));
compile(_optimize);
return getBytecode();
}

4
libsolidity/CompilerStack.h

@ -138,6 +138,10 @@ private:
Contract();
};
/// Expand source code with preprocessor-like includes.
/// @todo Replace with better framework.
std::string expanded(std::string const& _sourceCode);
void reset(bool _keepSources = false);
void resolveImports();

5
libsolidity/DeclarationContainer.h

@ -42,11 +42,12 @@ public:
explicit DeclarationContainer(Declaration const* _enclosingDeclaration = nullptr,
DeclarationContainer const* _enclosingContainer = nullptr):
m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {}
/// Registers the declaration in the scope unless its name is already declared. Returns true iff
/// it was not yet declared.
/// Registers the declaration in the scope unless its name is already declared.
/// @returns true iff it was not yet declared.
bool registerDeclaration(Declaration const& _declaration, bool _update = false);
Declaration const* resolveName(ASTString const& _name, bool _recursive = false) const;
Declaration const* getEnclosingDeclaration() const { return m_enclosingDeclaration; }
std::map<ASTString, Declaration const*> const& getDeclarations() const { return m_declarations; }
private:
Declaration const* m_enclosingDeclaration;

130
libsolidity/ExpressionCompiler.cpp

@ -94,13 +94,8 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
m_context << eth::Instruction::NOT;
break;
case Token::DELETE: // delete
// @todo semantics change for complex types
solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
m_context << u256(0);
if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1;
m_currentLValue.storeValue(_unaryOperation);
m_currentLValue.setToZero(_unaryOperation);
m_currentLValue.reset();
break;
case Token::INC: // ++ (pre- or postfix)
@ -397,9 +392,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
m_context << eth::Instruction::COINBASE;
else if (member == "timestamp")
m_context << eth::Instruction::TIMESTAMP;
/* else if (member == "blockhash")
m_context << eth::Instruction::BLOCKHASH;
*/ else if (member == "difficulty")
else if (member == "difficulty")
m_context << eth::Instruction::DIFFICULTY;
else if (member == "number")
m_context << eth::Instruction::NUMBER;
@ -426,6 +419,22 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
break;
}
case Type::Category::TYPE:
{
TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.getExpression().getType());
if (type.getMembers().getMemberType(member))
{
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*type.getActualType())
.getContractDefinition();
for (ASTPointer<FunctionDefinition> const& function: contract.getDefinedFunctions())
if (function->getName() == member)
{
m_context << m_context.getFunctionEntryLabel(*function).pushTag();
return;
}
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to " + type.toString()));
}
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to unknown type."));
}
@ -456,20 +465,22 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
{
if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) // must be "this"
m_context << eth::Instruction::ADDRESS;
return;
}
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
{
m_context << m_context.getFunctionEntryLabel(*functionDef).pushTag();
return;
}
if (dynamic_cast<VariableDeclaration const*>(declaration))
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag();
else if (dynamic_cast<VariableDeclaration const*>(declaration))
{
m_currentLValue.fromIdentifier(_identifier, *declaration);
m_currentLValue.retrieveValueIfLValueNotRequested(_identifier);
return;
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context."));
else if (dynamic_cast<ContractDefinition const*>(declaration))
{
// no-op
}
else
{
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context."));
}
}
void ExpressionCompiler::endVisit(Literal const& _literal)
@ -753,9 +764,14 @@ unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _typ
ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,
unsigned _baseStackOffset):
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset),
m_stackSize(_dataType.getSizeOnStack())
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset)
{
//@todo change the type cast for arrays
solAssert(_dataType.getStorageSize() <= numeric_limits<unsigned>::max(), "The storage size of " +_dataType.toString() + " should fit in unsigned");
if (m_type == STORAGE)
m_size = unsigned(_dataType.getStorageSize());
else
m_size = unsigned(_dataType.getSizeOnStack());
}
void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bool _remove) const
@ -768,7 +784,7 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep."));
for (unsigned i = 0; i < m_stackSize; ++i)
for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::dupInstruction(stackPos + 1);
break;
}
@ -777,14 +793,14 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
break; // no distinction between value and reference for non-value types
if (!_remove)
*m_context << eth::Instruction::DUP1;
if (m_stackSize == 1)
if (m_size == 1)
*m_context << eth::Instruction::SLOAD;
else
for (unsigned i = 0; i < m_stackSize; ++i)
for (unsigned i = 0; i < m_size; ++i)
{
*m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1;
if (i + 1 < m_stackSize)
*m_context << u256(1) << eth::Instruction::ADD;
if (i + 1 < m_size)
*m_context << u256(1) << eth::Instruction::ADD;
else
*m_context << eth::Instruction::POP;
}
@ -808,12 +824,12 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
{
case STACK:
{
unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_stackSize + 1;
unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_size + 1;
if (stackDiff > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep."));
else if (stackDiff > 0)
for (unsigned i = 0; i < m_stackSize; ++i)
for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP;
if (!_move)
retrieveValue(_expression);
@ -825,17 +841,17 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
// stack layout: value value ... value ref
if (!_move) // copy values
{
if (m_stackSize + 1 > 16)
if (m_size + 1 > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep."));
for (unsigned i = 0; i < m_stackSize; ++i)
*m_context << eth::dupInstruction(m_stackSize + 1) << eth::Instruction::SWAP1;
for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::dupInstruction(m_size + 1) << eth::Instruction::SWAP1;
}
if (m_stackSize > 0) // store high index value first
*m_context << u256(m_stackSize - 1) << eth::Instruction::ADD;
for (unsigned i = 0; i < m_stackSize; ++i)
if (m_size > 0) // store high index value first
*m_context << u256(m_size - 1) << eth::Instruction::ADD;
for (unsigned i = 0; i < m_size; ++i)
{
if (i + 1 >= m_stackSize)
if (i + 1 >= m_size)
*m_context << eth::Instruction::SSTORE;
else
// v v ... v v r+x
@ -857,6 +873,48 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
}
}
void ExpressionCompiler::LValue::setToZero(Expression const& _expression) const
{
switch (m_type)
{
case STACK:
{
unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset));
if (stackDiff > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep."));
solAssert(stackDiff >= m_size - 1, "");
for (unsigned i = 0; i < m_size; ++i)
*m_context << u256(0) << eth::swapInstruction(stackDiff + 1 - i)
<< eth::Instruction::POP;
break;
}
case LValue::STORAGE:
if (m_size == 0)
*m_context << eth::Instruction::POP;
for (unsigned i = 0; i < m_size; ++i)
{
if (i + 1 >= m_size)
*m_context << u256(0) << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
else
*m_context << u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE
<< u256(1) << eth::Instruction::ADD;
}
break;
case LValue::MEMORY:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Location type not yet implemented."));
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Unsupported location type."));
break;
}
}
void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression const& _expression)
{
if (!_expression.lvalueRequested())
@ -868,15 +926,17 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co
void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration)
{
m_stackSize = _identifier.getType()->getSizeOnStack();
if (m_context->isLocalVariable(&_declaration))
{
m_type = STACK;
m_size = _identifier.getType()->getSizeOnStack();
m_baseStackOffset = m_context->getBaseStackOffsetOfVariable(_declaration);
}
else if (m_context->isStateVariable(&_declaration))
{
m_type = STORAGE;
solAssert(_identifier.getType()->getStorageSize() <= numeric_limits<unsigned>::max(), "The storage size of " + _identifier.getType()->toString() + " should fit in unsigned");
m_size = unsigned(_identifier.getType()->getStorageSize());
*m_context << m_context->getStorageLocationOfVariable(_declaration);
}
else

10
libsolidity/ExpressionCompiler.h

@ -111,7 +111,7 @@ private:
/// Set type according to the declaration and retrieve the reference.
/// @a _expression is the current expression
void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration);
void reset() { m_type = NONE; m_baseStackOffset = 0; }
void reset() { m_type = NONE; m_baseStackOffset = 0; m_size = 0; }
bool isValid() const { return m_type != NONE; }
bool isInOnStack() const { return m_type == STACK; }
@ -130,7 +130,9 @@ private:
/// Also removes the stored value from the stack if @a _move is
/// true. @a _expression is the current expression, used for error reporting.
void storeValue(Expression const& _expression, bool _move = false) const;
/// Stores zero in the lvalue.
/// @a _expression is the current expression, used for error reporting.
void setToZero(Expression const& _expression) const;
/// Convenience function to convert the stored reference to a value and reset type to NONE if
/// the reference was not requested by @a _expression.
void retrieveValueIfLValueNotRequested(Expression const& _expression);
@ -141,8 +143,8 @@ private:
/// If m_type is STACK, this is base stack offset (@see
/// CompilerContext::getBaseStackOffsetOfVariable) of a local variable.
unsigned m_baseStackOffset = 0;
/// Size of the value of this lvalue on the stack.
unsigned m_stackSize = 0;
/// Size of the value of this lvalue on the stack or the storage.
unsigned m_size = 0;
};
bool m_optimize;

12
libsolidity/GlobalContext.cpp

@ -68,7 +68,7 @@ void GlobalContext::setCurrentContract(ContractDefinition const& _contract)
vector<Declaration const*> GlobalContext::getDeclarations() const
{
vector<Declaration const*> declarations;
declarations.reserve(m_magicVariables.size() + 1);
declarations.reserve(m_magicVariables.size());
for (ASTPointer<Declaration const> const& variable: m_magicVariables)
declarations.push_back(variable.get());
return declarations;
@ -83,15 +83,5 @@ MagicVariableDeclaration const* GlobalContext::getCurrentThis() const
}
vector<MagicVariableDeclaration const*> GlobalContext::getMagicVariables() const
{
vector<MagicVariableDeclaration const*> declarations;
declarations.reserve(m_magicVariables.size() + 1);
for (ASTPointer<MagicVariableDeclaration const> const& variable: m_magicVariables)
declarations.push_back(variable.get());
declarations.push_back(getCurrentThis());
return declarations;
}
}
}

2
libsolidity/GlobalContext.h

@ -49,8 +49,6 @@ public:
void setCurrentContract(ContractDefinition const& _contract);
MagicVariableDeclaration const* getCurrentThis() const;
/// @returns all magic variables.
std::vector<MagicVariableDeclaration const*> getMagicVariables() const;
/// @returns a vector of all implicit global declarations excluding "this".
std::vector<Declaration const*> getDeclarations() const;

10
libsolidity/InterfaceHandler.cpp

@ -351,9 +351,15 @@ void InterfaceHandler::parseDocString(std::string const& _string, CommentOwner _
currPos = appendDocTag(currPos, end, _owner);
else if (currPos != end)
{
if (nlPos == end) //end of text
// if it begins without a tag then consider it as @notice
if (currPos == _string.begin())
{
currPos = parseDocTag(currPos, end, "notice", CommentOwner::FUNCTION);
continue;
}
else if (nlPos == end) //end of text
return;
// else skip the line if a newline was found
// else skip the line if a newline was found and we get here
currPos = nlPos + 1;
}
}

116
libsolidity/NameAndTypeResolver.cpp

@ -31,7 +31,6 @@ namespace dev
namespace solidity
{
NameAndTypeResolver::NameAndTypeResolver(std::vector<Declaration const*> const& _globals)
{
for (Declaration const* declaration: _globals)
@ -46,18 +45,27 @@ void NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit)
void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
{
m_currentScope = &m_scopes[nullptr];
for (ASTPointer<InheritanceSpecifier> const& baseContract: _contract.getBaseContracts())
ReferencesResolver resolver(*baseContract, *this, &_contract, nullptr);
m_currentScope = &m_scopes[&_contract];
linearizeBaseContracts(_contract);
for (ContractDefinition const* base: _contract.getLinearizedBaseContracts())
importInheritedScope(*base);
for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs())
ReferencesResolver resolver(*structDef, *this, nullptr);
ReferencesResolver resolver(*structDef, *this, &_contract, nullptr);
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
ReferencesResolver resolver(*variable, *this, nullptr);
ReferencesResolver resolver(*variable, *this, &_contract, nullptr);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
{
m_currentScope = &m_scopes[function.get()];
ReferencesResolver referencesResolver(*function, *this,
ReferencesResolver referencesResolver(*function, *this, &_contract,
function->getReturnParameterList().get());
}
m_currentScope = &m_scopes[nullptr];
}
void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract)
@ -86,6 +94,96 @@ Declaration const* NameAndTypeResolver::getNameFromCurrentScope(ASTString const&
return m_currentScope->resolveName(_name, _recursive);
}
void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
{
auto iterator = m_scopes.find(&_base);
solAssert(iterator != end(m_scopes), "");
for (auto const& nameAndDeclaration: iterator->second.getDeclarations())
{
Declaration const* declaration = nameAndDeclaration.second;
// Import if it was declared in the base and is not the constructor
if (declaration->getScope() == &_base && declaration->getName() != _base.getName())
m_currentScope->registerDeclaration(*declaration);
}
}
void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) const
{
// order in the lists is from derived to base
// list of lists to linearize, the last element is the list of direct bases
list<list<ContractDefinition const*>> input(1, {&_contract});
for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: _contract.getBaseContracts())
{
ASTPointer<Identifier> baseName = baseSpecifier->getName();
ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(
baseName->getReferencedDeclaration());
if (!base)
BOOST_THROW_EXCEPTION(baseName->createTypeError("Contract expected."));
// "push_back" has the effect that bases mentioned earlier can overwrite members of bases
// mentioned later
input.back().push_back(base);
vector<ContractDefinition const*> const& basesBases = base->getLinearizedBaseContracts();
if (basesBases.empty())
BOOST_THROW_EXCEPTION(baseName->createTypeError("Definition of base has to precede definition of derived contract"));
input.push_front(list<ContractDefinition const*>(basesBases.begin(), basesBases.end()));
}
vector<ContractDefinition const*> result = cThreeMerge(input);
if (result.empty())
BOOST_THROW_EXCEPTION(_contract.createTypeError("Linearization of inheritance graph impossible"));
_contract.setLinearizedBaseContracts(result);
}
template <class _T>
vector<_T const*> NameAndTypeResolver::cThreeMerge(list<list<_T const*>>& _toMerge)
{
// returns true iff _candidate appears only as last element of the lists
auto appearsOnlyAtHead = [&](_T const* _candidate) -> bool
{
for (list<_T const*> const& bases: _toMerge)
{
solAssert(!bases.empty(), "");
if (find(++bases.begin(), bases.end(), _candidate) != bases.end())
return false;
}
return true;
};
// returns the next candidate to append to the linearized list or nullptr on failure
auto nextCandidate = [&]() -> _T const*
{
for (list<_T const*> const& bases: _toMerge)
{
solAssert(!bases.empty(), "");
if (appearsOnlyAtHead(bases.front()))
return bases.front();
}
return nullptr;
};
// removes the given contract from all lists
auto removeCandidate = [&](_T const* _candidate)
{
for (auto it = _toMerge.begin(); it != _toMerge.end();)
{
it->remove(_candidate);
if (it->empty())
it = _toMerge.erase(it);
else
++it;
}
};
_toMerge.remove_if([](list<_T const*> const& _bases) { return _bases.empty(); });
vector<_T const*> result;
while (!_toMerge.empty())
{
_T const* candidate = nextCandidate();
if (!candidate)
return vector<_T const*>();
result.push_back(candidate);
removeCandidate(candidate);
}
return result;
}
DeclarationRegistrationHelper::DeclarationRegistrationHelper(map<ASTNode const*, DeclarationContainer>& _scopes,
ASTNode& _astRoot):
m_scopes(_scopes), m_currentScope(nullptr)
@ -169,8 +267,10 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio
}
ReferencesResolver::ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver,
ParameterList* _returnParameters, bool _allowLazyTypes):
m_resolver(_resolver), m_returnParameters(_returnParameters), m_allowLazyTypes(_allowLazyTypes)
ContractDefinition const* _currentContract,
ParameterList const* _returnParameters, bool _allowLazyTypes):
m_resolver(_resolver), m_currentContract(_currentContract),
m_returnParameters(_returnParameters), m_allowLazyTypes(_allowLazyTypes)
{
_root.accept(*this);
}
@ -218,7 +318,7 @@ bool ReferencesResolver::visit(Identifier& _identifier)
if (!declaration)
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation())
<< errinfo_comment("Undeclared identifier."));
_identifier.setReferencedDeclaration(*declaration);
_identifier.setReferencedDeclaration(*declaration, m_currentContract);
return false;
}

19
libsolidity/NameAndTypeResolver.h

@ -23,6 +23,7 @@
#pragma once
#include <map>
#include <list>
#include <boost/noncopyable.hpp>
#include <libsolidity/DeclarationContainer.h>
@ -64,6 +65,17 @@ public:
private:
void reset();
/// Imports all members declared directly in the given contract (i.e. does not import inherited
/// members) into the current scope if they are not present already.
void importInheritedScope(ContractDefinition const& _base);
/// Computes "C3-Linearization" of base contracts and stores it inside the contract.
void linearizeBaseContracts(ContractDefinition& _contract) const;
/// Computes the C3-merge of the given list of lists of bases.
/// @returns the linearized vector or an empty vector if linearization is not possible.
template <class _T>
static std::vector<_T const*> cThreeMerge(std::list<std::list<_T const*>>& _toMerge);
/// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration,
/// where nullptr denotes the global scope. Note that structs are not scope since they do
/// not contain code.
@ -108,7 +120,9 @@ class ReferencesResolver: private ASTVisitor
{
public:
ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver,
ParameterList* _returnParameters, bool _allowLazyTypes = true);
ContractDefinition const* _currentContract,
ParameterList const* _returnParameters,
bool _allowLazyTypes = true);
private:
virtual void endVisit(VariableDeclaration& _variable) override;
@ -118,7 +132,8 @@ private:
virtual bool visit(Return& _return) override;
NameAndTypeResolver& m_resolver;
ParameterList* m_returnParameters;
ContractDefinition const* m_currentContract;
ParameterList const* m_returnParameters;
bool m_allowLazyTypes;
};

41
libsolidity/Parser.cpp

@ -112,15 +112,23 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
ASTPointer<ContractDefinition> Parser::parseContractDefinition()
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring;
ASTPointer<ASTString> docString;
if (m_scanner->getCurrentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
docString = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::CONTRACT);
ASTPointer<ASTString> name = expectIdentifierToken();
expectToken(Token::LBRACE);
vector<ASTPointer<InheritanceSpecifier>> baseContracts;
vector<ASTPointer<StructDefinition>> structs;
vector<ASTPointer<VariableDeclaration>> stateVariables;
vector<ASTPointer<FunctionDefinition>> functions;
if (m_scanner->getCurrentToken() == Token::IS)
do
{
m_scanner->next();
baseContracts.push_back(parseInheritanceSpecifier());
}
while (m_scanner->getCurrentToken() == Token::COMMA);
expectToken(Token::LBRACE);
bool visibilityIsPublic = true;
while (true)
{
@ -134,7 +142,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
expectToken(Token::COLON);
}
else if (currentToken == Token::FUNCTION)
functions.push_back(parseFunctionDefinition(visibilityIsPublic));
functions.push_back(parseFunctionDefinition(visibilityIsPublic, name.get()));
else if (currentToken == Token::STRUCT)
structs.push_back(parseStructDefinition());
else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING ||
@ -149,10 +157,28 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
}
nodeFactory.markEndPosition();
expectToken(Token::RBRACE);
return nodeFactory.createNode<ContractDefinition>(name, docstring, structs, stateVariables, functions);
return nodeFactory.createNode<ContractDefinition>(name, docString, baseContracts, structs,
stateVariables, functions);
}
ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<Identifier> name = ASTNodeFactory(*this).createNode<Identifier>(expectIdentifierToken());
vector<ASTPointer<Expression>> arguments;
if (m_scanner->getCurrentToken() == Token::LPAREN)
{
m_scanner->next();
arguments = parseFunctionCallArguments();
nodeFactory.markEndPosition();
expectToken(Token::RPAREN);
}
else
nodeFactory.setEndPositionFromNode(name);
return nodeFactory.createNode<InheritanceSpecifier>(name, arguments);
}
ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic, ASTString const* _contractName)
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring;
@ -184,7 +210,8 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
}
ASTPointer<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block);
return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, docstring,
bool const c_isConstructor = (_contractName && *name == *_contractName);
return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, c_isConstructor, docstring,
parameters,
isDeclaredConst, returnParameters, block);
}

3
libsolidity/Parser.h

@ -49,7 +49,8 @@ private:
///@name Parsing functions for the AST nodes
ASTPointer<ImportDirective> parseImportDirective();
ASTPointer<ContractDefinition> parseContractDefinition();
ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic);
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic, ASTString const* _contractName);
ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<VariableDeclaration> parseVariableDeclaration(bool _allowVar);
ASTPointer<TypeName> parseTypeName(bool _allowVar);

5
libsolidity/Token.h

@ -48,7 +48,8 @@
#include <libsolidity/Exceptions.h>
#if defined(DELETE)
#error The macro "DELETE" from windows.h conflicts with this file. Please change the order of includes.
#undef DELETE
//#error The macro "DELETE" from windows.h conflicts with this file. Please change the order of includes.
#endif
namespace dev
@ -152,7 +153,7 @@ namespace solidity
K(DEFAULT, "default", 0) \
K(DO, "do", 0) \
K(ELSE, "else", 0) \
K(EXTENDS, "extends", 0) \
K(IS, "is", 0) \
K(FOR, "for", 0) \
K(FUNCTION, "function", 0) \
K(IF, "if", 0) \

59
libsolidity/Types.cpp

@ -147,7 +147,7 @@ TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const
{
// "delete" is ok for all integer types
if (_operator == Token::DELETE)
return shared_from_this();
return make_shared<VoidType>();
// no further unary operators for addresses
else if (isAddress())
return TypePointer();
@ -408,6 +408,13 @@ u256 BoolType::literalValue(Literal const* _literal) const
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal."));
}
TypePointer BoolType::unaryOperatorResult(Token::Value _operator) const
{
if (_operator == Token::DELETE)
return make_shared<VoidType>();
return (_operator == Token::NOT) ? shared_from_this() : TypePointer();
}
TypePointer BoolType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
{
if (getCategory() != _other->getCategory())
@ -424,12 +431,24 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return true;
if (_convertTo.getCategory() == Category::INTEGER)
return dynamic_cast<IntegerType const&>(_convertTo).isAddress();
if (_convertTo.getCategory() == Category::CONTRACT)
{
auto const& bases = getContractDefinition().getLinearizedBaseContracts();
return find(bases.begin(), bases.end(),
&dynamic_cast<ContractType const&>(_convertTo).getContractDefinition()) != bases.end();
}
return false;
}
bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
return isImplicitlyConvertibleTo(_convertTo) || _convertTo.getCategory() == Category::INTEGER;
return isImplicitlyConvertibleTo(_convertTo) || _convertTo.getCategory() == Category::INTEGER ||
_convertTo.getCategory() == Category::CONTRACT;
}
TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const
{
return _operator == Token::DELETE ? make_shared<VoidType>() : TypePointer();
}
bool ContractType::operator==(Type const& _other) const
@ -440,14 +459,6 @@ bool ContractType::operator==(Type const& _other) const
return other.m_contract == m_contract;
}
u256 ContractType::getStorageSize() const
{
u256 size = 0;
for (ASTPointer<VariableDeclaration> const& variable: m_contract.getStateVariables())
size += variable->getType()->getStorageSize();
return max<u256>(1, size);
}
string ContractType::toString() const
{
return "contract " + m_contract.getName();
@ -491,6 +502,11 @@ u256 ContractType::getFunctionIdentifier(string const& _functionName) const
return Invalid256;
}
TypePointer StructType::unaryOperatorResult(Token::Value _operator) const
{
return _operator == Token::DELETE ? make_shared<VoidType>() : TypePointer();
}
bool StructType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
@ -686,6 +702,29 @@ bool TypeType::operator==(Type const& _other) const
return *getActualType() == *other.getActualType();
}
MemberList const& TypeType::getMembers() const
{
// We need to lazy-initialize it because of recursive references.
if (!m_members)
{
map<string, TypePointer> members;
if (m_actualType->getCategory() == Category::CONTRACT && m_currentContract != nullptr)
{
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_actualType).getContractDefinition();
vector<ContractDefinition const*> currentBases = m_currentContract->getLinearizedBaseContracts();
if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end())
// We are accessing the type of a base contract, so add all public and private
// functions. Note that this does not add inherited functions on purpose.
for (ASTPointer<FunctionDefinition> const& f: contract.getDefinedFunctions())
if (!f->isConstructor())
members[f->getName()] = make_shared<FunctionType>(*f);
}
m_members.reset(new MemberList(members));
}
return *m_members;
}
MagicType::MagicType(MagicType::Kind _kind):
m_kind(_kind)
{

21
libsolidity/Types.h

@ -259,10 +259,7 @@ public:
BoolType() {}
virtual Category getCategory() const { return Category::BOOL; }
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override
{
return (_operator == Token::NOT || _operator == Token::DELETE) ? shared_from_this() : TypePointer();
}
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
virtual unsigned getCalldataEncodedSize() const { return 1; }
@ -284,8 +281,8 @@ public:
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
/// Contracts can be converted to themselves and to integers.
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override;
virtual bool isValueType() const override { return true; }
virtual std::string toString() const override;
@ -317,11 +314,7 @@ class StructType: public Type
public:
virtual Category getCategory() const override { return Category::STRUCT; }
StructType(StructDefinition const& _struct): m_struct(_struct) {}
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override
{
return _operator == Token::DELETE ? shared_from_this() : TypePointer();
}
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override;
@ -449,7 +442,8 @@ class TypeType: public Type
{
public:
virtual Category getCategory() const override { return Category::TYPE; }
TypeType(TypePointer const& _actualType): m_actualType(_actualType) {}
TypeType(TypePointer const& _actualType, ContractDefinition const* _currentContract = nullptr):
m_actualType(_actualType), m_currentContract(_currentContract) {}
TypePointer const& getActualType() const { return m_actualType; }
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
@ -458,9 +452,14 @@ public:
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); }
virtual bool canLiveOutsideStorage() const override { return false; }
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
virtual MemberList const& getMembers() const override;
private:
TypePointer m_actualType;
/// Context in which this type is used (influences visibility etc.), can be nullptr.
ContractDefinition const* m_currentContract;
/// List of member types, will be lazy-initialized because of recursive references.
mutable std::unique_ptr<MemberList> m_members;
};

5
libsolidity/grammar.txt

@ -1,7 +1,10 @@
ContractDefinition = 'contract' Identifier '{' ContractPart* '}'
ContractDefinition = 'contract' Identifier
( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )?
'{' ContractPart* '}'
ContractPart = VariableDeclaration ';' | StructDefinition |
FunctionDefinition | 'public:' | 'private:'
InheritanceSpecifier = Identifier ( '(' Expression ( ',' Expression )* ')' )?
StructDefinition = 'struct' Identifier '{'
( VariableDeclaration (';' VariableDeclaration)* )? '}

32
mix/AppContext.cpp

@ -22,7 +22,6 @@
* - KeyEventManager
*/
#include <QDebug>
#include <QMessageBox>
#include <QQmlComponent>
#include <QQmlContext>
@ -30,7 +29,10 @@
#include "CodeModel.h"
#include "FileIo.h"
#include "ClientModel.h"
#include "CodeEditorExtensionManager.h"
#include "Exceptions.h"
#include "AppContext.h"
#include "QEther.h"
#include <libwebthree/WebThree.h>
using namespace dev;
@ -46,18 +48,44 @@ AppContext::AppContext(QQmlApplicationEngine* _engine)
m_codeModel.reset(new CodeModel(this));
m_clientModel.reset(new ClientModel(this));
m_fileIo.reset(new FileIo());
/*
m_applicationEngine->rootContext()->setContextProperty("appContext", this);
qmlRegisterType<FileIo>("org.ethereum.qml", 1, 0, "FileIo");
qmlRegisterSingletonType(QUrl("qrc:/qml/ProjectModel.qml"), "org.ethereum.qml.ProjectModel", 1, 0, "ProjectModel");
qmlRegisterType<QEther>("org.ethereum.qml.QEther", 1, 0, "QEther");
qmlRegisterType<QBigInt>("org.ethereum.qml.QBigInt", 1, 0, "QBigInt");
m_applicationEngine->rootContext()->setContextProperty("codeModel", m_codeModel.get());
m_applicationEngine->rootContext()->setContextProperty("fileIo", m_fileIo.get());
*/
}
AppContext::~AppContext()
{
}
void AppContext::load()
{
m_applicationEngine->rootContext()->setContextProperty("appContext", this);
qmlRegisterType<FileIo>("org.ethereum.qml", 1, 0, "FileIo");
m_applicationEngine->rootContext()->setContextProperty("codeModel", m_codeModel.get());
m_applicationEngine->rootContext()->setContextProperty("fileIo", m_fileIo.get());
qmlRegisterType<QEther>("org.ethereum.qml.QEther", 1, 0, "QEther");
qmlRegisterType<QBigInt>("org.ethereum.qml.QBigInt", 1, 0, "QBigInt");
QQmlComponent projectModelComponent(m_applicationEngine, QUrl("qrc:/qml/ProjectModel.qml"));
QObject* projectModel = projectModelComponent.create();
if (projectModelComponent.isError())
{
QmlLoadException exception;
for (auto const& e : projectModelComponent.errors())
exception << QmlErrorInfo(e);
BOOST_THROW_EXCEPTION(exception);
}
m_applicationEngine->rootContext()->setContextProperty("projectModel", projectModel);
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
m_applicationEngine->load(QUrl("qrc:/qml/main.qml"));
appLoaded();
}
QQmlApplicationEngine* AppContext::appEngine()
{
return m_applicationEngine;

2
mix/AppContext.h

@ -60,6 +60,8 @@ class AppContext: public QObject
public:
AppContext(QQmlApplicationEngine* _engine);
virtual ~AppContext();
/// Load the UI from qml files
void load();
/// Get the current QQMLApplicationEngine instance.
QQmlApplicationEngine* appEngine();
/// Get code model

2
mix/AssemblyDebuggerControl.cpp

@ -49,5 +49,5 @@ void AssemblyDebuggerControl::start() const
void AssemblyDebuggerControl::showDebugger()
{
QObject* debugPanel = m_view->findChild<QObject*>("debugPanel", Qt::FindChildrenRecursively);
QMetaObject::invokeMethod(debugPanel, "update");
QMetaObject::invokeMethod(debugPanel, "update", Q_ARG(QVariant, true));
}

15
mix/CMakeLists.txt

@ -12,12 +12,20 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
aux_source_directory(. SRC_LIST)
include_directories(..)
find_package (Qt5WebEngine)
qt5_add_resources(UI_RESOURCES qml.qrc)
file(GLOB HEADERS "*.h")
set(EXECUTABLE mix)
if ("${Qt5WebEngine_VERSION_STRING}" VERSION_GREATER "5.3.0")
set (ETH_HAVE_WEBENGINE TRUE)
qt5_add_resources(UI_RESOURCES web.qrc)
else()
qt5_add_resources(UI_RESOURCES noweb.qrc)
endif()
# eth_add_executable is defined in cmake/EthExecutableHelper.cmake
eth_add_executable(${EXECUTABLE}
ICON mix
@ -38,8 +46,13 @@ target_link_libraries(${EXECUTABLE} lll)
target_link_libraries(${EXECUTABLE} solidity)
target_link_libraries(${EXECUTABLE} evmcore)
target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} web3jsonrpc)
target_link_libraries(${EXECUTABLE} jsqrc)
target_link_libraries(${EXECUTABLE} web3jsonrpc)
if (${ETH_HAVE_WEBENGINE})
add_definitions(-DETH_HAVE_WEBENGINE)
target_link_libraries(${EXECUTABLE} Qt5::WebEngine)
endif()
# eth_install_executable is defined in cmake/EthExecutableHelper.cmake
eth_install_executable(${EXECUTABLE}

63
mix/ClientModel.cpp

@ -24,7 +24,7 @@
#include <QQmlApplicationEngine>
#include <libdevcore/CommonJS.h>
#include <libethereum/Transaction.h>
#include "ClientModel.h"
#include <libqwebthree/QWebThree.h>
#include "AppContext.h"
#include "DebuggingStateWrapper.h"
#include "QContractDefinition.h"
@ -32,28 +32,19 @@
#include "ContractCallDataEncoder.h"
#include "CodeModel.h"
#include "ClientModel.h"
#include "QEther.h"
#include "Web3Server.h"
#include "ClientModel.h"
using namespace dev;
using namespace dev::eth;
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());
}
ClientModel::ClientModel(AppContext* _context):
m_context(_context), m_running(false)
m_context(_context), m_running(false), m_qWebThree(nullptr)
{
qRegisterMetaType<QBigInt*>("QBigInt*");
qRegisterMetaType<QEther*>("QEther*");
qRegisterMetaType<QVariableDefinition*>("QVariableDefinition*");
qRegisterMetaType<QVariableDefinitionList*>("QVariableDefinitionList*");
qRegisterMetaType<QList<QVariableDefinition*>>("QList<QVariableDefinition*>");
@ -64,9 +55,30 @@ ClientModel::ClientModel(AppContext* _context):
connect(this, &ClientModel::dataAvailable, this, &ClientModel::showDebugger, Qt::QueuedConnection);
m_client.reset(new MixClient());
m_qWebThree = new QWebThree(this);
m_qWebThreeConnector.reset(new QWebThreeConnector());
m_qWebThreeConnector->setQWeb(m_qWebThree);
m_web3Server.reset(new Web3Server(*m_qWebThreeConnector.get(), std::vector<dev::KeyPair> { m_client->userAccount() }, m_client.get()));
connect(m_qWebThree, &QWebThree::response, this, &ClientModel::apiResponse);
_context->appEngine()->rootContext()->setContextProperty("clientModel", this);
}
ClientModel::~ClientModel()
{
}
void ClientModel::apiRequest(const QString& _message)
{
m_qWebThree->postMessage(_message);
}
QString ClientModel::contractAddress() const
{
QString address = QString::fromStdString(dev::toJS(m_client->lastContractAddress()));
return address;
}
void ClientModel::debugDeployment()
{
executeSequence(std::vector<TransactionSettings>(), 10000000 * ether);
@ -74,7 +86,7 @@ void ClientModel::debugDeployment()
void ClientModel::debugState(QVariantMap _state)
{
u256 balance = fromQString(_state.value("balance").toString());
u256 balance = (qvariant_cast<QEther*>(_state.value("balance")))->toU256Wei();
QVariantList transactions = _state.value("transactions").toList();
std::vector<TransactionSettings> transactionSequence;
@ -84,14 +96,17 @@ void ClientModel::debugState(QVariantMap _state)
QVariantMap transaction = t.toMap();
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());
u256 gas = (qvariant_cast<QEther*>(transaction.value("gas")))->toU256Wei();
u256 value = (qvariant_cast<QEther*>(transaction.value("value")))->toU256Wei();
u256 gasPrice = (qvariant_cast<QEther*>(transaction.value("gasPrice")))->toU256Wei();
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())));
{
QBigInt* param = qvariant_cast<QBigInt*>(p.value());
transactionSettings.parameterValues.insert(std::make_pair(p.key(), boost::get<dev::u256>(param->internalValue())));
}
transactionSequence.push_back(transactionSettings);
}
@ -206,9 +221,11 @@ ExecutionResult ClientModel::deployContract(bytes const& _code)
u256 gas = 125000;
u256 amount = 100;
Address contractAddress = m_client->transact(m_client->userAccount().secret(), amount, _code, gas, gasPrice);
Address lastAddress = m_client->lastContractAddress();
Address newAddress = m_client->transact(m_client->userAccount().secret(), amount, _code, gas, gasPrice);
ExecutionResult r = m_client->lastExecutionResult();
r.contractAddress = contractAddress;
if (newAddress != lastAddress)
contractAddressChanged();
return r;
}

43
mix/ClientModel.h

@ -1,18 +1,18 @@
/*
This file is part of cpp-ethereum.
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
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 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.
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/>.
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 ClientModel.h
* @author Yann yann@ethdev.com
@ -32,12 +32,16 @@ using AssemblyDebuggerData = std::tuple<QList<QObject*>, dev::mix::QQMLMap*>;
Q_DECLARE_METATYPE(AssemblyDebuggerData)
Q_DECLARE_METATYPE(dev::mix::ExecutionResult)
class QWebThree;
class QWebThreeConnector;
namespace dev
{
namespace mix
{
class AppContext;
class Web3Server;
/// Backend transaction config class
struct TransactionSettings
@ -67,10 +71,16 @@ class ClientModel: public QObject
public:
ClientModel(AppContext* _context);
~ClientModel();
/// @returns true if currently executing contract code
Q_PROPERTY(bool running MEMBER m_running NOTIFY stateChanged)
/// @returns address of the last executed contract
Q_PROPERTY(QString contractAddress READ contractAddress NOTIFY contractAddressChanged)
public slots:
/// ethereum.js RPC request entry point
/// @param _message RPC request in Json format
void apiRequest(QString const& _message);
/// Run the contract constructor and show debugger window.
void debugDeployment();
/// Setup state, run transaction sequence, show debugger for the last transaction
@ -91,15 +101,21 @@ signals:
/// Transaction execution completed with error
/// @param _message Error message
void runFailed(QString const& _message);
/// Contract address changed
void contractAddressChanged();
/// Execution state changed
void stateChanged();
/// Show debugger window request
void showDebuggerWindow();
/// ethereum.js RPC response ready
/// @param _message RPC response in Json format
void apiResponse(QString const& _message);
/// Emited when machine states are available.
void dataAvailable(QList<QVariableDefinition*> const& _returnParams = QList<QVariableDefinition*>(), QList<QObject*> const& _wStates = QList<QObject*>(), AssemblyDebuggerData const& _code = AssemblyDebuggerData());
private:
QString contractAddress() const;
void executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance);
ExecutionResult deployContract(bytes const& _code);
ExecutionResult callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr);
@ -107,6 +123,9 @@ private:
AppContext* m_context;
std::atomic<bool> m_running;
std::unique_ptr<MixClient> m_client;
QWebThree* m_qWebThree;
std::unique_ptr<QWebThreeConnector> m_qWebThreeConnector;
std::unique_ptr<Web3Server> m_web3Server;
};
}

14
mix/CodeModel.cpp

@ -26,6 +26,7 @@
#include <QtQml>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/SourceReferenceFormatter.h>
#include <libsolidity/InterfaceHandler.h>
#include <libevmcore/Instruction.h>
#include "QContractDefinition.h"
#include "QFunctionDefinition.h"
@ -55,9 +56,12 @@ CompilationResult::CompilationResult(const dev::solidity::CompilerStack& _compil
{
if (!_compiler.getContractNames().empty())
{
m_contract.reset(new QContractDefinition(&_compiler.getContractDefinition(std::string())));
auto const& contractDefinition = _compiler.getContractDefinition(std::string());
m_contract.reset(new QContractDefinition(&contractDefinition));
m_bytes = _compiler.getBytecode();
m_assemblyCode = QString::fromStdString(dev::eth::disassemble(m_bytes));
dev::solidity::InterfaceHandler interfaceHandler;
m_contractInterface = QString::fromStdString(*interfaceHandler.getABIInterface(contractDefinition));
}
else
m_contract.reset(new QContractDefinition());
@ -71,6 +75,7 @@ CompilationResult::CompilationResult(CompilationResult const& _prev, QString con
m_compilerMessage(_compilerMessage),
m_bytes(_prev.m_bytes),
m_assemblyCode(_prev.m_assemblyCode),
m_contractInterface(_prev.m_contractInterface),
m_codeHighlighter(_prev.m_codeHighlighter)
{}
@ -156,14 +161,19 @@ void CodeModel::runCompilationJob(int _jobId, QString const& _code)
emit compilationCompleteInternal(result.release());
}
void CodeModel::onCompilationComplete(CompilationResult*_newResult)
void CodeModel::onCompilationComplete(CompilationResult* _newResult)
{
m_compiling = false;
bool contractChanged = m_result->contractInterface() != _newResult->contractInterface();
m_result.reset(_newResult);
emit compilationComplete();
emit stateChanged();
if (m_result->successful())
{
emit codeChanged();
if (contractChanged)
emit contractInterfaceChanged();
}
}
bool CodeModel::hasContract() const

6
mix/CodeModel.h

@ -67,6 +67,7 @@ class CompilationResult: public QObject
Q_PROPERTY(QContractDefinition* contract READ contract)
Q_PROPERTY(QString compilerMessage READ compilerMessage CONSTANT)
Q_PROPERTY(bool successful READ successful CONSTANT)
Q_PROPERTY(QString contractInterface READ contractInterface CONSTANT)
public:
/// Empty compilation result constructor
@ -88,6 +89,8 @@ public:
dev::bytes const& bytes() const { return m_bytes; }
/// @returns contract bytecode in human-readable form
QString assemblyCode() const { return m_assemblyCode; }
/// @returns contract definition in JSON format
QString contractInterface() const { return m_contractInterface; }
/// Get code highlighter
std::shared_ptr<CodeHighlighter> codeHighlighter() { return m_codeHighlighter; }
@ -98,6 +101,7 @@ private:
QString m_compilerMessage; ///< @todo: use some structure here
dev::bytes m_bytes;
QString m_assemblyCode;
QString m_contractInterface;
std::shared_ptr<CodeHighlighter> m_codeHighlighter;
friend class CodeModel;
@ -137,6 +141,8 @@ signals:
void scheduleCompilationJob(int _jobId, QString const& _content);
/// Emitted if there are any changes in the code model
void codeChanged();
/// Emitted if there are any changes in the contract interface
void contractInterfaceChanged();
/// Emitted on compilation complete. Internal
void compilationCompleteInternal(CompilationResult* _newResult);

25
mix/DebuggingStateWrapper.cpp

@ -23,12 +23,14 @@
#include <QApplication>
#include <QDebug>
#include <QPointer>
#include <QQmlEngine>
#include <libevmcore/Instruction.h>
#include <libdevcore/CommonJS.h>
#include <libdevcrypto/Common.h>
#include <libevmcore/Instruction.h>
#include <libdevcore/Common.h>
#include "DebuggingStateWrapper.h"
#include "QBigInt.h"
using namespace dev;
using namespace dev::eth;
using namespace dev::mix;
@ -66,33 +68,26 @@ std::tuple<QList<QObject*>, QQMLMap*> DebuggingStateWrapper::getHumanReadableCod
return std::make_tuple(codeStr, QPointer<QQMLMap>(new QQMLMap(codeMapping)));
}
QString DebuggingStateWrapper::gasCost()
QBigInt* DebuggingStateWrapper::gasCost()
{
std::ostringstream ss;
ss << std::dec << m_state.gasCost;
return QString::fromStdString(ss.str());
return new QBigInt(m_state.gasCost);
}
QString DebuggingStateWrapper::gas()
QBigInt* DebuggingStateWrapper::gas()
{
std::ostringstream ss;
ss << std::dec << m_state.gas;
return QString::fromStdString(ss.str());
return new QBigInt(m_state.gas);
}
QString DebuggingStateWrapper::newMemSize()
QBigInt* DebuggingStateWrapper::newMemSize()
{
std::ostringstream ss;
ss << std::dec << m_state.newMemSize;
return QString::fromStdString(ss.str());
return new QBigInt(m_state.newMemSize);
}
QStringList DebuggingStateWrapper::debugStack()
{
QStringList stack;
for (auto i: m_state.stack)
stack.append(QString::fromStdString(prettyU256(i)));
for (std::vector<u256>::reverse_iterator i = m_state.stack.rbegin(); i != m_state.stack.rend(); ++i)
stack.append(QString::fromStdString(prettyU256(*i)));
return fillList(stack, "");
}

15
mix/DebuggingStateWrapper.h

@ -29,6 +29,7 @@
#include <libethereum/Executive.h>
#include "QVariableDefinition.h"
#include "MixClient.h"
#include "QBigInt.h"
namespace dev
{
@ -81,8 +82,8 @@ class DebuggingStateWrapper: public QObject
Q_OBJECT
Q_PROPERTY(int step READ step CONSTANT)
Q_PROPERTY(int curPC READ curPC CONSTANT)
Q_PROPERTY(QString gasCost READ gasCost CONSTANT)
Q_PROPERTY(QString gas READ gas CONSTANT)
Q_PROPERTY(QBigInt* gasCost READ gasCost CONSTANT)
Q_PROPERTY(QBigInt* gas READ gas CONSTANT)
Q_PROPERTY(QString instruction READ instruction CONSTANT)
Q_PROPERTY(QStringList debugStack READ debugStack CONSTANT)
Q_PROPERTY(QStringList debugStorage READ debugStorage CONSTANT)
@ -90,7 +91,7 @@ class DebuggingStateWrapper: public QObject
Q_PROPERTY(QVariantList debugCallData READ debugCallData CONSTANT)
Q_PROPERTY(QString headerInfo READ headerInfo CONSTANT)
Q_PROPERTY(QString endOfDebug READ endOfDebug CONSTANT)
Q_PROPERTY(QString newMemSize READ newMemSize CONSTANT)
Q_PROPERTY(QBigInt* newMemSize READ newMemSize CONSTANT)
Q_PROPERTY(QStringList levels READ levels CONSTANT)
public:
@ -99,12 +100,10 @@ public:
int step() { return (int)m_state.steps; }
/// Get the proccessed code index.
int curPC() { return (int)m_state.curPC; }
/// Get gas left.
QString gasLeft();
/// Get gas cost.
QString gasCost();
QBigInt* gasCost();
/// Get gas used.
QString gas();
QBigInt* gas();
/// Get stack.
QStringList debugStack();
/// Get storage.
@ -118,7 +117,7 @@ public:
/// get end of debug information.
QString endOfDebug();
/// Get the new memory size.
QString newMemSize();
QBigInt* newMemSize();
/// Get current instruction
QString instruction();
/// Get all previous steps.

31
mix/Exceptions.cpp

@ -0,0 +1,31 @@
/*
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 Exceptions.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#include <ostream>
#include <QQmlError>
#include "Exceptions.h"
std::ostream& operator<<(std::ostream& _out, QQmlError const& _error)
{
_out << _error.toString().toStdString();
return _out;
}

45
mix/Exceptions.h

@ -0,0 +1,45 @@
/*
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 Exceptions.h
* @author Arkadiy Paronyan arkadiy@ethdev.com
* @date 2015
* Ethereum IDE client.
*/
#pragma once
#include <iosfwd>
#include <libdevcore/Exceptions.h>
class QTextDocument;
class QQmlError;
namespace dev
{
namespace mix
{
struct QmlLoadException: virtual Exception {};
struct FileIoException: virtual Exception {};
typedef boost::error_info<struct tagQmlError, QQmlError> QmlErrorInfo;
typedef boost::error_info<struct tagFileError, std::string> FileError;
}
}
std::ostream& operator<<(std::ostream& _out, QQmlError const& _error);

6
mix/FileIo.cpp

@ -20,7 +20,6 @@
* Ethereum IDE client.
*/
#include <stdexcept>
#include <QDir>
#include <QFile>
#include <QFileInfo>
@ -41,7 +40,10 @@ void FileIo::makeDir(QString const& _url)
QString FileIo::readFile(QString const& _url)
{
QUrl url(_url);
QFile file(url.path());
QString path(url.path());
if (url.scheme() == "qrc")
path = ":" + path;
QFile file(path);
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QTextStream stream(&file);

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

Loading…
Cancel
Save