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. 67
      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. 5
      libjsqrc/ethereumjs/bower.json
  32. 886
      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. 356
      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. 812
      libjsqrc/ethereumjs/test/abi.parsers.js
  50. 2
      libjsqrc/ethereumjs/test/db.methods.js
  51. 4
      libjsqrc/ethereumjs/test/eth.methods.js
  52. 2
      libjsqrc/ethereumjs/test/mocha.opts
  53. 2
      libjsqrc/ethereumjs/test/shh.methods.js
  54. 4
      libjsqrc/ethereumjs/test/utils.js
  55. 2
      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. 66
      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. 114
      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. 126
      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. 21
      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 project.pbxproj
evmjit
doc/html doc/html
*.autosave *.autosave
node_modules/ node_modules/

4
CMakeLists.txt

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

10
alethzero/Main.ui

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

67
alethzero/MainWin.cpp

@ -60,6 +60,11 @@ using namespace dev::p2p;
using namespace dev::eth; using namespace dev::eth;
namespace js = json_spirit; 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) static void initUnits(QComboBox* _b)
{ {
for (auto n = (unsigned)units().size(); n-- != 0; ) for (auto n = (unsigned)units().size(); n-- != 0; )
@ -447,7 +452,7 @@ void Main::eval(QString const& _js)
else else
s = "<span style=\"color: #888\">unknown type</span>"; s = "<span style=\"color: #888\">unknown type</span>";
m_consoleHistory.push_back(qMakePair(_js, s)); 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) 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>" 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>"; "<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() 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 try
{ {
js::mValue val; js::mValue val;
@ -780,9 +785,13 @@ void Main::on_importKeyFile_triggered()
if (std::find(m_myKeys.begin(), m_myKeys.end(), k) == m_myKeys.end()) if (std::find(m_myKeys.begin(), m_myKeys.end(), k) == m_myKeys.end())
{ {
m_myKeys.append(k); if (m_myKeys.empty())
{
m_myKeys.push_back(KeyPair::create());
keysChanged(); keysChanged();
} }
ethereum()->transact(k.sec(), ethereum()->balanceAt(k.address()) - gasPrice() * c_txGas, m_myKeys.back().address(), {}, c_txGas, gasPrice());
}
else else
QMessageBox::warning(this, "Already Have Key", "Could not import the secret key: we already own this account."); 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); auto b = bc.block(h);
for (auto const& i: RLP(b)[1]) for (auto const& i: RLP(b)[1])
{ {
Transaction t(i.data()); Transaction t(i.data(), CheckSignature::Sender);
if (bm || transactionMatch(filter, t)) if (bm || transactionMatch(filter, t))
{ {
QString s = t.receiveAddress() ? QString s = t.receiveAddress() ?
@ -1273,12 +1282,12 @@ void Main::on_transactionQueue_currentItemChanged()
if (tx.data().size()) if (tx.data().size())
s << dev::memDump(tx.data(), 16, true); 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 << "<hr/>";
s << "<div>Log Bloom: " << receipt.bloom() << "</div>"; s << "<div>Log Bloom: " << receipt.bloom() << "</div>";
auto r = receipt.rlp(); auto r = receipt.rlp();
s << "<div>Receipt: " << toString(RLP(r)) << "</div>"; 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 << renderDiff(ethereum()->diff(i, 0));
// s << "Pre: " << fs.rootHash() << "<br/>"; // s << "Pre: " << fs.rootHash() << "<br/>";
// s << "Post: <b>" << ts.rootHash() << "</b>"; // s << "Post: <b>" << ts.rootHash() << "</b>";
@ -1369,13 +1378,13 @@ void Main::on_blocks_currentItemChanged()
for (auto const& i: block[1]) 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/>" << 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/>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 << "<br/>Dump: " Span(Mono) << 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 << "<div>Receipts-Hex: " Span(Mono) << toHex(ethereum()->blockChain().receipts(h).rlp()) << "</span></div>";
} }
else else
{ {
unsigned txi = item->data(Qt::UserRole + 1).toInt(); 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(); auto ss = tx.safeSender();
h256 th = sha3(rlpList(ss, tx.nonce())); h256 th = sha3(rlpList(ss, tx.nonce()));
TransactionReceipt receipt = ethereum()->blockChain().receipts(h).receipts[txi]; TransactionReceipt receipt = ethereum()->blockChain().receipts(h).receipts[txi];
@ -1404,12 +1413,12 @@ void Main::on_blocks_currentItemChanged()
if (tx.data().size()) if (tx.data().size())
s << dev::memDump(tx.data(), 16, true); 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 << "<hr/>";
s << "<div>Log Bloom: " << receipt.bloom() << "</div>"; s << "<div>Log Bloom: " << receipt.bloom() << "</div>";
auto r = receipt.rlp(); auto r = receipt.rlp();
s << "<div>Receipt: " << toString(RLP(r)) << "</div>"; 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)); s << renderDiff(ethereum()->diff(txi, h));
ui->debugCurrent->setEnabled(true); ui->debugCurrent->setEnabled(true);
ui->debugDumpState->setEnabled(true); ui->debugDumpState->setEnabled(true);
@ -1528,6 +1537,7 @@ void Main::on_contracts_currentItemChanged()
for (auto const& i: storage) for (auto const& i: storage)
s << "@" << showbase << hex << prettyU256(i.first).toStdString() << "&nbsp;&nbsp;&nbsp;&nbsp;" << showbase << hex << prettyU256(i.second).toStdString() << "<br/>"; 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 << "<h4>Body Code</h4>" << disassemble(ethereum()->codeAt(address));
s << Div(Mono) << toHex(ethereum()->codeAt(address)) << "</div>";
ui->contractInfo->appendHtml(QString::fromStdString(s.str())); ui->contractInfo->appendHtml(QString::fromStdString(s.str()));
} }
catch (dev::InvalidTrie) catch (dev::InvalidTrie)
@ -1637,13 +1647,18 @@ static shh::Topic topicFromText(QString _s)
return ret; return ret;
} }
bool Main::sourceIsSolidity(string const& _source) bool Main::sourceIsSolidity(string const& _source)
{ {
// TODO: Improve this heuristic // TODO: Improve this heuristic
return (_source.substr(0, 8) == "contract" || _source.substr(0, 5) == "//sol"); 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 Main::getFunctionHashes(dev::solidity::CompilerStack const &_compiler,
string const& _contractName) string const& _contractName)
{ {
@ -1678,6 +1693,7 @@ void Main::on_data_textChanged()
dev::solidity::CompilerStack compiler; dev::solidity::CompilerStack compiler;
try try
{ {
// compiler.addSources(dev::solidity::StandardSources);
m_data = compiler.compile(src, m_enableOptimizer); m_data = compiler.compile(src, m_enableOptimizer);
solidity = "<h4>Solidity</h4>"; solidity = "<h4>Solidity</h4>";
solidity += "<pre>" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped() + "</pre>"; solidity += "<pre>" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped() + "</pre>";
@ -1695,10 +1711,7 @@ void Main::on_data_textChanged()
solidity = "<h4>Solidity</h4><pre>Uncaught exception.</pre>"; 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
{ {
@ -1712,6 +1725,9 @@ void Main::on_data_textChanged()
} }
} }
else else
{
m_data = compileLLL(src, m_enableOptimizer, &errors);
if (errors.empty())
{ {
auto asmcode = compileLLLToAsm(src, false); auto asmcode = compileLLLToAsm(src, false);
lll = "<h4>Pre</h4><pre>" + QString::fromStdString(asmcode).toHtmlEscaped() + "</pre>"; lll = "<h4>Pre</h4><pre>" + QString::fromStdString(asmcode).toHtmlEscaped() + "</pre>";
@ -1729,7 +1745,7 @@ void Main::on_data_textChanged()
for (auto const& i: errors) for (auto const& i: errors)
errs.append("<div style=\"border-left: 6px solid #c00; margin-top: 2px\">" + QString::fromStdString(i).toHtmlEscaped() + "</div>"); 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)); ui->gas->setMinimum((qint64)Client::txGas(m_data, 0));
if (!ui->gas->isEnabled()) if (!ui->gas->isEnabled())
ui->gas->setValue(m_backupGas); 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() 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(); 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) 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 # LLVM
if(LLVM_DIR) # local LLVM build if(LLVM_DIR OR APPLE) # local LLVM build
find_package(LLVM REQUIRED CONFIG) find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") 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) execute_process(COMMAND llvm-config-3.5 --includedir OUTPUT_VARIABLE LLVM_INCLUDE_DIRS)
message(STATUS "LLVM include dirs: ${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") 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() endif()
# Boost # Boost
find_package(Boost REQUIRED) find_package(Boost REQUIRED)
add_subdirectory(libevmjit) 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; *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 addr = right160(*_addr256);
auto& code = _env->codeAt(addr); 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); explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest);
BasicBlock(const BasicBlock&) = delete; BasicBlock(const BasicBlock&) = delete;
void operator=(const BasicBlock&) = delete; BasicBlock& operator=(const BasicBlock&) = delete;
llvm::BasicBlock* llvm() { return m_llvmBB; } llvm::BasicBlock* llvm() { return m_llvmBB; }
@ -66,6 +66,9 @@ public:
bool isJumpDest() const { return m_isJumpDest; } 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; } LocalStack& localStack() { return m_stack; }
/// Optimization: propagates values between local stacks in basic blocks /// Optimization: propagates values between local stacks in basic blocks
@ -112,6 +115,9 @@ private:
/// Is the basic block a valid jump destination. /// Is the basic block a valid jump destination.
/// JUMPDEST is the first instruction of the basic block. /// JUMPDEST is the first instruction of the basic block.
bool const m_isJumpDest = false; 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 SOURCES "*.cpp")
file(GLOB HEADERS "*.h") file(GLOB HEADERS "*.h")
set(INTERFACE_HEADERS interface.h)
source_group("" FILES ${HEADERS}) source_group("" FILES ${HEADERS})
source_group("" FILES ${SOURCES}) source_group("" FILES ${SOURCES})
if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Disable rtti for Cache as LLVM has no rtti # Disable rtti for Cache as LLVM has no rtti
set_source_files_properties(Cache.cpp PROPERTIES COMPILE_FLAGS -fno-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") set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs")
include_directories(${LLVM_INCLUDE_DIRS}) include_directories(${LLVM_INCLUDE_DIRS})
include_directories(${Boost_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") #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
#install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib)
#install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) 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::system_temp_directory(false, cachePath);
llvm::sys::path::append(cachePath, "evm_objs", id); 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)) if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false))
lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer()); lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer());
else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory)) 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)); m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, m_builder, true));
InsertPointGuard g{m_builder}; InsertPointGuard g{m_builder};
m_builder.SetInsertPoint(m_jumpTableBlock->llvm()); 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()); auto switchInstr = m_builder.CreateSwitch(dest, getBadJumpBlock());
for (auto&& p : m_basicBlocks) for (auto&& p : m_basicBlocks)
{ {
@ -165,6 +165,26 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::str
removeDeadBlocks(); 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"); dumpCFGifRequired("blocks-init.dot");
if (m_options.optimizeStack) if (m_options.optimizeStack)
@ -394,7 +414,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
value = Endianness::toBE(m_builder, value); value = Endianness::toBE(m_builder, value);
auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes"); 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); value = m_builder.CreateZExt(byte, Type::Word);
auto byteNumValid = m_builder.CreateICmpULT(byteNum, Constant::get(32)); auto byteNumValid = m_builder.CreateICmpULT(byteNum, Constant::get(32));
@ -564,7 +585,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
} }
else else
{ {
stack.push(target); _basicBlock.setJumpTarget(target);
m_builder.CreateBr(getJumpTableBlock()); m_builder.CreateBr(getJumpTableBlock());
} }
} }
@ -580,7 +601,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
} }
else else
{ {
stack.push(target); _basicBlock.setJumpTarget(target);
m_builder.CreateCondBr(cond, getJumpTableBlock(), _nextBasicBlock); m_builder.CreateCondBr(cond, getJumpTableBlock(), _nextBasicBlock);
} }
} }

34
evmjit/libevmjit/ExecutionEngine.cpp

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

11
evmjit/libevmjit/Ext.cpp

@ -5,9 +5,6 @@
#include <llvm/IR/TypeBuilder.h> #include <llvm/IR/TypeBuilder.h>
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
//#include <libdevcrypto/SHA3.h>
//#include <libevm/FeeStructure.h>
#include "RuntimeManager.h" #include "RuntimeManager.h"
#include "Memory.h" #include "Memory.h"
#include "Type.h" #include "Type.h"
@ -23,12 +20,9 @@ namespace jit
Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan): Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan):
RuntimeHelper(_runtimeManager), RuntimeHelper(_runtimeManager),
m_memoryMan(_memoryMan) 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"); m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size");
} }
@ -74,7 +68,6 @@ llvm::Value* Ext::getArgAlloca()
getBuilder().SetInsertPoint(getMainFunction()->front().getFirstNonPHI()); getBuilder().SetInsertPoint(getMainFunction()->front().getFirstNonPHI());
a = getBuilder().CreateAlloca(Type::Word, nullptr, "arg"); a = getBuilder().CreateAlloca(Type::Word, nullptr, "arg");
} }
return a; return a;
} }

2
evmjit/libevmjit/Runtime.h

@ -34,7 +34,7 @@ public:
Runtime(RuntimeData* _data, Env* _env); Runtime(RuntimeData* _data, Env* _env);
Runtime(const Runtime&) = delete; Runtime(const Runtime&) = delete;
void operator=(const Runtime&) = delete; Runtime& operator=(const Runtime&) = delete;
StackImpl& getStack() { return m_stack; } StackImpl& getStack() { return m_stack; }
MemoryImpl& getMemory() { return m_memory; } 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> #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 // JIT object opaque type
typedef struct evm_jit evm_jit; typedef struct evm_jit evm_jit;
@ -9,6 +24,11 @@ typedef int evm_jit_return_code;
// Host-endian 256-bit integer type // Host-endian 256-bit integer type
typedef struct i256 i256; typedef struct i256 i256;
struct i256
{
char b[33];
};
// Big-endian right aligned 256-bit hash // Big-endian right aligned 256-bit hash
typedef struct h256 h256; 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_get_return_data(evm_jit* _jit, char* _return_data_offset, size_t* _return_data_size);
void evm_jit_destroy(evm_jit* _jit); 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); toBigEndian(_val, ret);
return ret; return ret;
} }
inline bytes toCompactBigEndian(byte _val, unsigned _min = 0)
{
return (_min || _val) ? bytes{ _val } : bytes{};
}
/// Convenience function for toBigEndian. /// Convenience function for toBigEndian.
/// @returns a string just big enough to represent @a _val. /// @returns a string just big enough to represent @a _val.

2
libdevcore/Exceptions.h

@ -31,7 +31,7 @@
namespace dev namespace dev
{ {
// base class for all exceptions // 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 BadHexCharacter: virtual Exception {};
struct RLPException: virtual Exception {}; struct RLPException: virtual Exception {};

11
libethcore/Exceptions.cpp

@ -27,14 +27,13 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::eth; 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(); #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 #else
#define ETH_RETURN_STRING(S) \ #define ETH_RETURN_STRING(S) m_message = S; return m_message.c_str();
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();
#endif #endif
const char* InvalidBlockFormat::what() const noexcept { ETH_RETURN_STRING("Invalid block format: Bad field " + toString(m_f) + " (" + toHex(m_d) + ")"); } 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; } h256 codeHash() const { assert(!isFreshCode()); return m_codeHash; }
/// Sets the code of the account. Must only be called when isFreshCode() returns true. /// 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(). /// @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(); } 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/Exceptions.h>
#include <libethcore/ProofOfWork.h> #include <libethcore/ProofOfWork.h>
#include <libethcore/BlockInfo.h> #include <libethcore/BlockInfo.h>
#include <liblll/Compiler.h>
#include "State.h" #include "State.h"
#include "Defaults.h" #include "Defaults.h"
using namespace std; using namespace std;
@ -56,6 +57,7 @@ std::map<Address, Account> const& dev::eth::genesisState()
{ {
static std::map<Address, Account> s_ret; static std::map<Address, Account> s_ret;
if (s_ret.empty()) if (s_ret.empty())
{
// Initialise. // Initialise.
for (auto i: vector<string>({ for (auto i: vector<string>({
"51ba59315b3a95761d0863b05ccc7a7f54703d99", "51ba59315b3a95761d0863b05ccc7a7f54703d99",
@ -68,6 +70,7 @@ std::map<Address, Account> const& dev::eth::genesisState()
"e4157b34ea9615cfbde6b4fda419828124b70c78" "e4157b34ea9615cfbde6b4fda419828124b70c78"
})) }))
s_ret[Address(fromHex(i))] = Account(u256(1) << 200, Account::NormalCreation); s_ret[Address(fromHex(i))] = Account(u256(1) << 200, Account::NormalCreation);
}
return s_ret; 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); auto bl = m_bc.block(_blockHash);
RLP b(bl); 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 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) bool Executive::setup(bytesConstRef _rlp)
{ {
// Entry point for a user-executed transaction. // Entry point for a user-executed transaction.
m_t = Transaction(_rlp); m_t = Transaction(_rlp, CheckSignature::Sender);
// Avoid invalid transactions. // Avoid invalid transactions.
auto nonceReq = m_s.transactionsFrom(m_t.sender()); 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; m_endGas -= m_out.size() * c_createDataGas;
else else
m_out.reset(); 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&) catch (StepsDone const&)

2
libethereum/State.cpp

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

10
libethereum/Transaction.cpp

@ -30,7 +30,7 @@ using namespace dev::eth;
#define ETH_ADDRESS_DEBUG 0 #define ETH_ADDRESS_DEBUG 0
Transaction::Transaction(bytesConstRef _rlpData, bool _checkSender) Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig)
{ {
int field = 0; int field = 0;
RLP rlp(_rlpData); RLP rlp(_rlpData);
@ -46,8 +46,14 @@ Transaction::Transaction(bytesConstRef _rlpData, bool _checkSender)
byte v = rlp[field = 6].toInt<byte>() - 27; byte v = rlp[field = 6].toInt<byte>() - 27;
h256 r = rlp[field = 7].toInt<u256>(); h256 r = rlp[field = 7].toInt<u256>();
h256 s = rlp[field = 8].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 }; 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(); m_sender = sender();
} }
catch (Exception& _e) catch (Exception& _e)

11
libethereum/Transaction.h

@ -37,6 +37,13 @@ enum IncludeSignature
WithSignature = 1, ///< Do include a signature. 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. /// Encodes a transaction, ready to be exported to or freshly imported from RLP.
class Transaction 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) {} 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. /// 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. /// 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. /// 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. // 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. // 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). // 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); UpgradeGuard ul(l);
// If valid, append to blocks. // If valid, append to blocks.
@ -69,14 +69,14 @@ void TransactionQueue::setFuture(std::pair<h256, bytes> const& _t)
if (m_current.count(_t.first)) if (m_current.count(_t.first))
{ {
m_current.erase(_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) void TransactionQueue::noteGood(std::pair<h256, bytes> const& _t)
{ {
WriteGuard l(m_lock); 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) for (auto it = r.first; it != r.second; ++it)
m_current.insert(it->second); m_current.insert(it->second);
m_unknown.erase(r.first, r.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()) if (m_jumpDests.empty())
for (unsigned i = 0; i < _ext.code.size(); ++i) for (unsigned i = 0; i < _ext.code.size(); ++i)
{
if (_ext.code[i] == (byte)Instruction::JUMPDEST) if (_ext.code[i] == (byte)Instruction::JUMPDEST)
m_jumpDests.insert(i); m_jumpDests.insert(i);
else if (_ext.code[i] >= (byte)Instruction::PUSH1 && _ext.code[i] <= (byte)Instruction::PUSH32) else if (_ext.code[i] >= (byte)Instruction::PUSH1 && _ext.code[i] <= (byte)Instruction::PUSH32)
i += _ext.code[i] - (unsigned)Instruction::PUSH1 + 1; i += _ext.code[i] - (unsigned)Instruction::PUSH1 + 1;
}
u256 nextPC = m_curPC + 1; u256 nextPC = m_curPC + 1;
auto osteps = _steps; auto osteps = _steps;
for (bool stopped = false; !stopped && _steps--; m_curPC = nextPC, nextPC = m_curPC + 1) 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 sudo apt-get install nodejs-legacy
``` ```
## Building ### Building (gulp)
```bash (gulp) ```bash
npm run-script build npm run-script build
``` ```
### Testing ### Testing (mocha)
```bash (mocha) ```bash
npm test npm test
``` ```

5
libjsqrc/ethereumjs/bower.json

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

886
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> <head>
<script type="text/javascript" src="js/es6-promise/promise.min.js"></script> <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" src="../dist/ethereum.js"></script>
<script type="text/javascript"> <script type="text/javascript">

3
libjsqrc/ethereumjs/example/contract.html

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

4
libjsqrc/ethereumjs/gulpfile.js

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

5
libjsqrc/ethereumjs/index.js

@ -1,8 +1,11 @@
var web3 = require('./lib/web3'); 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.WebSocketProvider = require('./lib/websocket');
web3.providers.HttpRpcProvider = require('./lib/httprpc'); web3.providers.HttpRpcProvider = require('./lib/httprpc');
web3.providers.QtProvider = require('./lib/qt'); web3.providers.QtProvider = require('./lib/qt');
web3.providers.AutoProvider = require('./lib/autoprovider'); web3.providers.AutoProvider = require('./lib/autoprovider');
web3.contract = require('./lib/contract'); web3.eth.contract = require('./lib/contract');
module.exports = web3; module.exports = web3;

356
libjsqrc/ethereumjs/lib/abi.js

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

18
libjsqrc/ethereumjs/lib/autoprovider.js

@ -27,12 +27,21 @@
* if it fails, it uses HttpRpcProvider * 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') { if (process.env.NODE_ENV !== 'build') {
var WebSocket = require('ws'); // jshint ignore:line 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) { var AutoProvider = function (userOptions) {
if (web3.haveProvider()) { if (web3.haveProvider()) {
return; return;
@ -63,7 +72,7 @@ var AutoProvider = function (userOptions) {
self.poll = self.provider.poll.bind(self.provider); self.poll = self.provider.poll.bind(self.provider);
} }
self.sendQueue.forEach(function (payload) { self.sendQueue.forEach(function (payload) {
self.provider(payload); self.provider.send(payload);
}); });
self.onmessageQueue.forEach(function (handler) { self.onmessageQueue.forEach(function (handler) {
self.provider.onmessage = 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) { AutoProvider.prototype.send = function (payload) {
if (this.provider) { if (this.provider) {
this.provider.send(payload); this.provider.send(payload);
@ -89,6 +100,7 @@ AutoProvider.prototype.send = function (payload) {
this.sendQueue.push(payload); this.sendQueue.push(payload);
}; };
/// On incoming message sends the message to the provider that is currently being used
Object.defineProperty(AutoProvider.prototype, 'onmessage', { Object.defineProperty(AutoProvider.prototype, 'onmessage', {
set: function (handler) { set: function (handler) {
if (this.provider) { if (this.provider) {

29
libjsqrc/ethereumjs/lib/contract.js

@ -20,16 +20,32 @@
* @date 2014 * @date 2014
*/ */
// TODO: is these line is supposed to be here? var web3 = require('./web3'); // jshint ignore:line
if (process.env.NODE_ENV !== 'build') {
var web3 = require('./web3'); // jshint ignore:line
}
var abi = require('./abi'); var abi = require('./abi');
// method signature length in bytes /// method signature length in bytes
var ETH_METHOD_SIGNATURE_LENGTH = 4; 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 contract = function (address, desc) {
var inputParser = abi.inputParser(desc); var inputParser = abi.inputParser(desc);
var outputParser = abi.outputParser(desc); var outputParser = abi.outputParser(desc);
@ -70,3 +86,4 @@ var contract = function (address, desc) {
}; };
module.exports = contract; 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 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) { var HttpRpcProvider = function (host) {
this.handlers = []; this.handlers = [];
this.host = host; this.host = host;
}; };
/// Transforms inner message to proper jsonrpc object
/// @param inner message object
/// @returns jsonrpc object
function formatJsonRpcObject(object) { function formatJsonRpcObject(object) {
return { return {
jsonrpc: '2.0', 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) { function formatJsonRpcMessage(message) {
var object = JSON.parse(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) { HttpRpcProvider.prototype.sendRequest = function (payload, cb) {
var data = formatJsonRpcObject(payload); 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) { HttpRpcProvider.prototype.send = function (payload) {
var self = this; var self = this;
this.sendRequest(payload, function (request) { 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) { HttpRpcProvider.prototype.poll = function (payload, id) {
var self = this; var self = this;
this.sendRequest(payload, function (request) { 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", { Object.defineProperty(HttpRpcProvider.prototype, "onmessage", {
set: function (handler) { set: function (handler) {
this.handlers.push(handler); this.handlers.push(handler);
@ -92,3 +123,4 @@ Object.defineProperty(HttpRpcProvider.prototype, "onmessage", {
}); });
module.exports = HttpRpcProvider; 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 * @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() { var QtProvider = function() {
this.handlers = []; 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) { QtProvider.prototype.send = function(payload) {
navigator.qt.postMessage(JSON.stringify(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", { Object.defineProperty(QtProvider.prototype, "onmessage", {
set: function(handler) { set: function(handler) {
this.handlers.push(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 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/>. along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file main.js /** @file web3.js
* @authors: * @authors:
* Jeffrey Wilcke <jeff@ethdev.com> * Jeffrey Wilcke <jeff@ethdev.com>
* Marek Kotewicz <marek@ethdev.com> * Marek Kotewicz <marek@ethdev.com>
@ -23,6 +23,9 @@
* @date 2014 * @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) { function flattenPromise (obj) {
if (obj instanceof Promise) { if (obj instanceof Promise) {
return Promise.resolve(obj); return Promise.resolve(obj);
@ -62,12 +65,14 @@ function flattenPromise (obj) {
return Promise.resolve(obj); return Promise.resolve(obj);
} }
/// @returns an array of objects describing web3 api methods
var web3Methods = function () { var web3Methods = function () {
return [ return [
{ name: 'sha3', call: 'web3_sha3' } { name: 'sha3', call: 'web3_sha3' }
]; ];
}; };
/// @returns an array of objects describing web3.eth api methods
var ethMethods = function () { var ethMethods = function () {
var blockCall = function (args) { var blockCall = function (args) {
return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber"; return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber";
@ -101,6 +106,7 @@ var ethMethods = function () {
return methods; return methods;
}; };
/// @returns an array of objects describing web3.eth api properties
var ethProperties = function () { var ethProperties = function () {
return [ return [
{ name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' }, { 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 () { var dbMethods = function () {
return [ return [
{ name: 'put', call: 'db_put' }, { 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 () { var shhMethods = function () {
return [ return [
{ name: 'post', call: 'shh_post' }, { 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 ethWatchMethods = function () {
var newFilter = function (args) { var newFilter = function (args) {
return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter'; 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 () { var shhWatchMethods = function () {
return [ return [
{ name: 'newFilter', call: 'shh_newFilter' }, { 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) { var setupMethods = function (obj, methods) {
methods.forEach(function (method) { methods.forEach(function (method) {
obj[method.name] = function () { 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) { var setupProperties = function (obj, properties) {
properties.forEach(function (property) { properties.forEach(function (property) {
var proto = {}; var proto = {};
@ -221,7 +235,7 @@ var decToHex = function (dec) {
return parseInt(dec).toString(16); return parseInt(dec).toString(16);
}; };
/// setups web3 object, and it's in-browser executed methods
var web3 = { var web3 = {
_callbacks: {}, _callbacks: {},
_events: {}, _events: {},
@ -237,6 +251,7 @@ var web3 = {
return hex; return hex;
}, },
/// @returns ascii string representation of hex value prefixed with 0x
toAscii: function(hex) { toAscii: function(hex) {
// Find termination // Find termination
var str = ""; var str = "";
@ -244,17 +259,18 @@ var web3 = {
if (hex.substring(0, 2) === '0x') if (hex.substring(0, 2) === '0x')
i = 2; i = 2;
for(; i < l; i+=2) { for(; i < l; i+=2) {
var code = hex.charCodeAt(i); var code = parseInt(hex.substr(i, 2), 16);
if(code === 0) { if(code === 0) {
break; break;
} }
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); str += String.fromCharCode(code);
} }
return str; return str;
}, },
/// @returns hex representation (prefixed by 0x) of ascii string
fromAscii: function(str, pad) { fromAscii: function(str, pad) {
pad = pad === undefined ? 0 : pad; pad = pad === undefined ? 0 : pad;
var hex = this.toHex(str); var hex = this.toHex(str);
@ -263,14 +279,17 @@ var web3 = {
return "0x" + hex; return "0x" + hex;
}, },
/// @returns decimal representaton of hex value prefixed by 0x
toDecimal: function (val) { toDecimal: function (val) {
return hexToDec(val.substring(2)); return hexToDec(val.substring(2));
}, },
/// @returns hex representation (prefixed by 0x) of decimal value
fromDecimal: function (val) { fromDecimal: function (val) {
return "0x" + decToHex(val); return "0x" + decToHex(val);
}, },
/// used to transform value/string to eth string
toEth: function(str) { toEth: function(str) {
var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str; var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;
var unit = 0; var unit = 0;
@ -294,24 +313,24 @@ var web3 = {
return s + ' ' + units[unit]; return s + ' ' + units[unit];
}, },
/// eth object prototype
eth: { eth: {
prototype: Object(), // jshint ignore:line
watch: function (params) { watch: function (params) {
return new Filter(params, ethWatch); return new web3.filter(params, ethWatch);
} }
}, },
db: { /// db object prototype
prototype: Object() // jshint ignore:line db: {},
},
/// shh object prototype
shh: { shh: {
prototype: Object(), // jshint ignore:line
watch: function (params) { 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) { on: function(event, id, cb) {
if(web3._events[event] === undefined) { if(web3._events[event] === undefined) {
web3._events[event] = {}; web3._events[event] = {};
@ -321,6 +340,7 @@ var web3 = {
return this; return this;
}, },
/// used by filter to unregister callback with given id
off: function(event, id) { off: function(event, id) {
if(web3._events[event] !== undefined) { if(web3._events[event] !== undefined) {
delete web3._events[event][id]; delete web3._events[event][id];
@ -329,6 +349,7 @@ var web3 = {
return this; return this;
}, },
/// used to trigger callback registered by filter
trigger: function(event, id, data) { trigger: function(event, id, data) {
var callbacks = web3._events[event]; var callbacks = web3._events[event];
if (!callbacks || !callbacks[id]) { if (!callbacks || !callbacks[id]) {
@ -336,9 +357,15 @@ var web3 = {
} }
var cb = callbacks[id]; var cb = callbacks[id];
cb(data); cb(data);
},
/// @returns true if provider is installed
haveProvider: function() {
return !!web3.provider.provider;
} }
}; };
/// setups all api methods
setupMethods(web3, web3Methods()); setupMethods(web3, web3Methods());
setupMethods(web3.eth, ethMethods()); setupMethods(web3.eth, ethMethods());
setupProperties(web3.eth, ethProperties()); setupProperties(web3.eth, ethProperties());
@ -348,87 +375,14 @@ setupMethods(web3.shh, shhMethods());
var ethWatch = { var ethWatch = {
changed: 'eth_changed' changed: 'eth_changed'
}; };
setupMethods(ethWatch, ethWatchMethods()); setupMethods(ethWatch, ethWatchMethods());
var shhWatch = { var shhWatch = {
changed: 'shh_changed' 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) { web3.setProvider = function(provider) {
provider.onmessage = messageHandler; provider.onmessage = messageHandler;
@ -436,60 +390,7 @@ web3.setProvider = function(provider) {
web3.provider.sendQueued(); web3.provider.sendQueued();
}; };
web3.haveProvider = function() { /// callled when there is new incoming message
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();
};
function messageHandler(data) { function messageHandler(data) {
if(data._event !== undefined) { if(data._event !== undefined) {
web3.trigger(data._event, data._id, data.data); 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 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) { var WebSocketProvider = function(host) {
// onmessage handlers // onmessage handlers
this.handlers = []; this.handlers = [];
// queue will be filled with messages if send is invoked before the ws is ready // queue will be filled with messages if send is invoked before the ws is ready
this.queued = []; this.queued = [];
this.ready = false; this.ready = false;
@ -46,15 +54,20 @@ var WebSocketProvider = function(host) {
this.ws.onopen = function() { this.ws.onopen = function() {
self.ready = true; self.ready = true;
for(var i = 0; i < self.queued.length; i++) { for (var i = 0; i < self.queued.length; i++) {
// Resend // Resend
self.send(self.queued[i]); 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) { WebSocketProvider.prototype.send = function(payload) {
if(this.ready) { if (this.ready) {
var data = JSON.stringify(payload); var data = JSON.stringify(payload);
this.ws.send(data); 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) { WebSocketProvider.prototype.onMessage = function(handler) {
this.handlers.push(handler); this.handlers.push(handler);
}; };
/// Prototype object method
/// Should be called to close websockets connection
WebSocketProvider.prototype.unload = function() { WebSocketProvider.prototype.unload = function() {
this.ws.close(); this.ws.close();
}; };
/// Prototype object property
/// Should be used to set message handlers for this provider
Object.defineProperty(WebSocketProvider.prototype, "onmessage", { Object.defineProperty(WebSocketProvider.prototype, "onmessage", {
set: function(provider) { this.onMessage(provider); } set: function(provider) { this.onMessage(provider); }
}); });

5
libjsqrc/ethereumjs/package.json

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

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

@ -1,14 +1,11 @@
var assert = require('assert'); var assert = require('assert');
var BigNumber = require('bignumber.js');
var abi = require('../lib/abi.js'); var abi = require('../lib/abi.js');
var clone = function (object) { return JSON.parse(JSON.stringify(object)); };
describe('abi', function() { var description = [{
describe('inputParser', function() { "name": "test",
it('should parse ...', function() { "inputs": [{
var desc = [{
"name": "multiply",
"inputs": [
{
"name": "a", "name": "a",
"type": "uint256" "type": "uint256"
} }
@ -19,19 +16,812 @@ describe('abi', function() {
"type": "uint256" "type": "uint256"
} }
] ]
}];
describe('abi', function() {
describe('inputParser', function() {
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); // when
assert.equal(iParser.multiply(1), "0x000000000000000000000000000000000000000000000000000000000000000001"); 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() { 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
);
});
}); });
}); });

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

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

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

@ -7,7 +7,6 @@ web3.setProvider(new web3.providers.WebSocketProvider('http://localhost:8080'));
describe('web3', function() { describe('web3', function() {
describe('eth', function() { describe('eth', function() {
it('should have all methods implemented', function() {
u.methodExists(web3.eth, 'balanceAt'); u.methodExists(web3.eth, 'balanceAt');
u.methodExists(web3.eth, 'stateAt'); u.methodExists(web3.eth, 'stateAt');
u.methodExists(web3.eth, 'storageAt'); u.methodExists(web3.eth, 'storageAt');
@ -23,9 +22,7 @@ describe('web3', function() {
u.methodExists(web3.eth, 'solidity'); u.methodExists(web3.eth, 'solidity');
u.methodExists(web3.eth, 'serpent'); u.methodExists(web3.eth, 'serpent');
u.methodExists(web3.eth, 'logs'); u.methodExists(web3.eth, 'logs');
});
it('should have all properties implemented', function () {
u.propertyExists(web3.eth, 'coinbase'); u.propertyExists(web3.eth, 'coinbase');
u.propertyExists(web3.eth, 'listening'); u.propertyExists(web3.eth, 'listening');
u.propertyExists(web3.eth, 'mining'); u.propertyExists(web3.eth, 'mining');
@ -36,7 +33,6 @@ describe('web3', function() {
u.propertyExists(web3.eth, 'defaultBlock'); u.propertyExists(web3.eth, 'defaultBlock');
u.propertyExists(web3.eth, 'number'); u.propertyExists(web3.eth, 'number');
}); });
});
}); });

2
libjsqrc/ethereumjs/test/mocha.opts

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

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

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

4
libjsqrc/ethereumjs/test/utils.js

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

2
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 web3.setProvider(new web3.providers.WebSocketProvider('http://localhost:8080')); // TODO: create some mock provider
describe('web3', function() { describe('web3', function() {
it('should have all methods implemented', function() {
u.methodExists(web3, 'sha3'); u.methodExists(web3, 'sha3');
u.methodExists(web3, 'toAscii'); u.methodExists(web3, 'toAscii');
u.methodExists(web3, 'fromAscii'); u.methodExists(web3, 'fromAscii');
u.methodExists(web3, 'toFixed'); u.methodExists(web3, 'toFixed');
u.methodExists(web3, 'fromFixed'); u.methodExists(web3, 'fromFixed');
u.methodExists(web3, 'offset'); u.methodExists(web3, 'offset');
});
}); });

1
libjsqrc/js.qrc

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

1
liblll/CodeFragment.cpp

@ -266,7 +266,6 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
} }
++ii; ++ii;
} }
} }
else if (us == "LIT") 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("_web3", qweb, QWebFrame::ScriptOwnership); \
_frame->addToJavaScriptWindowObject("env", _env, QWebFrame::QtOwnership); \ _frame->addToJavaScriptWindowObject("env", _env, QWebFrame::QtOwnership); \
_frame->evaluateJavaScript(contentsOfQResource(":/js/es6-promise-2.0.0.js")); \ _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/webthree.js")); \
_frame->evaluateJavaScript(contentsOfQResource(":/js/setup.js")); \ _frame->evaluateJavaScript(contentsOfQResource(":/js/setup.js")); \
} }

66
libsolidity/AST.cpp

@ -43,6 +43,11 @@ TypeError ASTNode::createTypeError(string const& _description) const
void ContractDefinition::checkTypeRequirements() void ContractDefinition::checkTypeRequirements()
{ {
for (ASTPointer<InheritanceSpecifier> const& base: getBaseContracts())
base->checkTypeRequirements();
checkIllegalOverrides();
FunctionDefinition const* constructor = getConstructor(); FunctionDefinition const* constructor = getConstructor();
if (constructor && !constructor->getReturnParameters().empty()) if (constructor && !constructor->getReturnParameters().empty())
BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError( BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError(
@ -52,7 +57,6 @@ void ContractDefinition::checkTypeRequirements()
function->checkTypeRequirements(); function->checkTypeRequirements();
// check for hash collisions in function signatures // check for hash collisions in function signatures
vector<pair<FixedHash<4>, FunctionDefinition const*>> exportedFunctionList = getInterfaceFunctionList();
set<FixedHash<4>> hashes; set<FixedHash<4>> hashes;
for (auto const& hashAndFunction: getInterfaceFunctionList()) for (auto const& hashAndFunction: getInterfaceFunctionList())
{ {
@ -78,22 +82,63 @@ map<FixedHash<4>, FunctionDefinition const*> ContractDefinition::getInterfaceFun
FunctionDefinition const* ContractDefinition::getConstructor() const FunctionDefinition const* ContractDefinition::getConstructor() const
{ {
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions) for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
if (f->getName() == getName()) if (f->isConstructor())
return f.get(); return f.get();
return nullptr; return nullptr;
} }
vector<pair<FixedHash<4>, FunctionDefinition const*>> ContractDefinition::getInterfaceFunctionList() const void ContractDefinition::checkIllegalOverrides() const
{ {
vector<pair<FixedHash<4>, FunctionDefinition const*>> exportedFunctions; map<string, FunctionDefinition const*> functions;
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
if (f->isPublic() && f->getName() != getName()) // 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())
{
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."));
}
}
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())); FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
exportedFunctions.push_back(make_pair(hash, f.get())); m_interfaceFunctionList->push_back(make_pair(hash, f.get()));
} }
}
return *m_interfaceFunctionList;
}
return exportedFunctions; 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 void StructDefinition::checkMemberTypes() const
@ -346,7 +391,8 @@ void MemberAccess::checkTypeRequirements()
Type const& type = *m_expression->getType(); Type const& type = *m_expression->getType();
m_type = type.getMemberType(*m_memberName); m_type = type.getMemberType(*m_memberName);
if (!m_type) 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 //@todo later, this will not always be STORAGE
m_lvalue = type.getCategory() == Type::Category::STRUCT ? LValueType::STORAGE : LValueType::NONE; 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); ContractDefinition const* contractDef = dynamic_cast<ContractDefinition const*>(m_referencedDeclaration);
if (contractDef) if (contractDef)
{ {
m_type = make_shared<TypeType>(make_shared<ContractType>(*contractDef)); m_type = make_shared<TypeType>(make_shared<ContractType>(*contractDef), m_currentContract);
return; return;
} }
MagicVariableDeclaration const* magicVariable = dynamic_cast<MagicVariableDeclaration const*>(m_referencedDeclaration); MagicVariableDeclaration const* magicVariable = dynamic_cast<MagicVariableDeclaration const*>(m_referencedDeclaration);

64
libsolidity/AST.h

@ -158,10 +158,12 @@ public:
ContractDefinition(Location const& _location, ContractDefinition(Location const& _location,
ASTPointer<ASTString> const& _name, ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation, ASTPointer<ASTString> const& _documentation,
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
std::vector<ASTPointer<StructDefinition>> const& _definedStructs, std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables, std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions): std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions):
Declaration(_location, _name), Declaration(_location, _name),
m_baseContracts(_baseContracts),
m_definedStructs(_definedStructs), m_definedStructs(_definedStructs),
m_stateVariables(_stateVariables), m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions), m_definedFunctions(_definedFunctions),
@ -171,12 +173,13 @@ public:
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const 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<StructDefinition>> const& getDefinedStructs() const { return m_definedStructs; }
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; } std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; }
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; } std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; }
/// Checks that the constructor does not have a "returns" statement and calls /// Checks that there are no illegal overrides, that the constructor does not have a "returns"
/// checkTypeRequirements on all its functions. /// and calls checkTypeRequirements on all its functions.
void checkTypeRequirements(); void checkTypeRequirements();
/// @return A shared pointer of an ASTString. /// @return A shared pointer of an ASTString.
@ -187,16 +190,47 @@ public:
/// as intended for use by the ABI. /// as intended for use by the ABI.
std::map<FixedHash<4>, FunctionDefinition const*> getInterfaceFunctions() const; 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 /// Returns the constructor or nullptr if no constructor was specified
FunctionDefinition const* getConstructor() const; FunctionDefinition const* getConstructor() const;
private: 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<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables; std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions; std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
ASTPointer<ASTString> m_documentation; 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 class StructDefinition: public Declaration
@ -247,12 +281,13 @@ class FunctionDefinition: public Declaration
public: public:
FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name, FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name,
bool _isPublic, bool _isPublic,
bool _isConstructor,
ASTPointer<ASTString> const& _documentation, ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters, ASTPointer<ParameterList> const& _parameters,
bool _isDeclaredConst, bool _isDeclaredConst,
ASTPointer<ParameterList> const& _returnParameters, ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body): ASTPointer<Block> const& _body):
Declaration(_location, _name), m_isPublic(_isPublic), Declaration(_location, _name), m_isPublic(_isPublic), m_isConstructor(_isConstructor),
m_parameters(_parameters), m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst), m_isDeclaredConst(_isDeclaredConst),
m_returnParameters(_returnParameters), m_returnParameters(_returnParameters),
@ -264,6 +299,7 @@ public:
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
bool isPublic() const { return m_isPublic; } bool isPublic() const { return m_isPublic; }
bool isConstructor() const { return m_isConstructor; }
bool isDeclaredConst() const { return m_isDeclaredConst; } bool isDeclaredConst() const { return m_isDeclaredConst; }
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); } std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
ParameterList const& getParameterList() const { return *m_parameters; } ParameterList const& getParameterList() const { return *m_parameters; }
@ -287,6 +323,7 @@ public:
private: private:
bool m_isPublic; bool m_isPublic;
bool m_isConstructor;
ASTPointer<ParameterList> m_parameters; ASTPointer<ParameterList> m_parameters;
bool m_isDeclaredConst; bool m_isDeclaredConst;
ASTPointer<ParameterList> m_returnParameters; ASTPointer<ParameterList> m_returnParameters;
@ -581,7 +618,7 @@ public:
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; } void setFunctionReturnParameters(ParameterList const& _parameters) { m_returnParameters = &_parameters; }
ParameterList const& getFunctionReturnParameters() const ParameterList const& getFunctionReturnParameters() const
{ {
solAssert(m_returnParameters, ""); solAssert(m_returnParameters, "");
@ -593,7 +630,7 @@ private:
ASTPointer<Expression> m_expression; ///< value to return, optional ASTPointer<Expression> m_expression; ///< value to return, optional
/// Pointer to the parameter list of the function, filled by the @ref NameAndTypeResolver. /// 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: public:
Identifier(Location const& _location, ASTPointer<ASTString> const& _name): 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(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
ASTString const& getName() const { return *m_name; } 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; } Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; }
ContractDefinition const* getCurrentContract() const { return m_currentContract; }
private: private:
ASTPointer<ASTString> m_name; ASTPointer<ASTString> m_name;
/// Declaration the name refers to. /// 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 ImportDirective;
class Declaration; class Declaration;
class ContractDefinition; class ContractDefinition;
class InheritanceSpecifier;
class StructDefinition; class StructDefinition;
class ParameterList; class ParameterList;
class FunctionDefinition; 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)) if (_visitor.visit(*this))
{ {
listAccept(m_baseContracts, _visitor);
listAccept(m_definedStructs, _visitor); listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor); listAccept(m_stateVariables, _visitor);
listAccept(m_definedFunctions, _visitor); listAccept(m_definedFunctions, _visitor);
@ -72,6 +73,7 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{ {
listAccept(m_baseContracts, _visitor);
listAccept(m_definedStructs, _visitor); listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor); listAccept(m_stateVariables, _visitor);
listAccept(m_definedFunctions, _visitor); listAccept(m_definedFunctions, _visitor);
@ -79,6 +81,26 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this); _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) void StructDefinition::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))

53
libsolidity/CallGraph.cpp

@ -31,17 +31,14 @@ namespace dev
namespace solidity namespace solidity
{ {
void CallGraph::addFunction(FunctionDefinition const& _function) void CallGraph::addNode(ASTNode const& _node)
{ {
if (!m_functionsSeen.count(&_function)) _node.accept(*this);
{
m_functionsSeen.insert(&_function);
m_workQueue.push(&_function);
}
} }
set<FunctionDefinition const*> const& CallGraph::getCalls() set<FunctionDefinition const*> const& CallGraph::getCalls()
{ {
computeCallGraph();
return m_functionsSeen; return m_functionsSeen;
} }
@ -49,8 +46,7 @@ void CallGraph::computeCallGraph()
{ {
while (!m_workQueue.empty()) while (!m_workQueue.empty())
{ {
FunctionDefinition const* fun = m_workQueue.front(); m_workQueue.front()->accept(*this);
fun->accept(*this);
m_workQueue.pop(); m_workQueue.pop();
} }
} }
@ -59,9 +55,50 @@ bool CallGraph::visit(Identifier const& _identifier)
{ {
FunctionDefinition const* fun = dynamic_cast<FunctionDefinition const*>(_identifier.getReferencedDeclaration()); FunctionDefinition const* fun = dynamic_cast<FunctionDefinition const*>(_identifier.getReferencedDeclaration());
if (fun) if (fun)
{
if (m_overrideResolver)
fun = (*m_overrideResolver)(fun->getName());
solAssert(fun, "Error finding override for function " + fun->getName());
addFunction(*fun); 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; 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 <set>
#include <queue> #include <queue>
#include <functional>
#include <boost/range/iterator_range.hpp> #include <boost/range/iterator_range.hpp>
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
@ -38,15 +39,23 @@ namespace solidity
class CallGraph: private ASTConstVisitor class CallGraph: private ASTConstVisitor
{ {
public: public:
void addFunction(FunctionDefinition const& _function); using OverrideResolver = std::function<FunctionDefinition const*(std::string const&)>;
void computeCallGraph();
CallGraph(OverrideResolver const& _overrideResolver): m_overrideResolver(&_overrideResolver) {}
void addNode(ASTNode const& _node);
std::set<FunctionDefinition const*> const& getCalls(); std::set<FunctionDefinition const*> const& getCalls();
private: private:
void addFunctionToQueue(FunctionDefinition const& _function); virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(Identifier const& _identifier) 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::set<FunctionDefinition const*> m_functionsSeen;
std::queue<FunctionDefinition const*> m_workQueue; std::queue<FunctionDefinition const*> m_workQueue;
}; };

114
libsolidity/Compiler.cpp

@ -21,6 +21,7 @@
*/ */
#include <algorithm> #include <algorithm>
#include <boost/range/adaptor/reversed.hpp>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include <libevmcore/Assembly.h> #include <libevmcore/Assembly.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
@ -34,48 +35,103 @@ using namespace std;
namespace dev { namespace dev {
namespace solidity { 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) map<ContractDefinition const*, bytes const*> const& _contracts)
{ {
m_context = CompilerContext(); // clear it just in case m_context = CompilerContext(); // clear it just in case
initializeContext(_contract, _magicGlobals, _contracts); initializeContext(_contract, _contracts);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions()) for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts())
if (function->getName() != _contract.getName()) // don't add the constructor here for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
if (!function->isConstructor())
m_context.addFunction(*function); m_context.addFunction(*function);
appendFunctionSelector(_contract); appendFunctionSelector(_contract);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions()) for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts())
if (function->getName() != _contract.getName()) // don't add the constructor here for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
if (!function->isConstructor())
function->accept(*this); function->accept(*this);
// Swap the runtime context with the creation-time context // Swap the runtime context with the creation-time context
swap(m_context, m_runtimeContext); swap(m_context, m_runtimeContext);
initializeContext(_contract, _magicGlobals, _contracts); initializeContext(_contract, _contracts);
packIntoContractCreator(_contract, m_runtimeContext); 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) map<ContractDefinition const*, bytes const*> const& _contracts)
{ {
m_context.setCompiledContracts(_contracts); m_context.setCompiledContracts(_contracts);
for (MagicVariableDeclaration const* variable: _magicGlobals)
m_context.addMagicGlobal(*variable);
registerStateVariables(_contract); registerStateVariables(_contract);
} }
void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext) 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; set<FunctionDefinition const*> neededFunctions;
FunctionDefinition const* constructor = _contract.getConstructor(); set<ASTNode const*> nodesUsedInConstructors;
if (constructor)
neededFunctions = getFunctionsNeededByConstructor(*constructor);
// 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) for (FunctionDefinition const* fun: neededFunctions)
{
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); m_context.addFunction(*fun);
if (constructor) // Call constructors in base-to-derived order.
appendConstructorCall(*constructor); // 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()); eth::AssemblyItem sub = m_context.addSubroutine(_runtimeContext.getAssembly());
// stack contains sub size // stack contains sub size
@ -88,6 +144,21 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp
fun->accept(*this); 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) void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
{ {
eth::AssemblyItem returnTag = m_context.pushNewTag(); eth::AssemblyItem returnTag = m_context.pushNewTag();
@ -107,11 +178,12 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
m_context << returnTag; 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 callgraph(_resolveOverrides);
callgraph.addFunction(_constructor); for (ASTNode const* node: _nodes)
callgraph.computeCallGraph(); callgraph.addNode(*node);
return callgraph.getCalls(); return callgraph.getCalls();
} }
@ -193,8 +265,8 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
void Compiler::registerStateVariables(ContractDefinition const& _contract) void Compiler::registerStateVariables(ContractDefinition const& _contract)
{ {
//@todo sort them? for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.getLinearizedBaseContracts()))
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables()) for (ASTPointer<VariableDeclaration> const& variable: contract->getStateVariables())
m_context.addStateVariable(*variable); m_context.addStateVariable(*variable);
} }

18
libsolidity/Compiler.h

@ -21,6 +21,7 @@
*/ */
#include <ostream> #include <ostream>
#include <functional>
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
#include <libsolidity/CompilerContext.h> #include <libsolidity/CompilerContext.h>
@ -32,23 +33,26 @@ class Compiler: private ASTConstVisitor
public: public:
explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_context(), m_returnTag(m_context.newTag()) {} 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); std::map<ContractDefinition const*, bytes const*> const& _contracts);
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); } bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); }
bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);} bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);}
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); } void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
private: private:
/// Registers the global objects and the non-function objects inside the contract with the context. /// Registers the non-function objects inside the contract with the context.
void initializeContext(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals, void initializeContext(ContractDefinition const& _contract,
std::map<ContractDefinition const*, bytes const*> const& _contracts); 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 /// Adds the code that is run at creation time. Should be run after exchanging the run-time context
/// with a new and initialized context. /// with a new and initialized context. Adds the constructor code.
/// adds the constructor code.
void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext); void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext);
void appendBaseConstructorCall(FunctionDefinition const& _constructor,
std::vector<ASTPointer<Expression>> const& _arguments);
void appendConstructorCall(FunctionDefinition const& _constructor); void appendConstructorCall(FunctionDefinition const& _constructor);
/// Recursively searches the call graph and returns all functions needed by the constructor (including itself). /// Recursively searches the call graph and returns all functions referenced inside _nodes.
std::set<FunctionDefinition const*> getFunctionsNeededByConstructor(FunctionDefinition const& _constructor); /// _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); void appendFunctionSelector(ContractDefinition const& _contract);
/// Creates code that unpacks the arguments for the given function, from memory if /// 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. /// @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) 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 bytes const& CompilerContext::getCompiledContract(const ContractDefinition& _contract) const
@ -83,6 +85,13 @@ eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition cons
return res->second.tag(); 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 unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const
{ {
auto res = m_localVariables.find(&_declaration); 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; } bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration) != 0; }
eth::AssemblyItem getFunctionEntryLabel(FunctionDefinition const& _function) const; 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. /// Returns the distance of the given local variable from the top of the local variable stack.
unsigned getBaseStackOffsetOfVariable(Declaration const& _declaration) const; unsigned getBaseStackOffsetOfVariable(Declaration const& _declaration) const;
/// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns /// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns
@ -116,6 +118,8 @@ private:
unsigned m_localVariablesSize; unsigned m_localVariablesSize;
/// Labels pointing to the entry points of funcitons. /// Labels pointing to the entry points of funcitons.
std::map<Declaration const*, eth::AssemblyItem> m_functionEntryLabels; 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. * Full-stack compiler that converts a source code string to bytecode.
*/ */
#include <boost/algorithm/string.hpp>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h> #include <libsolidity/Parser.h>
@ -113,10 +114,8 @@ void CompilerStack::compile(bool _optimize)
for (ASTPointer<ASTNode> const& node: source->ast->getNodes()) for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{ {
m_globalContext->setCurrentContract(*contract);
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize); shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
compiler->compileContract(*contract, m_globalContext->getMagicVariables(), compiler->compileContract(*contract, contractBytecode);
contractBytecode);
Contract& compiledContract = m_contracts[contract->getName()]; Contract& compiledContract = m_contracts[contract->getName()];
compiledContract.bytecode = compiler->getAssembledBytecode(); compiledContract.bytecode = compiler->getAssembledBytecode();
compiledContract.runtimeBytecode = compiler->getRuntimeBytecode(); 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) bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
{ {
parse(_sourceCode); parse(expanded(_sourceCode));
compile(_optimize); compile(_optimize);
return getBytecode(); return getBytecode();
} }

4
libsolidity/CompilerStack.h

@ -138,6 +138,10 @@ private:
Contract(); 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 reset(bool _keepSources = false);
void resolveImports(); void resolveImports();

5
libsolidity/DeclarationContainer.h

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

126
libsolidity/ExpressionCompiler.cpp

@ -94,13 +94,8 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
m_context << eth::Instruction::NOT; m_context << eth::Instruction::NOT;
break; break;
case Token::DELETE: // delete case Token::DELETE: // delete
// @todo semantics change for complex types
solAssert(m_currentLValue.isValid(), "LValue not retrieved."); solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
m_currentLValue.setToZero(_unaryOperation);
m_context << u256(0);
if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1;
m_currentLValue.storeValue(_unaryOperation);
m_currentLValue.reset(); m_currentLValue.reset();
break; break;
case Token::INC: // ++ (pre- or postfix) case Token::INC: // ++ (pre- or postfix)
@ -397,9 +392,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
m_context << eth::Instruction::COINBASE; m_context << eth::Instruction::COINBASE;
else if (member == "timestamp") else if (member == "timestamp")
m_context << eth::Instruction::TIMESTAMP; m_context << eth::Instruction::TIMESTAMP;
/* else if (member == "blockhash") else if (member == "difficulty")
m_context << eth::Instruction::BLOCKHASH;
*/ else if (member == "difficulty")
m_context << eth::Instruction::DIFFICULTY; m_context << eth::Instruction::DIFFICULTY;
else if (member == "number") else if (member == "number")
m_context << eth::Instruction::NUMBER; m_context << eth::Instruction::NUMBER;
@ -426,6 +419,22 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess); m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
break; 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: default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to unknown type.")); 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" if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) // must be "this"
m_context << eth::Instruction::ADDRESS; m_context << eth::Instruction::ADDRESS;
return;
} }
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration)) else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
{ m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag();
m_context << m_context.getFunctionEntryLabel(*functionDef).pushTag(); else if (dynamic_cast<VariableDeclaration const*>(declaration))
return;
}
if (dynamic_cast<VariableDeclaration const*>(declaration))
{ {
m_currentLValue.fromIdentifier(_identifier, *declaration); m_currentLValue.fromIdentifier(_identifier, *declaration);
m_currentLValue.retrieveValueIfLValueNotRequested(_identifier); m_currentLValue.retrieveValueIfLValueNotRequested(_identifier);
return;
} }
else if (dynamic_cast<ContractDefinition const*>(declaration))
{
// no-op
}
else
{
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context."));
}
} }
void ExpressionCompiler::endVisit(Literal const& _literal) 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, ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,
unsigned _baseStackOffset): unsigned _baseStackOffset):
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset), m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset)
m_stackSize(_dataType.getSizeOnStack())
{ {
//@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 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 if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep.")); << 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); *m_context << eth::dupInstruction(stackPos + 1);
break; break;
} }
@ -777,13 +793,13 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
break; // no distinction between value and reference for non-value types break; // no distinction between value and reference for non-value types
if (!_remove) if (!_remove)
*m_context << eth::Instruction::DUP1; *m_context << eth::Instruction::DUP1;
if (m_stackSize == 1) if (m_size == 1)
*m_context << eth::Instruction::SLOAD; *m_context << eth::Instruction::SLOAD;
else 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; *m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1;
if (i + 1 < m_stackSize) if (i + 1 < m_size)
*m_context << u256(1) << eth::Instruction::ADD; *m_context << u256(1) << eth::Instruction::ADD;
else else
*m_context << eth::Instruction::POP; *m_context << eth::Instruction::POP;
@ -808,12 +824,12 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
{ {
case STACK: 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) if (stackDiff > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep.")); << errinfo_comment("Stack too deep."));
else if (stackDiff > 0) 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; *m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP;
if (!_move) if (!_move)
retrieveValue(_expression); retrieveValue(_expression);
@ -825,17 +841,17 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
// stack layout: value value ... value ref // stack layout: value value ... value ref
if (!_move) // copy values if (!_move) // copy values
{ {
if (m_stackSize + 1 > 16) if (m_size + 1 > 16)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Stack too deep.")); << 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(m_stackSize + 1) << eth::Instruction::SWAP1; *m_context << eth::dupInstruction(m_size + 1) << eth::Instruction::SWAP1;
} }
if (m_stackSize > 0) // store high index value first if (m_size > 0) // store high index value first
*m_context << u256(m_stackSize - 1) << eth::Instruction::ADD; *m_context << u256(m_size - 1) << eth::Instruction::ADD;
for (unsigned i = 0; i < m_stackSize; ++i) for (unsigned i = 0; i < m_size; ++i)
{ {
if (i + 1 >= m_stackSize) if (i + 1 >= m_size)
*m_context << eth::Instruction::SSTORE; *m_context << eth::Instruction::SSTORE;
else else
// v v ... v v r+x // 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) void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression const& _expression)
{ {
if (!_expression.lvalueRequested()) if (!_expression.lvalueRequested())
@ -868,15 +926,17 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co
void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration) void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration)
{ {
m_stackSize = _identifier.getType()->getSizeOnStack();
if (m_context->isLocalVariable(&_declaration)) if (m_context->isLocalVariable(&_declaration))
{ {
m_type = STACK; m_type = STACK;
m_size = _identifier.getType()->getSizeOnStack();
m_baseStackOffset = m_context->getBaseStackOffsetOfVariable(_declaration); m_baseStackOffset = m_context->getBaseStackOffsetOfVariable(_declaration);
} }
else if (m_context->isStateVariable(&_declaration)) else if (m_context->isStateVariable(&_declaration))
{ {
m_type = STORAGE; 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); *m_context << m_context->getStorageLocationOfVariable(_declaration);
} }
else else

10
libsolidity/ExpressionCompiler.h

@ -111,7 +111,7 @@ private:
/// Set type according to the declaration and retrieve the reference. /// Set type according to the declaration and retrieve the reference.
/// @a _expression is the current expression /// @a _expression is the current expression
void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration); 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 isValid() const { return m_type != NONE; }
bool isInOnStack() const { return m_type == STACK; } bool isInOnStack() const { return m_type == STACK; }
@ -130,7 +130,9 @@ private:
/// Also removes the stored value from the stack if @a _move is /// Also removes the stored value from the stack if @a _move is
/// true. @a _expression is the current expression, used for error reporting. /// true. @a _expression is the current expression, used for error reporting.
void storeValue(Expression const& _expression, bool _move = false) const; 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 /// Convenience function to convert the stored reference to a value and reset type to NONE if
/// the reference was not requested by @a _expression. /// the reference was not requested by @a _expression.
void retrieveValueIfLValueNotRequested(Expression const& _expression); void retrieveValueIfLValueNotRequested(Expression const& _expression);
@ -141,8 +143,8 @@ private:
/// If m_type is STACK, this is base stack offset (@see /// If m_type is STACK, this is base stack offset (@see
/// CompilerContext::getBaseStackOffsetOfVariable) of a local variable. /// CompilerContext::getBaseStackOffsetOfVariable) of a local variable.
unsigned m_baseStackOffset = 0; unsigned m_baseStackOffset = 0;
/// Size of the value of this lvalue on the stack. /// Size of the value of this lvalue on the stack or the storage.
unsigned m_stackSize = 0; unsigned m_size = 0;
}; };
bool m_optimize; 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*> GlobalContext::getDeclarations() const
{ {
vector<Declaration const*> declarations; vector<Declaration const*> declarations;
declarations.reserve(m_magicVariables.size() + 1); declarations.reserve(m_magicVariables.size());
for (ASTPointer<Declaration const> const& variable: m_magicVariables) for (ASTPointer<Declaration const> const& variable: m_magicVariables)
declarations.push_back(variable.get()); declarations.push_back(variable.get());
return declarations; 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); void setCurrentContract(ContractDefinition const& _contract);
MagicVariableDeclaration const* getCurrentThis() const; MagicVariableDeclaration const* getCurrentThis() const;
/// @returns all magic variables.
std::vector<MagicVariableDeclaration const*> getMagicVariables() const;
/// @returns a vector of all implicit global declarations excluding "this". /// @returns a vector of all implicit global declarations excluding "this".
std::vector<Declaration const*> getDeclarations() const; 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); currPos = appendDocTag(currPos, end, _owner);
else if (currPos != end) 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; 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; currPos = nlPos + 1;
} }
} }

116
libsolidity/NameAndTypeResolver.cpp

@ -31,7 +31,6 @@ namespace dev
namespace solidity namespace solidity
{ {
NameAndTypeResolver::NameAndTypeResolver(std::vector<Declaration const*> const& _globals) NameAndTypeResolver::NameAndTypeResolver(std::vector<Declaration const*> const& _globals)
{ {
for (Declaration const* declaration: _globals) for (Declaration const* declaration: _globals)
@ -46,18 +45,27 @@ void NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit)
void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) 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]; m_currentScope = &m_scopes[&_contract];
linearizeBaseContracts(_contract);
for (ContractDefinition const* base: _contract.getLinearizedBaseContracts())
importInheritedScope(*base);
for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs()) 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()) 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()) for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
{ {
m_currentScope = &m_scopes[function.get()]; m_currentScope = &m_scopes[function.get()];
ReferencesResolver referencesResolver(*function, *this, ReferencesResolver referencesResolver(*function, *this, &_contract,
function->getReturnParameterList().get()); function->getReturnParameterList().get());
} }
m_currentScope = &m_scopes[nullptr];
} }
void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract) void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract)
@ -86,6 +94,96 @@ Declaration const* NameAndTypeResolver::getNameFromCurrentScope(ASTString const&
return m_currentScope->resolveName(_name, _recursive); 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, DeclarationRegistrationHelper::DeclarationRegistrationHelper(map<ASTNode const*, DeclarationContainer>& _scopes,
ASTNode& _astRoot): ASTNode& _astRoot):
m_scopes(_scopes), m_currentScope(nullptr) m_scopes(_scopes), m_currentScope(nullptr)
@ -169,8 +267,10 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio
} }
ReferencesResolver::ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, ReferencesResolver::ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver,
ParameterList* _returnParameters, bool _allowLazyTypes): ContractDefinition const* _currentContract,
m_resolver(_resolver), m_returnParameters(_returnParameters), m_allowLazyTypes(_allowLazyTypes) ParameterList const* _returnParameters, bool _allowLazyTypes):
m_resolver(_resolver), m_currentContract(_currentContract),
m_returnParameters(_returnParameters), m_allowLazyTypes(_allowLazyTypes)
{ {
_root.accept(*this); _root.accept(*this);
} }
@ -218,7 +318,7 @@ bool ReferencesResolver::visit(Identifier& _identifier)
if (!declaration) if (!declaration)
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation()) BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation())
<< errinfo_comment("Undeclared identifier.")); << errinfo_comment("Undeclared identifier."));
_identifier.setReferencedDeclaration(*declaration); _identifier.setReferencedDeclaration(*declaration, m_currentContract);
return false; return false;
} }

19
libsolidity/NameAndTypeResolver.h

@ -23,6 +23,7 @@
#pragma once #pragma once
#include <map> #include <map>
#include <list>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <libsolidity/DeclarationContainer.h> #include <libsolidity/DeclarationContainer.h>
@ -64,6 +65,17 @@ public:
private: private:
void reset(); 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, /// 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 /// where nullptr denotes the global scope. Note that structs are not scope since they do
/// not contain code. /// not contain code.
@ -108,7 +120,9 @@ class ReferencesResolver: private ASTVisitor
{ {
public: public:
ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver,
ParameterList* _returnParameters, bool _allowLazyTypes = true); ContractDefinition const* _currentContract,
ParameterList const* _returnParameters,
bool _allowLazyTypes = true);
private: private:
virtual void endVisit(VariableDeclaration& _variable) override; virtual void endVisit(VariableDeclaration& _variable) override;
@ -118,7 +132,8 @@ private:
virtual bool visit(Return& _return) override; virtual bool visit(Return& _return) override;
NameAndTypeResolver& m_resolver; NameAndTypeResolver& m_resolver;
ParameterList* m_returnParameters; ContractDefinition const* m_currentContract;
ParameterList const* m_returnParameters;
bool m_allowLazyTypes; bool m_allowLazyTypes;
}; };

41
libsolidity/Parser.cpp

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

3
libsolidity/Parser.h

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

5
libsolidity/Token.h

@ -48,7 +48,8 @@
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
#if defined(DELETE) #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 #endif
namespace dev namespace dev
@ -152,7 +153,7 @@ namespace solidity
K(DEFAULT, "default", 0) \ K(DEFAULT, "default", 0) \
K(DO, "do", 0) \ K(DO, "do", 0) \
K(ELSE, "else", 0) \ K(ELSE, "else", 0) \
K(EXTENDS, "extends", 0) \ K(IS, "is", 0) \
K(FOR, "for", 0) \ K(FOR, "for", 0) \
K(FUNCTION, "function", 0) \ K(FUNCTION, "function", 0) \
K(IF, "if", 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 // "delete" is ok for all integer types
if (_operator == Token::DELETE) if (_operator == Token::DELETE)
return shared_from_this(); return make_shared<VoidType>();
// no further unary operators for addresses // no further unary operators for addresses
else if (isAddress()) else if (isAddress())
return TypePointer(); 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.")); 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 TypePointer BoolType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
{ {
if (getCategory() != _other->getCategory()) if (getCategory() != _other->getCategory())
@ -424,12 +431,24 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return true; return true;
if (_convertTo.getCategory() == Category::INTEGER) if (_convertTo.getCategory() == Category::INTEGER)
return dynamic_cast<IntegerType const&>(_convertTo).isAddress(); 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; return false;
} }
bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const 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 bool ContractType::operator==(Type const& _other) const
@ -440,14 +459,6 @@ bool ContractType::operator==(Type const& _other) const
return other.m_contract == m_contract; 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 string ContractType::toString() const
{ {
return "contract " + m_contract.getName(); return "contract " + m_contract.getName();
@ -491,6 +502,11 @@ u256 ContractType::getFunctionIdentifier(string const& _functionName) const
return Invalid256; return Invalid256;
} }
TypePointer StructType::unaryOperatorResult(Token::Value _operator) const
{
return _operator == Token::DELETE ? make_shared<VoidType>() : TypePointer();
}
bool StructType::operator==(Type const& _other) const bool StructType::operator==(Type const& _other) const
{ {
if (_other.getCategory() != getCategory()) if (_other.getCategory() != getCategory())
@ -686,6 +702,29 @@ bool TypeType::operator==(Type const& _other) const
return *getActualType() == *other.getActualType(); 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): MagicType::MagicType(MagicType::Kind _kind):
m_kind(_kind) m_kind(_kind)
{ {

21
libsolidity/Types.h

@ -259,10 +259,7 @@ public:
BoolType() {} BoolType() {}
virtual Category getCategory() const { return Category::BOOL; } virtual Category getCategory() const { return Category::BOOL; }
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
{
return (_operator == Token::NOT || _operator == Token::DELETE) ? shared_from_this() : TypePointer();
}
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
virtual unsigned getCalldataEncodedSize() const { return 1; } virtual unsigned getCalldataEncodedSize() const { return 1; }
@ -284,8 +281,8 @@ public:
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
/// Contracts can be converted to themselves and to integers. /// Contracts can be converted to themselves and to integers.
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; 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 bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override;
virtual bool isValueType() const override { return true; } virtual bool isValueType() const override { return true; }
virtual std::string toString() const override; virtual std::string toString() const override;
@ -317,11 +314,7 @@ class StructType: public Type
public: public:
virtual Category getCategory() const override { return Category::STRUCT; } virtual Category getCategory() const override { return Category::STRUCT; }
StructType(StructDefinition const& _struct): m_struct(_struct) {} StructType(StructDefinition const& _struct): m_struct(_struct) {}
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
{
return _operator == Token::DELETE ? shared_from_this() : TypePointer();
}
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override; virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override; virtual bool canLiveOutsideStorage() const override;
@ -449,7 +442,8 @@ class TypeType: public Type
{ {
public: public:
virtual Category getCategory() const override { return Category::TYPE; } 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; } TypePointer const& getActualType() const { return m_actualType; }
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } 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 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 bool canLiveOutsideStorage() const override { return false; }
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; } virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
virtual MemberList const& getMembers() const override;
private: private:
TypePointer m_actualType; 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 | ContractPart = VariableDeclaration ';' | StructDefinition |
FunctionDefinition | 'public:' | 'private:' FunctionDefinition | 'public:' | 'private:'
InheritanceSpecifier = Identifier ( '(' Expression ( ',' Expression )* ')' )?
StructDefinition = 'struct' Identifier '{' StructDefinition = 'struct' Identifier '{'
( VariableDeclaration (';' VariableDeclaration)* )? '} ( VariableDeclaration (';' VariableDeclaration)* )? '}

32
mix/AppContext.cpp

@ -22,7 +22,6 @@
* - KeyEventManager * - KeyEventManager
*/ */
#include <QDebug>
#include <QMessageBox> #include <QMessageBox>
#include <QQmlComponent> #include <QQmlComponent>
#include <QQmlContext> #include <QQmlContext>
@ -30,7 +29,10 @@
#include "CodeModel.h" #include "CodeModel.h"
#include "FileIo.h" #include "FileIo.h"
#include "ClientModel.h" #include "ClientModel.h"
#include "CodeEditorExtensionManager.h"
#include "Exceptions.h"
#include "AppContext.h" #include "AppContext.h"
#include "QEther.h"
#include <libwebthree/WebThree.h> #include <libwebthree/WebThree.h>
using namespace dev; using namespace dev;
@ -46,18 +48,44 @@ AppContext::AppContext(QQmlApplicationEngine* _engine)
m_codeModel.reset(new CodeModel(this)); m_codeModel.reset(new CodeModel(this));
m_clientModel.reset(new ClientModel(this)); m_clientModel.reset(new ClientModel(this));
m_fileIo.reset(new FileIo()); m_fileIo.reset(new FileIo());
/*
m_applicationEngine->rootContext()->setContextProperty("appContext", this); m_applicationEngine->rootContext()->setContextProperty("appContext", this);
qmlRegisterType<FileIo>("org.ethereum.qml", 1, 0, "FileIo"); qmlRegisterType<FileIo>("org.ethereum.qml", 1, 0, "FileIo");
qmlRegisterSingletonType(QUrl("qrc:/qml/ProjectModel.qml"), "org.ethereum.qml.ProjectModel", 1, 0, "ProjectModel"); 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("codeModel", m_codeModel.get());
m_applicationEngine->rootContext()->setContextProperty("fileIo", m_fileIo.get()); m_applicationEngine->rootContext()->setContextProperty("fileIo", m_fileIo.get());
*/
} }
AppContext::~AppContext() 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() QQmlApplicationEngine* AppContext::appEngine()
{ {
return m_applicationEngine; return m_applicationEngine;

2
mix/AppContext.h

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

2
mix/AssemblyDebuggerControl.cpp

@ -49,5 +49,5 @@ void AssemblyDebuggerControl::start() const
void AssemblyDebuggerControl::showDebugger() void AssemblyDebuggerControl::showDebugger()
{ {
QObject* debugPanel = m_view->findChild<QObject*>("debugPanel", Qt::FindChildrenRecursively); 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) aux_source_directory(. SRC_LIST)
include_directories(..) include_directories(..)
find_package (Qt5WebEngine)
qt5_add_resources(UI_RESOURCES qml.qrc) qt5_add_resources(UI_RESOURCES qml.qrc)
file(GLOB HEADERS "*.h") file(GLOB HEADERS "*.h")
set(EXECUTABLE mix) 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 is defined in cmake/EthExecutableHelper.cmake
eth_add_executable(${EXECUTABLE} eth_add_executable(${EXECUTABLE}
ICON mix ICON mix
@ -38,8 +46,13 @@ target_link_libraries(${EXECUTABLE} lll)
target_link_libraries(${EXECUTABLE} solidity) target_link_libraries(${EXECUTABLE} solidity)
target_link_libraries(${EXECUTABLE} evmcore) target_link_libraries(${EXECUTABLE} evmcore)
target_link_libraries(${EXECUTABLE} devcore) target_link_libraries(${EXECUTABLE} devcore)
target_link_libraries(${EXECUTABLE} web3jsonrpc)
target_link_libraries(${EXECUTABLE} jsqrc) 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 is defined in cmake/EthExecutableHelper.cmake
eth_install_executable(${EXECUTABLE} eth_install_executable(${EXECUTABLE}

63
mix/ClientModel.cpp

@ -24,7 +24,7 @@
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <libdevcore/CommonJS.h> #include <libdevcore/CommonJS.h>
#include <libethereum/Transaction.h> #include <libethereum/Transaction.h>
#include "ClientModel.h" #include <libqwebthree/QWebThree.h>
#include "AppContext.h" #include "AppContext.h"
#include "DebuggingStateWrapper.h" #include "DebuggingStateWrapper.h"
#include "QContractDefinition.h" #include "QContractDefinition.h"
@ -32,28 +32,19 @@
#include "ContractCallDataEncoder.h" #include "ContractCallDataEncoder.h"
#include "CodeModel.h" #include "CodeModel.h"
#include "ClientModel.h" #include "ClientModel.h"
#include "QEther.h"
#include "Web3Server.h"
#include "ClientModel.h"
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
using namespace dev::mix; using namespace dev::mix;
/// @todo Move this to QML
dev::u256 fromQString(QString const& _s)
{
return dev::jsToU256(_s.toStdString());
}
/// @todo Move this to QML
QString toQString(dev::u256 _value)
{
std::ostringstream s;
s << _value;
return QString::fromStdString(s.str());
}
ClientModel::ClientModel(AppContext* _context): 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<QVariableDefinition*>("QVariableDefinition*");
qRegisterMetaType<QVariableDefinitionList*>("QVariableDefinitionList*"); qRegisterMetaType<QVariableDefinitionList*>("QVariableDefinitionList*");
qRegisterMetaType<QList<QVariableDefinition*>>("QList<QVariableDefinition*>"); qRegisterMetaType<QList<QVariableDefinition*>>("QList<QVariableDefinition*>");
@ -64,9 +55,30 @@ ClientModel::ClientModel(AppContext* _context):
connect(this, &ClientModel::dataAvailable, this, &ClientModel::showDebugger, Qt::QueuedConnection); connect(this, &ClientModel::dataAvailable, this, &ClientModel::showDebugger, Qt::QueuedConnection);
m_client.reset(new MixClient()); 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); _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() void ClientModel::debugDeployment()
{ {
executeSequence(std::vector<TransactionSettings>(), 10000000 * ether); executeSequence(std::vector<TransactionSettings>(), 10000000 * ether);
@ -74,7 +86,7 @@ void ClientModel::debugDeployment()
void ClientModel::debugState(QVariantMap _state) 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(); QVariantList transactions = _state.value("transactions").toList();
std::vector<TransactionSettings> transactionSequence; std::vector<TransactionSettings> transactionSequence;
@ -84,14 +96,17 @@ void ClientModel::debugState(QVariantMap _state)
QVariantMap transaction = t.toMap(); QVariantMap transaction = t.toMap();
QString functionId = transaction.value("functionId").toString(); QString functionId = transaction.value("functionId").toString();
u256 value = fromQString(transaction.value("value").toString()); u256 gas = (qvariant_cast<QEther*>(transaction.value("gas")))->toU256Wei();
u256 gas = fromQString(transaction.value("gas").toString()); u256 value = (qvariant_cast<QEther*>(transaction.value("value")))->toU256Wei();
u256 gasPrice = fromQString(transaction.value("gasPrice").toString()); u256 gasPrice = (qvariant_cast<QEther*>(transaction.value("gasPrice")))->toU256Wei();
QVariantMap params = transaction.value("parameters").toMap(); QVariantMap params = transaction.value("parameters").toMap();
TransactionSettings transactionSettings(functionId, value, gas, gasPrice); TransactionSettings transactionSettings(functionId, value, gas, gasPrice);
for (auto p = params.cbegin(); p != params.cend(); ++p) 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); transactionSequence.push_back(transactionSettings);
} }
@ -206,9 +221,11 @@ ExecutionResult ClientModel::deployContract(bytes const& _code)
u256 gas = 125000; u256 gas = 125000;
u256 amount = 100; 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(); ExecutionResult r = m_client->lastExecutionResult();
r.contractAddress = contractAddress; if (newAddress != lastAddress)
contractAddressChanged();
return r; return r;
} }

21
mix/ClientModel.h

@ -32,12 +32,16 @@ using AssemblyDebuggerData = std::tuple<QList<QObject*>, dev::mix::QQMLMap*>;
Q_DECLARE_METATYPE(AssemblyDebuggerData) Q_DECLARE_METATYPE(AssemblyDebuggerData)
Q_DECLARE_METATYPE(dev::mix::ExecutionResult) Q_DECLARE_METATYPE(dev::mix::ExecutionResult)
class QWebThree;
class QWebThreeConnector;
namespace dev namespace dev
{ {
namespace mix namespace mix
{ {
class AppContext; class AppContext;
class Web3Server;
/// Backend transaction config class /// Backend transaction config class
struct TransactionSettings struct TransactionSettings
@ -67,10 +71,16 @@ class ClientModel: public QObject
public: public:
ClientModel(AppContext* _context); ClientModel(AppContext* _context);
~ClientModel();
/// @returns true if currently executing contract code
Q_PROPERTY(bool running MEMBER m_running NOTIFY stateChanged) 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: 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. /// Run the contract constructor and show debugger window.
void debugDeployment(); void debugDeployment();
/// Setup state, run transaction sequence, show debugger for the last transaction /// Setup state, run transaction sequence, show debugger for the last transaction
@ -91,15 +101,21 @@ signals:
/// Transaction execution completed with error /// Transaction execution completed with error
/// @param _message Error message /// @param _message Error message
void runFailed(QString const& _message); void runFailed(QString const& _message);
/// Contract address changed
void contractAddressChanged();
/// Execution state changed /// Execution state changed
void stateChanged(); void stateChanged();
/// Show debugger window request /// Show debugger window request
void showDebuggerWindow(); 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. /// Emited when machine states are available.
void dataAvailable(QList<QVariableDefinition*> const& _returnParams = QList<QVariableDefinition*>(), QList<QObject*> const& _wStates = QList<QObject*>(), AssemblyDebuggerData const& _code = AssemblyDebuggerData()); void dataAvailable(QList<QVariableDefinition*> const& _returnParams = QList<QVariableDefinition*>(), QList<QObject*> const& _wStates = QList<QObject*>(), AssemblyDebuggerData const& _code = AssemblyDebuggerData());
private: private:
QString contractAddress() const;
void executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance); void executeSequence(std::vector<TransactionSettings> const& _sequence, u256 _balance);
ExecutionResult deployContract(bytes const& _code); ExecutionResult deployContract(bytes const& _code);
ExecutionResult callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr); ExecutionResult callContract(Address const& _contract, bytes const& _data, TransactionSettings const& _tr);
@ -107,6 +123,9 @@ private:
AppContext* m_context; AppContext* m_context;
std::atomic<bool> m_running; std::atomic<bool> m_running;
std::unique_ptr<MixClient> m_client; 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 <QtQml>
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
#include <libsolidity/SourceReferenceFormatter.h> #include <libsolidity/SourceReferenceFormatter.h>
#include <libsolidity/InterfaceHandler.h>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include "QContractDefinition.h" #include "QContractDefinition.h"
#include "QFunctionDefinition.h" #include "QFunctionDefinition.h"
@ -55,9 +56,12 @@ CompilationResult::CompilationResult(const dev::solidity::CompilerStack& _compil
{ {
if (!_compiler.getContractNames().empty()) 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_bytes = _compiler.getBytecode();
m_assemblyCode = QString::fromStdString(dev::eth::disassemble(m_bytes)); m_assemblyCode = QString::fromStdString(dev::eth::disassemble(m_bytes));
dev::solidity::InterfaceHandler interfaceHandler;
m_contractInterface = QString::fromStdString(*interfaceHandler.getABIInterface(contractDefinition));
} }
else else
m_contract.reset(new QContractDefinition()); m_contract.reset(new QContractDefinition());
@ -71,6 +75,7 @@ CompilationResult::CompilationResult(CompilationResult const& _prev, QString con
m_compilerMessage(_compilerMessage), m_compilerMessage(_compilerMessage),
m_bytes(_prev.m_bytes), m_bytes(_prev.m_bytes),
m_assemblyCode(_prev.m_assemblyCode), m_assemblyCode(_prev.m_assemblyCode),
m_contractInterface(_prev.m_contractInterface),
m_codeHighlighter(_prev.m_codeHighlighter) m_codeHighlighter(_prev.m_codeHighlighter)
{} {}
@ -156,14 +161,19 @@ void CodeModel::runCompilationJob(int _jobId, QString const& _code)
emit compilationCompleteInternal(result.release()); emit compilationCompleteInternal(result.release());
} }
void CodeModel::onCompilationComplete(CompilationResult*_newResult) void CodeModel::onCompilationComplete(CompilationResult* _newResult)
{ {
m_compiling = false; m_compiling = false;
bool contractChanged = m_result->contractInterface() != _newResult->contractInterface();
m_result.reset(_newResult); m_result.reset(_newResult);
emit compilationComplete(); emit compilationComplete();
emit stateChanged(); emit stateChanged();
if (m_result->successful()) if (m_result->successful())
{
emit codeChanged(); emit codeChanged();
if (contractChanged)
emit contractInterfaceChanged();
}
} }
bool CodeModel::hasContract() const bool CodeModel::hasContract() const

6
mix/CodeModel.h

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

25
mix/DebuggingStateWrapper.cpp

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

15
mix/DebuggingStateWrapper.h

@ -29,6 +29,7 @@
#include <libethereum/Executive.h> #include <libethereum/Executive.h>
#include "QVariableDefinition.h" #include "QVariableDefinition.h"
#include "MixClient.h" #include "MixClient.h"
#include "QBigInt.h"
namespace dev namespace dev
{ {
@ -81,8 +82,8 @@ class DebuggingStateWrapper: public QObject
Q_OBJECT Q_OBJECT
Q_PROPERTY(int step READ step CONSTANT) Q_PROPERTY(int step READ step CONSTANT)
Q_PROPERTY(int curPC READ curPC CONSTANT) Q_PROPERTY(int curPC READ curPC CONSTANT)
Q_PROPERTY(QString gasCost READ gasCost CONSTANT) Q_PROPERTY(QBigInt* gasCost READ gasCost CONSTANT)
Q_PROPERTY(QString gas READ gas CONSTANT) Q_PROPERTY(QBigInt* gas READ gas CONSTANT)
Q_PROPERTY(QString instruction READ instruction CONSTANT) Q_PROPERTY(QString instruction READ instruction CONSTANT)
Q_PROPERTY(QStringList debugStack READ debugStack CONSTANT) Q_PROPERTY(QStringList debugStack READ debugStack CONSTANT)
Q_PROPERTY(QStringList debugStorage READ debugStorage 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(QVariantList debugCallData READ debugCallData CONSTANT)
Q_PROPERTY(QString headerInfo READ headerInfo CONSTANT) Q_PROPERTY(QString headerInfo READ headerInfo CONSTANT)
Q_PROPERTY(QString endOfDebug READ endOfDebug 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) Q_PROPERTY(QStringList levels READ levels CONSTANT)
public: public:
@ -99,12 +100,10 @@ public:
int step() { return (int)m_state.steps; } int step() { return (int)m_state.steps; }
/// Get the proccessed code index. /// Get the proccessed code index.
int curPC() { return (int)m_state.curPC; } int curPC() { return (int)m_state.curPC; }
/// Get gas left.
QString gasLeft();
/// Get gas cost. /// Get gas cost.
QString gasCost(); QBigInt* gasCost();
/// Get gas used. /// Get gas used.
QString gas(); QBigInt* gas();
/// Get stack. /// Get stack.
QStringList debugStack(); QStringList debugStack();
/// Get storage. /// Get storage.
@ -118,7 +117,7 @@ public:
/// get end of debug information. /// get end of debug information.
QString endOfDebug(); QString endOfDebug();
/// Get the new memory size. /// Get the new memory size.
QString newMemSize(); QBigInt* newMemSize();
/// Get current instruction /// Get current instruction
QString instruction(); QString instruction();
/// Get all previous steps. /// 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. * Ethereum IDE client.
*/ */
#include <stdexcept>
#include <QDir> #include <QDir>
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
@ -41,7 +40,10 @@ void FileIo::makeDir(QString const& _url)
QString FileIo::readFile(QString const& _url) QString FileIo::readFile(QString const& _url)
{ {
QUrl url(_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)) if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{ {
QTextStream stream(&file); QTextStream stream(&file);

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

Loading…
Cancel
Save