Browse Source

Merge branch 'develop' into p2p

Conflicts:
	test/whisperTopic.cpp
cl-refactor
subtly 10 years ago
parent
commit
d59cf78854
  1. 16
      alethzero/MainWin.cpp
  2. 53
      alethzero/OurWebThreeStubServer.cpp
  3. 11
      alethzero/OurWebThreeStubServer.h
  4. 2
      eth/main.cpp
  5. 10
      libdevcore/Common.h
  6. 2
      libdevcore/CommonJS.h
  7. 3
      libdevcore/FixedHash.h
  8. 14
      libdevcrypto/Common.cpp
  9. 10
      libdevcrypto/Common.h
  10. 2
      libdevcrypto/CryptoPP.h
  11. 2
      libethcore/CommonEth.cpp
  12. 13
      libethereum/Client.cpp
  13. 23
      libethereum/Client.h
  14. 28
      libethereum/LogFilter.cpp
  15. 4
      libethereum/LogFilter.h
  16. 966
      libjsqrc/es6-promise-2.0.0.js
  17. 15
      libjsqrc/ethereumjs/README.md
  18. 3
      libjsqrc/ethereumjs/bower.json
  19. 1189
      libjsqrc/ethereumjs/dist/ethereum.js
  20. 24
      libjsqrc/ethereumjs/dist/ethereum.js.map
  21. 2
      libjsqrc/ethereumjs/dist/ethereum.min.js
  22. 3
      libjsqrc/ethereumjs/example/balance.html
  23. 6
      libjsqrc/ethereumjs/example/contract.html
  24. 76
      libjsqrc/ethereumjs/example/contract_with_array.html
  25. 120
      libjsqrc/ethereumjs/example/event.html
  26. 3
      libjsqrc/ethereumjs/example/natspec_contract.html
  27. 18
      libjsqrc/ethereumjs/example/node-app.js
  28. 304
      libjsqrc/ethereumjs/lib/abi.js
  29. 33
      libjsqrc/ethereumjs/lib/const.js
  30. 205
      libjsqrc/ethereumjs/lib/contract.js
  31. 69
      libjsqrc/ethereumjs/lib/event.js
  32. 28
      libjsqrc/ethereumjs/lib/filter.js
  33. 154
      libjsqrc/ethereumjs/lib/formatters.js
  34. 4
      libjsqrc/ethereumjs/lib/httpsync.js
  35. 18
      libjsqrc/ethereumjs/lib/local.js
  36. 16
      libjsqrc/ethereumjs/lib/providermanager.js
  37. 79
      libjsqrc/ethereumjs/lib/types.js
  38. 113
      libjsqrc/ethereumjs/lib/utils.js
  39. 62
      libjsqrc/ethereumjs/lib/web3.js
  40. 3
      libjsqrc/ethereumjs/package.json
  41. 37
      libjsqrc/ethereumjs/test/abi.parsers.js
  42. 1
      libjsqrc/ethereumjs/test/db.methods.js
  43. 201
      libjsqrc/ethereumjs/test/eth.contract.js
  44. 3
      libjsqrc/ethereumjs/test/eth.methods.js
  45. 124
      libjsqrc/ethereumjs/test/event.js
  46. 2
      libjsqrc/ethereumjs/test/shh.methods.js
  47. 49
      libjsqrc/ethereumjs/test/utils.filters.js
  48. 5
      libjsqrc/ethereumjs/test/web3.methods.js
  49. 1
      libjsqrc/js.qrc
  50. 14
      libjsqrc/natspec.js
  51. 4
      libjsqrc/setup.js
  52. 1
      libqwebthree/QWebThree.h
  53. 222
      libsolidity/AST.cpp
  54. 235
      libsolidity/AST.h
  55. 6
      libsolidity/ASTForward.h
  56. 3
      libsolidity/ASTJsonConverter.cpp
  57. 60
      libsolidity/ASTPrinter.cpp
  58. 10
      libsolidity/ASTPrinter.h
  59. 20
      libsolidity/ASTVisitor.h
  60. 72
      libsolidity/AST_accept.h
  61. 4
      libsolidity/BaseTypes.h
  62. 104
      libsolidity/CallGraph.cpp
  63. 64
      libsolidity/CallGraph.h
  64. 240
      libsolidity/Compiler.cpp
  65. 26
      libsolidity/Compiler.h
  66. 91
      libsolidity/CompilerContext.cpp
  67. 32
      libsolidity/CompilerContext.h
  68. 94
      libsolidity/CompilerStack.cpp
  69. 6
      libsolidity/CompilerStack.h
  70. 269
      libsolidity/ExpressionCompiler.cpp
  71. 22
      libsolidity/ExpressionCompiler.h
  72. 8
      libsolidity/GlobalContext.cpp
  73. 2
      libsolidity/GlobalContext.h
  74. 81
      libsolidity/InterfaceHandler.cpp
  75. 43
      libsolidity/NameAndTypeResolver.cpp
  76. 22
      libsolidity/NameAndTypeResolver.h
  77. 166
      libsolidity/Parser.cpp
  78. 21
      libsolidity/Parser.h
  79. 3
      libsolidity/Token.h
  80. 164
      libsolidity/Types.cpp
  81. 74
      libsolidity/Types.h
  82. 6
      libsolidity/grammar.txt
  83. 46
      libweb3jsonrpc/CorsHttpServer.cpp
  84. 35
      libweb3jsonrpc/CorsHttpServer.h
  85. 38
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  86. 24
      libwhisper/Common.cpp
  87. 20
      libwhisper/Common.h
  88. 3
      libwhisper/Interface.cpp
  89. 20
      libwhisper/Interface.h
  90. 66
      libwhisper/Message.cpp
  91. 28
      libwhisper/Message.h
  92. 11
      libwhisper/WhisperHost.cpp
  93. 8
      libwhisper/WhisperHost.h
  94. 5
      libwhisper/WhisperPeer.cpp
  95. 4
      libwhisper/WhisperPeer.h
  96. 10
      mix/AppContext.cpp
  97. 2
      mix/AppContext.h
  98. 7
      mix/AssemblyDebuggerControl.cpp
  99. 4
      mix/AssemblyDebuggerControl.h
  100. 7
      mix/CMakeLists.txt

16
alethzero/MainWin.cpp

@ -311,7 +311,7 @@ void Main::installBalancesWatch()
altCoins.push_back(right160(ethereum()->stateAt(coinsAddr, i + 1)));
for (auto i: m_myKeys)
for (auto c: altCoins)
tf.address(c).topic(h256(i.address(), h256::AlignRight));
tf.address(c).topic(0, h256(i.address(), h256::AlignRight));
uninstallWatch(m_balancesFilter);
m_balancesFilter = installWatch(tf, [=](LocalisedLogEntries const&){ onBalancesChange(); });
@ -726,7 +726,7 @@ void Main::readSettings(bool _skipGeometry)
ui->enableOptimizer->setChecked(m_enableOptimizer);
ui->clientName->setText(s.value("clientName", "").toString());
if (ui->clientName->text().isEmpty())
ui->clientName->setText(QInputDialog::getText(this, "Enter identity", "Enter a name that will identify you on the peer network"));
ui->clientName->setText(QInputDialog::getText(nullptr, "Enter identity", "Enter a name that will identify you on the peer network"));
ui->idealPeers->setValue(s.value("idealPeers", ui->idealPeers->value()).toInt());
ui->port->setValue(s.value("port", ui->port->value()).toInt());
ui->nameReg->setText(s.value("nameReg", "").toString());
@ -1593,7 +1593,7 @@ void Main::on_destination_currentTextChanged()
// updateFee();
}
static shh::Topic topicFromText(QString _s)
static shh::FullTopic topicFromText(QString _s)
{
shh::BuildTopic ret;
while (_s.size())
@ -1670,7 +1670,7 @@ string const Main::getFunctionHashes(dev::solidity::CompilerStack const &_compil
{
ret += it.first.abridged();
ret += " :";
ret += it.second->getName() + "\n";
ret += it.second->getDeclaration().getName() + "\n";
}
return ret;
}
@ -1696,7 +1696,7 @@ void Main::on_data_textChanged()
// compiler.addSources(dev::solidity::StandardSources);
m_data = compiler.compile(src, m_enableOptimizer);
solidity = "<h4>Solidity</h4>";
solidity += "<pre>var " + QString::fromStdString(compiler.getContractNames().back()) + " = web3.eth.contractFromAbi(" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped() + ");</pre>";
solidity += "<pre>var " + QString::fromStdString(compiler.getContractNames().front()) + " = web3.eth.contractFromAbi(" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped() + ");</pre>";
solidity += "<pre>" + QString::fromStdString(compiler.getSolidityInterface()).toHtmlEscaped() + "</pre>";
solidity += "<pre>" + QString::fromStdString(getFunctionHashes(compiler)).toHtmlEscaped() + "</pre>";
}
@ -2411,10 +2411,10 @@ void Main::refreshWhispers()
shh::Envelope const& e = w.second;
shh::Message m;
for (pair<Public, Secret> const& i: m_server->ids())
if (!!(m = e.open(i.second)))
if (!!(m = e.open(shh::FullTopic(), i.second)))
break;
if (!m)
m = e.open();
m = e.open(shh::FullTopic());
QString msg;
if (m.from())
@ -2427,7 +2427,7 @@ void Main::refreshWhispers()
time_t ex = e.expiry();
QString t(ctime(&ex));
t.chop(1);
QString item = QString("[%1 - %2s] *%3 %5 %4").arg(t).arg(e.ttl()).arg(e.workProved()).arg(toString(e.topics()).c_str()).arg(msg);
QString item = QString("[%1 - %2s] *%3 %5 %4").arg(t).arg(e.ttl()).arg(e.workProved()).arg(toString(e.topic()).c_str()).arg(msg);
ui->whispers->addItem(item);
}
}

53
alethzero/OurWebThreeStubServer.cpp

@ -31,19 +31,19 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
OurWebThreeStubServer::OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, dev::WebThreeDirect& _web3,
std::vector<dev::KeyPair> const& _accounts, Main* main):
OurWebThreeStubServer::OurWebThreeStubServer(jsonrpc::AbstractServerConnector& _conn, WebThreeDirect& _web3,
vector<KeyPair> const& _accounts, Main* main):
WebThreeStubServer(_conn, _web3, _accounts), m_web3(&_web3), m_main(main)
{}
std::string OurWebThreeStubServer::shh_newIdentity()
string OurWebThreeStubServer::shh_newIdentity()
{
dev::KeyPair kp = dev::KeyPair::create();
KeyPair kp = dev::KeyPair::create();
emit onNewId(QString::fromStdString(toJS(kp.sec())));
return toJS(kp.pub());
}
bool OurWebThreeStubServer::showAuthenticationPopup(std::string const& _title, std::string const& _text) const
bool OurWebThreeStubServer::showAuthenticationPopup(string const& _title, string const& _text) const
{
QMessageBox userInput;
userInput.setText(QString::fromStdString(_title));
@ -55,16 +55,31 @@ bool OurWebThreeStubServer::showAuthenticationPopup(std::string const& _title, s
return userInput.exec() == QMessageBox::Ok;
}
bool OurWebThreeStubServer::authenticate(dev::TransactionSkeleton const& _t)
void OurWebThreeStubServer::showBasicValueTransferNotice(u256 _value) const
{
QMessageBox notice;
notice.setText("Basic Value Transfer Transaction");
notice.setInformativeText(QString::fromStdString("Value is " + toString(_value)));
notice.setStandardButtons(QMessageBox::Ok);
notice.exec();
}
bool OurWebThreeStubServer::authenticate(TransactionSkeleton const& _t)
{
h256 contractCodeHash = m_web3->ethereum()->postState().codeHash(_t.to);
if (contractCodeHash == EmptySHA3)
// recipient has no code - nothing special about this transaction.
// TODO: show basic message for value transfer.
// contract creation
return true;
std::string userNotice = m_main->lookupNatSpecUserNotice(contractCodeHash, _t.data);
if (false) //TODO: When is is just a value transfer?
{
// recipient has no code - nothing special about this transaction, show basic value transfer info
showBasicValueTransferNotice(_t.value);
return true;
}
string userNotice = m_main->lookupNatSpecUserNotice(contractCodeHash, _t.data);
if (userNotice.empty())
return showAuthenticationPopup("Unverified Pending Transaction",
"An undocumented transaction is about to be executed.");
@ -83,17 +98,17 @@ QNatspecExpressionEvaluator::QNatspecExpressionEvaluator(OurWebThreeStubServer*
QNatspecExpressionEvaluator::~QNatspecExpressionEvaluator()
{}
QString QNatspecExpressionEvaluator::evalExpression(QString const& _expression)
QString QNatspecExpressionEvaluator::evalExpression(QString const& _expression) const
{
// evaluate the natspec
// load natspec.js only when we need it for natspec evaluation
m_main->evalRaw(contentsOfQResource(":/js/natspec.js"));
// _expression should be in the format like this
// auto toEval = QString::fromStdString("the result of calling multiply(4) is `multiply(4)`");
auto toEval = _expression;
auto result = m_main->evalRaw("evaluateExpression('" + toEval + "')");
QVariant result = m_main->evalRaw("evaluateExpression('" + _expression + "')");
if (result.type() == QVariant::Invalid)
{
cerr << "Could not evaluate natspec expression: \"" << _expression.toStdString() << "\"" << endl;
// return the expression unevaluated
return _expression;
}
return result.toString();
}

11
alethzero/OurWebThreeStubServer.h

@ -42,6 +42,7 @@ signals:
private:
bool showAuthenticationPopup(std::string const& _title, std::string const& _text) const;
void showBasicValueTransferNotice(dev::u256 _value) const;
dev::WebThreeDirect* m_web3;
Main* m_main;
@ -51,14 +52,14 @@ private:
class QNatspecExpressionEvaluator: public QObject
{
Q_OBJECT
public:
QNatspecExpressionEvaluator(OurWebThreeStubServer* _server, Main* _main);
virtual ~QNatspecExpressionEvaluator();
QString evalExpression(QString const& _expression);
QString evalExpression(QString const& _expression) const;
private:
OurWebThreeStubServer* m_server;
Main* m_main;
};
};

2
eth/main.cpp

@ -39,7 +39,7 @@
#endif
#if ETH_JSONRPC
#include <libweb3jsonrpc/WebThreeStubServer.h>
#include <libweb3jsonrpc/CorsHttpServer.h>
#include <jsonrpccpp/server/connectors/httpserver.h>
#endif
#include "BuildInfo.h"
using namespace std;

10
libdevcore/Common.h

@ -36,6 +36,7 @@
#include <map>
#include <vector>
#include <set>
#include <functional>
#pragma warning(push)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
@ -117,6 +118,15 @@ inline unsigned int toLog2(u256 _x)
return ret;
}
/// RAII utility class whose destructor calls a given function.
class ScopeGuard {
public:
ScopeGuard(std::function<void(void)> _f): m_f(_f) {}
~ScopeGuard() { m_f(); }
private:
std::function<void(void)> m_f;
};
// Assertions...
#if defined(_MSC_VER)

2
libdevcore/CommonJS.h

@ -65,7 +65,7 @@ template <unsigned N> FixedHash<N> jsToFixed(std::string const& _s)
{
if (_s.substr(0, 2) == "0x")
// Hex
return FixedHash<N>(_s.substr(2 + std::max<unsigned>(40, _s.size() - 2) - 40));
return FixedHash<N>(_s.substr(2 + std::max<unsigned>(N * 2, _s.size() - 2) - N * 2));
else if (_s.find_first_not_of("0123456789") == std::string::npos)
// Decimal
return (typename FixedHash<N>::Arith)(_s);

3
libdevcore/FixedHash.h

@ -67,6 +67,9 @@ public:
/// Explicitly construct, copying from a byte array.
explicit FixedHash(bytes const& _b) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min<unsigned>(_b.size(), N)); }
/// Explicitly construct, copying from a byte array.
explicit FixedHash(bytesConstRef _b) { if (_b.size() == N) memcpy(m_data.data(), _b.data(), std::min<unsigned>(_b.size(), N)); }
/// Explicitly construct, copying from a bytes in memory with given pointer.
explicit FixedHash(byte const* _bs, ConstructFromPointerType) { memcpy(m_data.data(), _bs, N); }

14
libdevcrypto/Common.cpp

@ -35,7 +35,7 @@ using namespace dev::crypto;
static Secp256k1 s_secp256k1;
bool dev::SignatureStruct::isValid()
bool dev::SignatureStruct::isValid() const
{
if (this->v > 1 ||
this->r >= h256("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141") ||
@ -80,6 +80,18 @@ bool dev::decrypt(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext)
return true;
}
void dev::encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher)
{
// TOOD: @alex @subtly do this properly.
encrypt(KeyPair(_k).pub(), _plain, o_cipher);
}
bool dev::decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plain)
{
// TODO: @alex @subtly do this properly.
return decrypt(_k, _cipher, o_plain);
}
Public dev::recover(Signature const& _sig, h256 const& _message)
{
return s_secp256k1.recover(_sig, _message.ref());

10
libdevcrypto/Common.h

@ -51,7 +51,7 @@ struct SignatureStruct
operator Signature() const { return *(h520 const*)this; }
/// @returns true if r,s,v values are valid, otherwise false
bool isValid();
bool isValid() const;
h256 r;
h256 s;
@ -86,7 +86,13 @@ void encrypt(Public const& _k, bytesConstRef _plain, bytes& o_cipher);
/// Decrypts cipher using Secret key.
bool decrypt(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext);
/// Symmetric encryption.
void encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher);
/// Symmetric decryption.
bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext);
/// Recovers Public key from signed message hash.
Public recover(Signature const& _sig, h256 const& _hash);

2
libdevcrypto/CryptoPP.h

@ -62,7 +62,7 @@ using namespace CryptoPP;
inline ECP::Point publicToPoint(Public const& _p) { Integer x(_p.data(), 32); Integer y(_p.data() + 32, 32); return std::move(ECP::Point(x,y)); }
inline Integer secretToExponent(Secret const& _s) { return std::move(Integer(_s.data(), Secret::size)); }
/**
* CryptoPP secp256k1 algorithms.
*/

2
libethcore/CommonEth.cpp

@ -32,7 +32,7 @@ namespace dev
namespace eth
{
const unsigned c_protocolVersion = 51;
const unsigned c_protocolVersion = 52;
const unsigned c_databaseVersion = 5;
static const vector<pair<u256, string>> g_units =

13
libethereum/Client.cpp

@ -226,11 +226,12 @@ void Client::uninstallWatch(unsigned _i)
void Client::noteChanged(h256Set const& _filters)
{
Guard l(m_filterLock);
cnote << "noteChanged(" << _filters << ")";
// accrue all changes left in each filter into the watches.
for (auto& i: m_watches)
if (_filters.count(i.second.id))
{
// cwatch << "!!!" << i.first << i.second.id;
cwatch << "!!!" << i.first << i.second.id;
if (m_filters.count(i.second.id))
i.second.changes += m_filters.at(i.second.id).changes;
else
@ -642,14 +643,20 @@ Transaction Client::transaction(h256 _blockHash, unsigned _i) const
{
auto bl = m_bc.block(_blockHash);
RLP b(bl);
return Transaction(b[1][_i].data(), CheckSignature::Range);
if (_i < b[1].itemCount())
return Transaction(b[1][_i].data(), CheckSignature::Range);
else
return Transaction();
}
BlockInfo Client::uncle(h256 _blockHash, unsigned _i) const
{
auto bl = m_bc.block(_blockHash);
RLP b(bl);
return BlockInfo::fromHeader(b[2][_i].data());
if (_i < b[2].itemCount())
return BlockInfo::fromHeader(b[2][_i].data());
else
return BlockInfo();
}
LocalisedLogEntries Client::logs(LogFilter const& _f) const

23
libethereum/Client.h

@ -277,17 +277,7 @@ public:
/// Kills the blockchain. Just for debug use.
void killChain();
private:
/// Do some work. Handles blockchain maintenance and mining.
virtual void doWork();
virtual void doneWorking();
/// Overrides for being a mining host.
virtual void setupState(State& _s);
virtual bool turbo() const { return m_turboMining; }
virtual bool force() const { return m_forceMining; }
protected:
/// Collate the changed filters for the bloom filter of the given pending transaction.
/// Insert any filters that are activated into @a o_changed.
void appendFromNewPending(TransactionReceipt const& _receipt, h256Set& io_changed);
@ -300,6 +290,17 @@ private:
/// This doesn't actually make any callbacks, but incrememnts some counters in m_watches.
void noteChanged(h256Set const& _filters);
private:
/// Do some work. Handles blockchain maintenance and mining.
virtual void doWork();
virtual void doneWorking();
/// Overrides for being a mining host.
virtual void setupState(State& _s);
virtual bool turbo() const { return m_turboMining; }
virtual bool force() const { return m_forceMining; }
/// Return the actual block number of the block with the given int-number (positive is the same, INT_MIN is genesis block, < 0 is negative age, thus -1 is most recently mined, 0 is pending.
unsigned numberOf(int _b) const;

28
libethereum/LogFilter.cpp

@ -43,20 +43,21 @@ bool LogFilter::matches(LogBloom _bloom) const
{
if (m_addresses.size())
{
for (auto i: m_addresses)
for (auto const& i: m_addresses)
if (_bloom.containsBloom<3>(dev::sha3(i)))
goto OK1;
return false;
}
OK1:
if (m_topics.size())
{
for (auto i: m_topics)
if (_bloom.containsBloom<3>(dev::sha3(i)))
goto OK2;
return false;
}
OK2:
for (auto const& t: m_topics)
if (t.size())
{
for (auto const& i: t)
if (_bloom.containsBloom<3>(dev::sha3(i)))
goto OK2;
return false;
OK2:;
}
return true;
}
@ -72,11 +73,12 @@ LogEntries LogFilter::matches(TransactionReceipt const& _m) const
for (LogEntry const& e: _m.log())
{
if (!m_addresses.empty() && !m_addresses.count(e.address))
continue;
for (auto const& t: m_topics)
if (!std::count(e.topics.begin(), e.topics.end(), t))
continue;
goto continue2;
for (unsigned i = 0; i < 4; ++i)
if (!m_topics[i].empty() && (e.topics.size() < i || !m_topics[i].count(e.topics[i])))
goto continue2;
ret.push_back(e);
continue2:;
}
return ret;
}

4
libethereum/LogFilter.h

@ -50,7 +50,7 @@ public:
LogEntries matches(TransactionReceipt const& _r) const;
LogFilter address(Address _a) { m_addresses.insert(_a); return *this; }
LogFilter topic(h256 const& _t) { m_topics.insert(_t); return *this; }
LogFilter topic(unsigned _index, h256 const& _t) { if (_index < 4) m_topics[_index].insert(_t); return *this; }
LogFilter withMax(unsigned _m) { m_max = _m; return *this; }
LogFilter withSkip(unsigned _m) { m_skip = _m; return *this; }
LogFilter withEarliest(int _e) { m_earliest = _e; return *this; }
@ -58,7 +58,7 @@ public:
private:
AddressSet m_addresses;
h256Set m_topics;
std::array<h256Set, 4> m_topics;
int m_earliest = 0;
int m_latest = -1;
unsigned m_max;

966
libjsqrc/es6-promise-2.0.0.js

@ -1,966 +0,0 @@
/*!
* @overview es6-promise - a tiny implementation of Promises/A+.
* @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
* @license Licensed under MIT license
* See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE
* @version 2.0.0
*/
(function() {
"use strict";
function $$utils$$objectOrFunction(x) {
return typeof x === 'function' || (typeof x === 'object' && x !== null);
}
function $$utils$$isFunction(x) {
return typeof x === 'function';
}
function $$utils$$isMaybeThenable(x) {
return typeof x === 'object' && x !== null;
}
var $$utils$$_isArray;
if (!Array.isArray) {
$$utils$$_isArray = function (x) {
return Object.prototype.toString.call(x) === '[object Array]';
};
} else {
$$utils$$_isArray = Array.isArray;
}
var $$utils$$isArray = $$utils$$_isArray;
var $$utils$$now = Date.now || function() { return new Date().getTime(); };
function $$utils$$F() { }
var $$utils$$o_create = (Object.create || function (o) {
if (arguments.length > 1) {
throw new Error('Second argument not supported');
}
if (typeof o !== 'object') {
throw new TypeError('Argument must be an object');
}
$$utils$$F.prototype = o;
return new $$utils$$F();
});
var $$asap$$len = 0;
var $$asap$$default = function asap(callback, arg) {
$$asap$$queue[$$asap$$len] = callback;
$$asap$$queue[$$asap$$len + 1] = arg;
$$asap$$len += 2;
if ($$asap$$len === 2) {
// If len is 1, that means that we need to schedule an async flush.
// If additional callbacks are queued before the queue is flushed, they
// will be processed by this flush that we are scheduling.
$$asap$$scheduleFlush();
}
};
var $$asap$$browserGlobal = (typeof window !== 'undefined') ? window : {};
var $$asap$$BrowserMutationObserver = $$asap$$browserGlobal.MutationObserver || $$asap$$browserGlobal.WebKitMutationObserver;
// test for web worker but not in IE10
var $$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' &&
typeof importScripts !== 'undefined' &&
typeof MessageChannel !== 'undefined';
// node
function $$asap$$useNextTick() {
return function() {
process.nextTick($$asap$$flush);
};
}
function $$asap$$useMutationObserver() {
var iterations = 0;
var observer = new $$asap$$BrowserMutationObserver($$asap$$flush);
var node = document.createTextNode('');
observer.observe(node, { characterData: true });
return function() {
node.data = (iterations = ++iterations % 2);
};
}
// web worker
function $$asap$$useMessageChannel() {
var channel = new MessageChannel();
channel.port1.onmessage = $$asap$$flush;
return function () {
channel.port2.postMessage(0);
};
}
function $$asap$$useSetTimeout() {
return function() {
setTimeout($$asap$$flush, 1);
};
}
var $$asap$$queue = new Array(1000);
function $$asap$$flush() {
for (var i = 0; i < $$asap$$len; i+=2) {
var callback = $$asap$$queue[i];
var arg = $$asap$$queue[i+1];
callback(arg);
$$asap$$queue[i] = undefined;
$$asap$$queue[i+1] = undefined;
}
$$asap$$len = 0;
}
var $$asap$$scheduleFlush;
// Decide what async method to use to triggering processing of queued callbacks:
if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') {
$$asap$$scheduleFlush = $$asap$$useNextTick();
} else if ($$asap$$BrowserMutationObserver) {
$$asap$$scheduleFlush = $$asap$$useMutationObserver();
} else if ($$asap$$isWorker) {
$$asap$$scheduleFlush = $$asap$$useMessageChannel();
} else {
$$asap$$scheduleFlush = $$asap$$useSetTimeout();
}
function $$$internal$$noop() {}
var $$$internal$$PENDING = void 0;
var $$$internal$$FULFILLED = 1;
var $$$internal$$REJECTED = 2;
var $$$internal$$GET_THEN_ERROR = new $$$internal$$ErrorObject();
function $$$internal$$selfFullfillment() {
return new TypeError("You cannot resolve a promise with itself");
}
function $$$internal$$cannotReturnOwn() {
return new TypeError('A promises callback cannot return that same promise.')
}
function $$$internal$$getThen(promise) {
try {
return promise.then;
} catch(error) {
$$$internal$$GET_THEN_ERROR.error = error;
return $$$internal$$GET_THEN_ERROR;
}
}
function $$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) {
try {
then.call(value, fulfillmentHandler, rejectionHandler);
} catch(e) {
return e;
}
}
function $$$internal$$handleForeignThenable(promise, thenable, then) {
$$asap$$default(function(promise) {
var sealed = false;
var error = $$$internal$$tryThen(then, thenable, function(value) {
if (sealed) { return; }
sealed = true;
if (thenable !== value) {
$$$internal$$resolve(promise, value);
} else {
$$$internal$$fulfill(promise, value);
}
}, function(reason) {
if (sealed) { return; }
sealed = true;
$$$internal$$reject(promise, reason);
}, 'Settle: ' + (promise._label || ' unknown promise'));
if (!sealed && error) {
sealed = true;
$$$internal$$reject(promise, error);
}
}, promise);
}
function $$$internal$$handleOwnThenable(promise, thenable) {
if (thenable._state === $$$internal$$FULFILLED) {
$$$internal$$fulfill(promise, thenable._result);
} else if (promise._state === $$$internal$$REJECTED) {
$$$internal$$reject(promise, thenable._result);
} else {
$$$internal$$subscribe(thenable, undefined, function(value) {
$$$internal$$resolve(promise, value);
}, function(reason) {
$$$internal$$reject(promise, reason);
});
}
}
function $$$internal$$handleMaybeThenable(promise, maybeThenable) {
if (maybeThenable.constructor === promise.constructor) {
$$$internal$$handleOwnThenable(promise, maybeThenable);
} else {
var then = $$$internal$$getThen(maybeThenable);
if (then === $$$internal$$GET_THEN_ERROR) {
$$$internal$$reject(promise, $$$internal$$GET_THEN_ERROR.error);
} else if (then === undefined) {
$$$internal$$fulfill(promise, maybeThenable);
} else if ($$utils$$isFunction(then)) {
$$$internal$$handleForeignThenable(promise, maybeThenable, then);
} else {
$$$internal$$fulfill(promise, maybeThenable);
}
}
}
function $$$internal$$resolve(promise, value) {
if (promise === value) {
$$$internal$$reject(promise, $$$internal$$selfFullfillment());
} else if ($$utils$$objectOrFunction(value)) {
$$$internal$$handleMaybeThenable(promise, value);
} else {
$$$internal$$fulfill(promise, value);
}
}
function $$$internal$$publishRejection(promise) {
if (promise._onerror) {
promise._onerror(promise._result);
}
$$$internal$$publish(promise);
}
function $$$internal$$fulfill(promise, value) {
if (promise._state !== $$$internal$$PENDING) { return; }
promise._result = value;
promise._state = $$$internal$$FULFILLED;
if (promise._subscribers.length === 0) {
} else {
$$asap$$default($$$internal$$publish, promise);
}
}
function $$$internal$$reject(promise, reason) {
if (promise._state !== $$$internal$$PENDING) { return; }
promise._state = $$$internal$$REJECTED;
promise._result = reason;
$$asap$$default($$$internal$$publishRejection, promise);
}
function $$$internal$$subscribe(parent, child, onFulfillment, onRejection) {
var subscribers = parent._subscribers;
var length = subscribers.length;
parent._onerror = null;
subscribers[length] = child;
subscribers[length + $$$internal$$FULFILLED] = onFulfillment;
subscribers[length + $$$internal$$REJECTED] = onRejection;
if (length === 0 && parent._state) {
$$asap$$default($$$internal$$publish, parent);
}
}
function $$$internal$$publish(promise) {
var subscribers = promise._subscribers;
var settled = promise._state;
if (subscribers.length === 0) { return; }
var child, callback, detail = promise._result;
for (var i = 0; i < subscribers.length; i += 3) {
child = subscribers[i];
callback = subscribers[i + settled];
if (child) {
$$$internal$$invokeCallback(settled, child, callback, detail);
} else {
callback(detail);
}
}
promise._subscribers.length = 0;
}
function $$$internal$$ErrorObject() {
this.error = null;
}
var $$$internal$$TRY_CATCH_ERROR = new $$$internal$$ErrorObject();
function $$$internal$$tryCatch(callback, detail) {
try {
return callback(detail);
} catch(e) {
$$$internal$$TRY_CATCH_ERROR.error = e;
return $$$internal$$TRY_CATCH_ERROR;
}
}
function $$$internal$$invokeCallback(settled, promise, callback, detail) {
var hasCallback = $$utils$$isFunction(callback),
value, error, succeeded, failed;
if (hasCallback) {
value = $$$internal$$tryCatch(callback, detail);
if (value === $$$internal$$TRY_CATCH_ERROR) {
failed = true;
error = value.error;
value = null;
} else {
succeeded = true;
}
if (promise === value) {
$$$internal$$reject(promise, $$$internal$$cannotReturnOwn());
return;
}
} else {
value = detail;
succeeded = true;
}
if (promise._state !== $$$internal$$PENDING) {
// noop
} else if (hasCallback && succeeded) {
$$$internal$$resolve(promise, value);
} else if (failed) {
$$$internal$$reject(promise, error);
} else if (settled === $$$internal$$FULFILLED) {
$$$internal$$fulfill(promise, value);
} else if (settled === $$$internal$$REJECTED) {
$$$internal$$reject(promise, value);
}
}
function $$$internal$$initializePromise(promise, resolver) {
try {
resolver(function resolvePromise(value){
$$$internal$$resolve(promise, value);
}, function rejectPromise(reason) {
$$$internal$$reject(promise, reason);
});
} catch(e) {
$$$internal$$reject(promise, e);
}
}
function $$$enumerator$$makeSettledResult(state, position, value) {
if (state === $$$internal$$FULFILLED) {
return {
state: 'fulfilled',
value: value
};
} else {
return {
state: 'rejected',
reason: value
};
}
}
function $$$enumerator$$Enumerator(Constructor, input, abortOnReject, label) {
this._instanceConstructor = Constructor;
this.promise = new Constructor($$$internal$$noop, label);
this._abortOnReject = abortOnReject;
if (this._validateInput(input)) {
this._input = input;
this.length = input.length;
this._remaining = input.length;
this._init();
if (this.length === 0) {
$$$internal$$fulfill(this.promise, this._result);
} else {
this.length = this.length || 0;
this._enumerate();
if (this._remaining === 0) {
$$$internal$$fulfill(this.promise, this._result);
}
}
} else {
$$$internal$$reject(this.promise, this._validationError());
}
}
$$$enumerator$$Enumerator.prototype._validateInput = function(input) {
return $$utils$$isArray(input);
};
$$$enumerator$$Enumerator.prototype._validationError = function() {
return new Error('Array Methods must be provided an Array');
};
$$$enumerator$$Enumerator.prototype._init = function() {
this._result = new Array(this.length);
};
var $$$enumerator$$default = $$$enumerator$$Enumerator;
$$$enumerator$$Enumerator.prototype._enumerate = function() {
var length = this.length;
var promise = this.promise;
var input = this._input;
for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) {
this._eachEntry(input[i], i);
}
};
$$$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) {
var c = this._instanceConstructor;
if ($$utils$$isMaybeThenable(entry)) {
if (entry.constructor === c && entry._state !== $$$internal$$PENDING) {
entry._onerror = null;
this._settledAt(entry._state, i, entry._result);
} else {
this._willSettleAt(c.resolve(entry), i);
}
} else {
this._remaining--;
this._result[i] = this._makeResult($$$internal$$FULFILLED, i, entry);
}
};
$$$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) {
var promise = this.promise;
if (promise._state === $$$internal$$PENDING) {
this._remaining--;
if (this._abortOnReject && state === $$$internal$$REJECTED) {
$$$internal$$reject(promise, value);
} else {
this._result[i] = this._makeResult(state, i, value);
}
}
if (this._remaining === 0) {
$$$internal$$fulfill(promise, this._result);
}
};
$$$enumerator$$Enumerator.prototype._makeResult = function(state, i, value) {
return value;
};
$$$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) {
var enumerator = this;
$$$internal$$subscribe(promise, undefined, function(value) {
enumerator._settledAt($$$internal$$FULFILLED, i, value);
}, function(reason) {
enumerator._settledAt($$$internal$$REJECTED, i, reason);
});
};
var $$promise$all$$default = function all(entries, label) {
return new $$$enumerator$$default(this, entries, true /* abort on reject */, label).promise;
};
var $$promise$race$$default = function race(entries, label) {
/*jshint validthis:true */
var Constructor = this;
var promise = new Constructor($$$internal$$noop, label);
if (!$$utils$$isArray(entries)) {
$$$internal$$reject(promise, new TypeError('You must pass an array to race.'));
return promise;
}
var length = entries.length;
function onFulfillment(value) {
$$$internal$$resolve(promise, value);
}
function onRejection(reason) {
$$$internal$$reject(promise, reason);
}
for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) {
$$$internal$$subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection);
}
return promise;
};
var $$promise$resolve$$default = function resolve(object, label) {
/*jshint validthis:true */
var Constructor = this;
if (object && typeof object === 'object' && object.constructor === Constructor) {
return object;
}
var promise = new Constructor($$$internal$$noop, label);
$$$internal$$resolve(promise, object);
return promise;
};
var $$promise$reject$$default = function reject(reason, label) {
/*jshint validthis:true */
var Constructor = this;
var promise = new Constructor($$$internal$$noop, label);
$$$internal$$reject(promise, reason);
return promise;
};
var $$es6$promise$promise$$counter = 0;
function $$es6$promise$promise$$needsResolver() {
throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
}
function $$es6$promise$promise$$needsNew() {
throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
}
var $$es6$promise$promise$$default = $$es6$promise$promise$$Promise;
/**
Promise objects represent the eventual result of an asynchronous operation. The
primary way of interacting with a promise is through its `then` method, which
registers callbacks to receive either a promises eventual value or the reason
why the promise cannot be fulfilled.
Terminology
-----------
- `promise` is an object or function with a `then` method whose behavior conforms to this specification.
- `thenable` is an object or function that defines a `then` method.
- `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
- `exception` is a value that is thrown using the throw statement.
- `reason` is a value that indicates why a promise was rejected.
- `settled` the final resting state of a promise, fulfilled or rejected.
A promise can be in one of three states: pending, fulfilled, or rejected.
Promises that are fulfilled have a fulfillment value and are in the fulfilled
state. Promises that are rejected have a rejection reason and are in the
rejected state. A fulfillment value is never a thenable.
Promises can also be said to *resolve* a value. If this value is also a
promise, then the original promise's settled state will match the value's
settled state. So a promise that *resolves* a promise that rejects will
itself reject, and a promise that *resolves* a promise that fulfills will
itself fulfill.
Basic Usage:
------------
```js
var promise = new Promise(function(resolve, reject) {
// on success
resolve(value);
// on failure
reject(reason);
});
promise.then(function(value) {
// on fulfillment
}, function(reason) {
// on rejection
});
```
Advanced Usage:
---------------
Promises shine when abstracting away asynchronous interactions such as
`XMLHttpRequest`s.
```js
function getJSON(url) {
return new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onreadystatechange = handler;
xhr.responseType = 'json';
xhr.setRequestHeader('Accept', 'application/json');
xhr.send();
function handler() {
if (this.readyState === this.DONE) {
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
}
}
};
});
}
getJSON('/posts.json').then(function(json) {
// on fulfillment
}, function(reason) {
// on rejection
});
```
Unlike callbacks, promises are great composable primitives.
```js
Promise.all([
getJSON('/posts'),
getJSON('/comments')
]).then(function(values){
values[0] // => postsJSON
values[1] // => commentsJSON
return values;
});
```
@class Promise
@param {function} resolver
@param {String} label optional string for labeling the promise.
Useful for tooling.
@constructor
*/
function $$es6$promise$promise$$Promise(resolver, label) {
this._id = $$es6$promise$promise$$counter++;
this._label = label;
this._state = undefined;
this._result = undefined;
this._subscribers = [];
if ($$$internal$$noop !== resolver) {
if (!$$utils$$isFunction(resolver)) {
$$es6$promise$promise$$needsResolver();
}
if (!(this instanceof $$es6$promise$promise$$Promise)) {
$$es6$promise$promise$$needsNew();
}
$$$internal$$initializePromise(this, resolver);
}
}
$$es6$promise$promise$$Promise.all = $$promise$all$$default;
$$es6$promise$promise$$Promise.race = $$promise$race$$default;
$$es6$promise$promise$$Promise.resolve = $$promise$resolve$$default;
$$es6$promise$promise$$Promise.reject = $$promise$reject$$default;
$$es6$promise$promise$$Promise.prototype = {
constructor: $$es6$promise$promise$$Promise,
/**
The primary way of interacting with a promise is through its `then` method,
which registers callbacks to receive either a promise's eventual value or the
reason why the promise cannot be fulfilled.
```js
findUser().then(function(user){
// user is available
}, function(reason){
// user is unavailable, and you are given the reason why
});
```
Chaining
--------
The return value of `then` is itself a promise. This second, 'downstream'
promise is resolved with the return value of the first promise's fulfillment
or rejection handler, or rejected if the handler throws an exception.
```js
findUser().then(function (user) {
return user.name;
}, function (reason) {
return 'default name';
}).then(function (userName) {
// If `findUser` fulfilled, `userName` will be the user's name, otherwise it
// will be `'default name'`
});
findUser().then(function (user) {
throw new Error('Found user, but still unhappy');
}, function (reason) {
throw new Error('`findUser` rejected and we're unhappy');
}).then(function (value) {
// never reached
}, function (reason) {
// if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
// If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
});
```
If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
```js
findUser().then(function (user) {
throw new PedagogicalException('Upstream error');
}).then(function (value) {
// never reached
}).then(function (value) {
// never reached
}, function (reason) {
// The `PedgagocialException` is propagated all the way down to here
});
```
Assimilation
------------
Sometimes the value you want to propagate to a downstream promise can only be
retrieved asynchronously. This can be achieved by returning a promise in the
fulfillment or rejection handler. The downstream promise will then be pending
until the returned promise is settled. This is called *assimilation*.
```js
findUser().then(function (user) {
return findCommentsByAuthor(user);
}).then(function (comments) {
// The user's comments are now available
});
```
If the assimliated promise rejects, then the downstream promise will also reject.
```js
findUser().then(function (user) {
return findCommentsByAuthor(user);
}).then(function (comments) {
// If `findCommentsByAuthor` fulfills, we'll have the value here
}, function (reason) {
// If `findCommentsByAuthor` rejects, we'll have the reason here
});
```
Simple Example
--------------
Synchronous Example
```javascript
var result;
try {
result = findResult();
// success
} catch(reason) {
// failure
}
```
Errback Example
```js
findResult(function(result, err){
if (err) {
// failure
} else {
// success
}
});
```
Promise Example;
```javascript
findResult().then(function(result){
// success
}, function(reason){
// failure
});
```
Advanced Example
--------------
Synchronous Example
```javascript
var author, books;
try {
author = findAuthor();
books = findBooksByAuthor(author);
// success
} catch(reason) {
// failure
}
```
Errback Example
```js
function foundBooks(books) {
}
function failure(reason) {
}
findAuthor(function(author, err){
if (err) {
failure(err);
// failure
} else {
try {
findBoooksByAuthor(author, function(books, err) {
if (err) {
failure(err);
} else {
try {
foundBooks(books);
} catch(reason) {
failure(reason);
}
}
});
} catch(error) {
failure(err);
}
// success
}
});
```
Promise Example;
```javascript
findAuthor().
then(findBooksByAuthor).
then(function(books){
// found books
}).catch(function(reason){
// something went wrong
});
```
@method then
@param {Function} onFulfilled
@param {Function} onRejected
@param {String} label optional string for labeling the promise.
Useful for tooling.
@return {Promise}
*/
then: function(onFulfillment, onRejection, label) {
var parent = this;
var state = parent._state;
if (state === $$$internal$$FULFILLED && !onFulfillment || state === $$$internal$$REJECTED && !onRejection) {
return this;
}
parent._onerror = null;
var child = new this.constructor($$$internal$$noop, label);
var result = parent._result;
if (state) {
var callback = arguments[state - 1];
$$asap$$default(function(){
$$$internal$$invokeCallback(state, child, callback, result);
});
} else {
$$$internal$$subscribe(parent, child, onFulfillment, onRejection);
}
return child;
},
/**
`catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
as the catch block of a try/catch statement.
```js
function findAuthor(){
throw new Error('couldn't find that author');
}
// synchronous
try {
findAuthor();
} catch(reason) {
// something went wrong
}
// async with promises
findAuthor().catch(function(reason){
// something went wrong
});
```
@method catch
@param {Function} onRejection
@param {String} label optional string for labeling the promise.
Useful for tooling.
@return {Promise}
*/
'catch': function(onRejection, label) {
return this.then(null, onRejection, label);
}
};
var $$es6$promise$polyfill$$default = function polyfill() {
var local;
if (typeof global !== 'undefined') {
local = global;
} else if (typeof window !== 'undefined' && window.document) {
local = window;
} else {
local = self;
}
var es6PromiseSupport =
"Promise" in local &&
// Some of these methods are missing from
// Firefox/Chrome experimental implementations
"resolve" in local.Promise &&
"reject" in local.Promise &&
"all" in local.Promise &&
"race" in local.Promise &&
// Older version of the spec had a resolver object
// as the arg rather than a function
(function() {
var resolve;
new local.Promise(function(r) { resolve = r; });
return $$utils$$isFunction(resolve);
}());
if (!es6PromiseSupport) {
local.Promise = $$es6$promise$promise$$default;
}
};
var es6$promise$umd$$ES6Promise = {
Promise: $$es6$promise$promise$$default,
polyfill: $$es6$promise$polyfill$$default
};
/* global define:true module:true window: true */
if (typeof define === 'function' && define['amd']) {
define(function() { return es6$promise$umd$$ES6Promise; });
} else if (typeof module !== 'undefined' && module['exports']) {
module['exports'] = es6$promise$umd$$ES6Promise;
} else if (typeof this !== 'undefined') {
this['ES6Promise'] = es6$promise$umd$$ES6Promise;
}
}).call(this);

15
libjsqrc/ethereumjs/README.md

@ -1,6 +1,6 @@
# Ethereum JavaScript API
This is the Ethereum compatible JavaScript API using `Promise`s
This is the Ethereum compatible [JavaScript API](https://github.com/ethereum/wiki/wiki/JavaScript-API)
which implements the [Generic JSON RPC](https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC) spec. It's available on npm as a node module and also for bower and component as an embeddable js
[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![dependency status][dep-image]][dep-url] [![dev dependency status][dep-dev-image]][dep-dev-url]
@ -23,7 +23,7 @@ Component
component install ethereum/ethereum.js
* Include `ethereum.min.js` in your html file.
* Include [es6-promise](https://github.com/jakearchibald/es6-promise) or another ES6-Shim if your browser doesn't support ECMAScript 6.
* Include [bignumber.js](https://github.com/MikeMcl/bignumber.js/)
## Usage
Require the library:
@ -37,14 +37,8 @@ Set a provider (QtProvider, WebSocketProvider, HttpRpcProvider)
There you go, now you can use it:
```
web3.eth.coinbase.then(function(result){
console.log(result);
return web3.eth.balanceAt(result);
}).then(function(balance){
console.log(web3.toDecimal(balance));
}).catch(function(err){
console.log(err);
});
var coinbase = web3.eth.coinbase;
var balance = web3.eth.balanceAt(coinbase);
```
@ -99,3 +93,4 @@ ethereum -ws -loglevel=4
[dep-url]: https://david-dm.org/ethereum/ethereum.js
[dep-dev-image]: https://david-dm.org/ethereum/ethereum.js/dev-status.svg
[dep-dev-url]: https://david-dm.org/ethereum/ethereum.js#info=devDependencies

3
libjsqrc/ethereumjs/bower.json

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

1189
libjsqrc/ethereumjs/dist/ethereum.js

File diff suppressed because it is too large

24
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

3
libjsqrc/ethereumjs/example/balance.html

@ -2,7 +2,6 @@
<html>
<head>
<script type="text/javascript" src="js/es6-promise/promise.min.js"></script>
<script type="text/javascript" src="js/bignumber.js/bignumber.min.js"></script>
<script type="text/javascript" src="../dist/ethereum.js"></script>
<script type="text/javascript">
@ -18,7 +17,7 @@
var originalBalance = web3.toDecimal(balance);
document.getElementById('original').innerText = 'original balance: ' + originalBalance + ' watching...';
web3.eth.watch({altered: coinbase}).changed(function() {
web3.eth.watch('pending').changed(function() {
balance = web3.eth.balanceAt(coinbase)
var currentBalance = web3.toDecimal(balance);
document.getElementById("current").innerText = 'current: ' + currentBalance;

6
libjsqrc/ethereumjs/example/contract.html

@ -2,7 +2,6 @@
<html>
<head>
<script type="text/javascript" src="js/es6-promise/promise.min.js"></script>
<script type="text/javascript" src="js/bignumber.js/bignumber.min.js"></script>
<script type="text/javascript" src="../dist/ethereum.js"></script>
<script type="text/javascript">
@ -21,6 +20,7 @@
// contract description, this will be autogenerated somehow
var desc = [{
"name": "multiply(uint256)",
"type": "function",
"inputs": [
{
"name": "a",
@ -53,8 +53,8 @@
var param = parseInt(document.getElementById('value').value);
// call the contract
var res = contract.multiply(param);
document.getElementById('result').innerText = res[0];
var res = contract.call().multiply(param);
document.getElementById('result').innerText = res.toString(10);
}
</script>

76
libjsqrc/ethereumjs/example/contract_with_array.html

@ -0,0 +1,76 @@
<!doctype>
<html>
<head>
<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">
var web3 = require('web3');
web3.setProvider(new web3.providers.HttpSyncProvider());
// solidity source code
var source = "" +
"contract test {\n" +
" function multiply(uint[] a) returns(uint d) {\n" +
" return a[0] + a[1];\n" +
" }\n" +
"}\n";
// contract description, this will be autogenerated somehow
var desc = [{
"name": "multiply(uint256[])",
"type": "function",
"inputs": [
{
"name": "a",
"type": "uint256[]"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
var contract;
function createExampleContract() {
// hide create button
document.getElementById('create').style.visibility = 'hidden';
document.getElementById('source').innerText = source;
// create contract
var address = web3.eth.transact({code: web3.eth.solidity(source)});
contract = web3.eth.contract(address, desc);
document.getElementById('call').style.visibility = 'visible';
}
function callExampleContract() {
// this should be generated by ethereum
var param = parseInt(document.getElementById('value').value);
var param2 = parseInt(document.getElementById('value2').value);
// call the contract
var res = contract.call().multiply([param, param2]);
document.getElementById('result').innerText = res.toString(10);
}
</script>
</head>
<body>
<h1>contract</h1>
<div id="source"></div>
<div id='create'>
<button type="button" onClick="createExampleContract();">create example contract</button>
</div>
<div id='call' style='visibility: hidden;'>
<input type="number" id="value" onkeyup='callExampleContract()'></input>
<input type="number" id="value2" onkeyup='callExampleContract()'></input>
</div>
<div id="result"></div>
</body>
</html>

120
libjsqrc/ethereumjs/example/event.html

@ -0,0 +1,120 @@
<!doctype>
<html>
<head>
<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">
var web3 = require('web3');
web3.setProvider(new web3.providers.HttpSyncProvider('http://localhost:8080'));
var desc = [{
"type":"event",
"inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}],
"name":"Event"
}, {
"type":"event",
"inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}],
"name":"Event2"
}, {
"type":"function",
"inputs": [{"name":"a","type":"uint256"}],
"name":"foo",
"outputs": []
}];
var address = '0x01';
var contract = web3.eth.contract(address, desc);
function test1() {
// "{"topic":["0x83c9849c","0xc4d76332"],"address":"0x01"}"
web3.eth.watch(contract).changed(function (res) {
});
};
function test2() {
// "{"topic":["0x83c9849c"],"address":"0x01"}"
web3.eth.watch(contract.Event).changed(function (res) {
});
};
function test3() {
// "{"topic":["0x83c9849c"],"address":"0x01"}"
contract.Event().changed(function (res) {
});
};
function test4() {
// "{"topic":["0x83c9849c","0000000000000000000000000000000000000000000000000000000000000045"],"address":"0x01"}"
contract.Event({a: 69}).changed(function (res) {
});
};
function test5() {
// "{"topic":["0x83c9849c",["0000000000000000000000000000000000000000000000000000000000000045","000000000000000000000000000000000000000000000000000000000000002a"]],"address":"0x01"}"
contract.Event({a: [69, 42]}).changed(function (res) {
});
};
function test6() {
// "{"topic":["0x83c9849c","000000000000000000000000000000000000000000000000000000000000001e"],"max":100,"address":"0x01"}"
contract.Event({a: 30}, {max: 100}).changed(function (res) {
});
};
function test7() {
// "{"topic":["0x83c9849c","000000000000000000000000000000000000000000000000000000000000001e"],"address":"0x01"}"
web3.eth.watch(contract.Event, {a: 30}).changed(function (res) {
});
};
function test8() {
// "{"topic":["0x83c9849c","000000000000000000000000000000000000000000000000000000000000001e"],"max":100,"address":"0x01"}"
web3.eth.watch(contract.Event, {a: 30}, {max: 100}).changed(function (res) {
});
};
// not valid
// function testX() {
// web3.eth.watch([contract.Event, contract.Event2]).changed(function (res) {
// });
// };
</script>
</head>
<body>
<div>
<button type="button" onClick="test1();">test1</button>
</div>
<div>
<button type="button" onClick="test2();">test2</button>
</div>
<div>
<button type="button" onClick="test3();">test3</button>
</div>
<div>
<button type="button" onClick="test4();">test4</button>
</div>
<div>
<button type="button" onClick="test5();">test5</button>
</div>
<div>
<button type="button" onClick="test6();">test6</button>
</div>
<div>
<button type="button" onClick="test7();">test7</button>
</div>
<div>
<button type="button" onClick="test8();">test8</button>
</div>
</body>
</html>

3
libjsqrc/ethereumjs/example/natspec_contract.html

@ -2,10 +2,8 @@
<html>
<head>
<script type="text/javascript" src="js/es6-promise/promise.min.js"></script>
<script type="text/javascript" src="js/bignumber.js/bignumber.min.js"></script>
<script type="text/javascript" src="../dist/ethereum.js"></script>
<script type="text/javascript" src="../../natspec.js"></script>
<script type="text/javascript">
var web3 = require('web3');
@ -23,6 +21,7 @@
// contract description, this will be autogenerated somehow
var desc = [{
"name": "multiply(uint256)",
"type": "function",
"inputs": [
{
"name": "a",

18
libjsqrc/ethereumjs/example/node-app.js

@ -1,16 +1,12 @@
#!/usr/bin/env node
require('es6-promise').polyfill();
var web3 = require("../index.js");
web3.setProvider(new web3.providers.HttpRpcProvider('http://localhost:8080'));
web3.setProvider(new web3.providers.HttpSyncProvider('http://localhost:8080'));
var coinbase = web3.eth.coinbase;
console.log(coinbase);
var balance = web3.eth.balanceAt(coinbase);
console.log(balance);
web3.eth.coinbase.then(function(result){
console.log(result);
return web3.eth.balanceAt(result);
}).then(function(balance){
console.log(web3.toDecimal(balance));
}).catch(function(err){
console.log(err);
});

304
libjsqrc/ethereumjs/lib/abi.js

@ -21,170 +21,57 @@
* @date 2014
*/
// TODO: is these line is supposed to be here?
if (process.env.NODE_ENV !== 'build') {
var BigNumber = require('bignumber.js'); // jshint ignore:line
}
var web3 = require('./web3'); // jshint ignore:line
BigNumber.config({ ROUNDING_MODE: BigNumber.ROUND_DOWN });
var ETH_PADDING = 32;
/// method signature length in bytes
var ETH_METHOD_SIGNATURE_LENGTH = 4;
/// Finds first index of array element matching pattern
/// @param array
/// @param callback pattern
/// @returns index of element
var findIndex = function (array, callback) {
var end = false;
var i = 0;
for (; i < array.length && !end; i++) {
end = callback(array[i]);
}
return end ? i - 1 : -1;
};
/// @returns a function that is used as a pattern for 'findIndex'
var findMethodIndex = function (json, methodName) {
return findIndex(json, function (method) {
return method.name === methodName;
});
};
var web3 = require('./web3');
var utils = require('./utils');
var types = require('./types');
var c = require('./const');
var f = require('./formatters');
/// @param string string to be padded
/// @param number of characters that result string should have
/// @param sign, by default 0
/// @returns right aligned string
var padLeft = function (string, chars, sign) {
return new Array(chars - string.length + 1).join(sign ? sign : "0") + string;
};
/// @param expected type prefix (string)
/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false
var prefixedType = function (prefix) {
return function (type) {
return type.indexOf(prefix) === 0;
};
};
/// @param expected type name (string)
/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false
var namedType = function (name) {
return function (type) {
return name === type;
};
var displayTypeError = function (type) {
console.error('parser does not support type: ' + type);
};
/// This method should be called if we want to check if givent type is an array type
/// @returns true if it is, otherwise false
var arrayType = function (type) {
return type.slice(-2) === '[]';
};
/// Formats input value to byte representation of int
/// If value is negative, return it's two's complement
/// If the value is floating point, round it down
/// @returns right-aligned byte representation of int
var formatInputInt = function (value) {
var padding = ETH_PADDING * 2;
if (value instanceof BigNumber || typeof value === 'number') {
if (typeof value === 'number')
value = new BigNumber(value);
value = value.round();
if (value.lessThan(0))
value = new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(value).plus(1);
value = value.toString(16);
}
else if (value.indexOf('0x') === 0)
value = value.substr(2);
else if (typeof value === 'string')
value = formatInputInt(new BigNumber(value));
else
value = (+value).toString(16);
return padLeft(value, padding);
};
/// Formats input value to byte representation of string
/// @returns left-algined byte representation of string
var formatInputString = function (value) {
return web3.fromAscii(value, ETH_PADDING).substr(2);
};
/// Formats input value to byte representation of bool
/// @returns right-aligned byte representation bool
var formatInputBool = function (value) {
return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0');
};
/// Formats input value to byte representation of real
/// Values are multiplied by 2^m and encoded as integers
/// @returns byte representation of real
var formatInputReal = function (value) {
return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128)));
};
var dynamicTypeBytes = function (type, value) {
// TODO: decide what to do with array of strings
if (arrayType(type) || type == 'string') // only string itself that is dynamic; stringX is static length.
return formatInputInt(value.length);
if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length.
return f.formatInputInt(value.length);
return "";
};
/// Setups input formatters for solidity types
/// @returns an array of input formatters
var setupInputTypes = function () {
return [
{ type: prefixedType('uint'), format: formatInputInt },
{ type: prefixedType('int'), format: formatInputInt },
{ type: prefixedType('hash'), format: formatInputInt },
{ type: prefixedType('string'), format: formatInputString },
{ type: prefixedType('real'), format: formatInputReal },
{ type: prefixedType('ureal'), format: formatInputReal },
{ type: namedType('address'), format: formatInputInt },
{ type: namedType('bool'), format: formatInputBool }
];
};
var inputTypes = setupInputTypes();
var inputTypes = types.inputTypes();
/// Formats input params to bytes
/// @param contract json abi
/// @param name of the method that we want to use
/// @param abi contract method inputs
/// @param array of params that will be formatted to bytes
/// @returns bytes representation of input params
var toAbiInput = function (json, methodName, params) {
var formatInput = function (inputs, params) {
var bytes = "";
var index = findMethodIndex(json, methodName);
if (index === -1) {
return;
}
var method = json[index];
var padding = ETH_PADDING * 2;
var padding = c.ETH_PADDING * 2;
/// first we iterate in search for dynamic
method.inputs.forEach(function (input, index) {
inputs.forEach(function (input, index) {
bytes += dynamicTypeBytes(input.type, params[index]);
});
method.inputs.forEach(function (input, i) {
inputs.forEach(function (input, i) {
var typeMatch = false;
for (var j = 0; j < inputTypes.length && !typeMatch; j++) {
typeMatch = inputTypes[j].type(method.inputs[i].type, params[i]);
typeMatch = inputTypes[j].type(inputs[i].type, params[i]);
}
if (!typeMatch) {
console.error('input parser does not support type: ' + method.inputs[i].type);
displayTypeError(inputs[i].type);
}
var formatter = inputTypes[j - 1].format;
var toAppend = "";
if (arrayType(method.inputs[i].type))
if (arrayType(inputs[i].type))
toAppend = params[i].reduce(function (acc, curr) {
return acc + formatter(curr);
}, "");
@ -196,122 +83,44 @@ var toAbiInput = function (json, methodName, params) {
return bytes;
};
/// 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';
};
/// Formats input right-aligned input bytes to int
/// @returns right-aligned input bytes formatted to int
var formatOutputInt = function (value) {
// check if it's negative number
// it it is, return two's complement
if (signedIsNegative(value)) {
return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1);
}
return new BigNumber(value, 16);
};
/// Formats big right-aligned input bytes to uint
/// @returns right-aligned input bytes formatted to uint
var formatOutputUInt = function (value) {
return new BigNumber(value, 16);
};
/// @returns input bytes formatted to real
var formatOutputReal = function (value) {
return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128));
};
/// @returns input bytes formatted to ureal
var formatOutputUReal = function (value) {
return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128));
};
/// @returns right-aligned input bytes formatted to hex
var formatOutputHash = function (value) {
return "0x" + value;
};
/// @returns right-aligned input bytes formatted to bool
var formatOutputBool = function (value) {
return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false;
};
/// @returns left-aligned input bytes formatted to ascii string
var formatOutputString = function (value) {
return web3.toAscii(value);
};
/// @returns right-aligned input bytes formatted to address
var formatOutputAddress = function (value) {
return "0x" + value.slice(value.length - 40, value.length);
};
var dynamicBytesLength = function (type) {
if (arrayType(type) || type == 'string') // only string itself that is dynamic; stringX is static length.
return ETH_PADDING * 2;
if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length.
return c.ETH_PADDING * 2;
return 0;
};
/// Setups output formaters for solidity types
/// @returns an array of output formatters
var setupOutputTypes = function () {
return [
{ type: prefixedType('uint'), format: formatOutputUInt },
{ type: prefixedType('int'), format: formatOutputInt },
{ type: prefixedType('hash'), format: formatOutputHash },
{ type: prefixedType('string'), format: formatOutputString },
{ type: prefixedType('real'), format: formatOutputReal },
{ type: prefixedType('ureal'), format: formatOutputUReal },
{ type: namedType('address'), format: formatOutputAddress },
{ type: namedType('bool'), format: formatOutputBool }
];
};
var outputTypes = setupOutputTypes();
var outputTypes = types.outputTypes();
/// Formats output bytes back to param list
/// @param contract json abi
/// @param name of the method that we want to use
/// @param contract abi method outputs
/// @param bytes representtion of output
/// @returns array of output params
var fromAbiOutput = function (json, methodName, output) {
var index = findMethodIndex(json, methodName);
if (index === -1) {
return;
}
var formatOutput = function (outs, output) {
output = output.slice(2);
var result = [];
var method = json[index];
var padding = ETH_PADDING * 2;
var padding = c.ETH_PADDING * 2;
var dynamicPartLength = method.outputs.reduce(function (acc, curr) {
var dynamicPartLength = outs.reduce(function (acc, curr) {
return acc + dynamicBytesLength(curr.type);
}, 0);
var dynamicPart = output.slice(0, dynamicPartLength);
output = output.slice(dynamicPartLength);
method.outputs.forEach(function (out, i) {
outs.forEach(function (out, i) {
var typeMatch = false;
for (var j = 0; j < outputTypes.length && !typeMatch; j++) {
typeMatch = outputTypes[j].type(method.outputs[i].type);
typeMatch = outputTypes[j].type(outs[i].type);
}
if (!typeMatch) {
console.error('output parser does not support type: ' + method.outputs[i].type);
displayTypeError(outs[i].type);
}
var formatter = outputTypes[j - 1].format;
if (arrayType(method.outputs[i].type)) {
var size = formatOutputUInt(dynamicPart.slice(0, padding));
if (arrayType(outs[i].type)) {
var size = f.formatOutputUInt(dynamicPart.slice(0, padding));
dynamicPart = dynamicPart.slice(padding);
var array = [];
for (var k = 0; k < size; k++) {
@ -320,7 +129,7 @@ var fromAbiOutput = function (json, methodName, output) {
}
result.push(array);
}
else if (prefixedType('string')(method.outputs[i].type)) {
else if (types.prefixedType('string')(outs[i].type)) {
dynamicPart = dynamicPart.slice(padding);
result.push(formatter(output.slice(0, padding)));
output = output.slice(padding);
@ -333,30 +142,18 @@ var fromAbiOutput = function (json, methodName, output) {
return result;
};
/// @returns display name for method eg. multiply(uint256) -> multiply
var methodDisplayName = function (method) {
var length = method.indexOf('(');
return length !== -1 ? method.substr(0, length) : method;
};
/// @returns overloaded part of method's name
var methodTypeName = function (method) {
/// TODO: make it not vulnerable
var length = method.indexOf('(');
return length !== -1 ? method.substr(length + 1, method.length - 1 - (length + 1)) : "";
};
/// @param json abi for contract
/// @returns input parser object for given json abi
/// TODO: refactor creating the parser, do not double logic from contract
var inputParser = function (json) {
var parser = {};
json.forEach(function (method) {
var displayName = methodDisplayName(method.name);
var typeName = methodTypeName(method.name);
var displayName = utils.extractDisplayName(method.name);
var typeName = utils.extractTypeName(method.name);
var impl = function () {
var params = Array.prototype.slice.call(arguments);
return toAbiInput(json, method.name, params);
return formatInput(method.inputs, params);
};
if (parser[displayName] === undefined) {
@ -375,11 +172,11 @@ var outputParser = function (json) {
var parser = {};
json.forEach(function (method) {
var displayName = methodDisplayName(method.name);
var typeName = methodTypeName(method.name);
var displayName = utils.extractDisplayName(method.name);
var typeName = utils.extractTypeName(method.name);
var impl = function (output) {
return fromAbiOutput(json, method.name, output);
return formatOutput(method.outputs, output);
};
if (parser[displayName] === undefined) {
@ -392,17 +189,22 @@ var outputParser = function (json) {
return parser;
};
/// @param method name for which we want to get method signature
/// @returns (promise) contract method signature for method with given name
var methodSignature = function (name) {
return web3.sha3(web3.fromAscii(name)).slice(0, 2 + ETH_METHOD_SIGNATURE_LENGTH * 2);
/// @param function/event name for which we want to get signature
/// @returns signature of function/event with given name
var signatureFromAscii = function (name) {
return web3.sha3(web3.fromAscii(name)).slice(0, 2 + c.ETH_SIGNATURE_LENGTH * 2);
};
var eventSignatureFromAscii = function (name) {
return web3.sha3(web3.fromAscii(name));
};
module.exports = {
inputParser: inputParser,
outputParser: outputParser,
methodSignature: methodSignature,
methodDisplayName: methodDisplayName,
methodTypeName: methodTypeName
formatInput: formatInput,
formatOutput: formatOutput,
signatureFromAscii: signatureFromAscii,
eventSignatureFromAscii: eventSignatureFromAscii
};

33
libjsqrc/ethereumjs/lib/const.js

@ -0,0 +1,33 @@
/*
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 const.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
/// required to define ETH_BIGNUMBER_ROUNDING_MODE
if (process.env.NODE_ENV !== 'build') {
var BigNumber = require('bignumber.js'); // jshint ignore:line
}
module.exports = {
ETH_PADDING: 32,
ETH_SIGNATURE_LENGTH: 4,
ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN }
};

205
libjsqrc/ethereumjs/lib/contract.js

@ -20,96 +20,78 @@
* @date 2014
*/
var web3 = require('./web3'); // jshint ignore:line
var web3 = require('./web3');
var abi = require('./abi');
var utils = require('./utils');
var eventImpl = require('./event');
var exportNatspecGlobals = function (vars) {
// it's used byt natspec.js
// TODO: figure out better way to solve this
web3._currentContractAbi = vars.abi;
web3._currentContractAddress = vars.address;
web3._currentContractMethodName = vars.method;
web3._currentContractMethodParams = vars.params;
};
/**
* 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'); // myMethod call (implicit, default)
* myContract.call().myMethod('this is test string param for call'); // myMethod call (explicit)
* myContract.transact().myMethod('this is test string param for 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) {
desc.forEach(function (method) {
// workaround for invalid assumption that method.name is the full anonymous prototype of the method.
// it's not. it's just the name. the rest of the code assumes it's actually the anonymous
// prototype, so we make it so as a workaround.
if (method.name.indexOf('(') === -1) {
var displayName = method.name;
var typeName = method.inputs.map(function(i){return i.type}).join();
method.name = displayName + '(' + typeName + ')';
}
});
var inputParser = abi.inputParser(desc);
var outputParser = abi.outputParser(desc);
var result = {};
result.call = function (options) {
result._isTransact = false;
result._options = options;
return result;
var addFunctionRelatedPropertiesToContract = function (contract) {
contract.call = function (options) {
contract._isTransact = false;
contract._options = options;
return contract;
};
result.transact = function (options) {
result._isTransact = true;
result._options = options;
return result;
contract.transact = function (options) {
contract._isTransact = true;
contract._options = options;
return contract;
};
result._options = {};
contract._options = {};
['gas', 'gasPrice', 'value', 'from'].forEach(function(p) {
result[p] = function (v) {
result._options[p] = v;
return result;
contract[p] = function (v) {
contract._options[p] = v;
return contract;
};
});
};
desc.forEach(function (method) {
var addFunctionsToContract = function (contract, desc, address) {
var inputParser = abi.inputParser(desc);
var outputParser = abi.outputParser(desc);
// create contract functions
utils.filterFunctions(desc).forEach(function (method) {
var displayName = abi.methodDisplayName(method.name);
var typeName = abi.methodTypeName(method.name);
var displayName = utils.extractDisplayName(method.name);
var typeName = utils.extractTypeName(method.name);
var impl = function () {
var params = Array.prototype.slice.call(arguments);
var signature = abi.methodSignature(method.name);
var signature = abi.signatureFromAscii(method.name);
var parsed = inputParser[displayName][typeName].apply(null, params);
var options = result._options || {};
var options = contract._options || {};
options.to = address;
options.data = signature + parsed;
var isTransact = result._isTransact === true || (result._isTransact !== false && !method.constant);
var isTransact = contract._isTransact === true || (contract._isTransact !== false && !method.constant);
var collapse = options.collapse !== false;
// reset
result._options = {};
result._isTransact = null;
contract._options = {};
contract._isTransact = null;
if (isTransact) {
// it's used byt natspec.js
// TODO: figure out better way to solve this
web3._currentContractAbi = desc;
web3._currentContractAddress = address;
exportNatspecGlobals({
abi: desc,
address: address,
method: method.name,
params: params
});
// transactions do not have any output, cause we do not know, when they will be processed
web3.eth.transact(options);
@ -120,22 +102,105 @@ var contract = function (address, desc) {
var ret = outputParser[displayName][typeName](output);
if (collapse)
{
if (ret.length == 1)
if (ret.length === 1)
ret = ret[0];
else if (ret.length == 0)
else if (ret.length === 0)
ret = null;
}
return ret;
};
if (result[displayName] === undefined) {
result[displayName] = impl;
if (contract[displayName] === undefined) {
contract[displayName] = impl;
}
result[displayName][typeName] = impl;
contract[displayName][typeName] = impl;
});
};
var addEventRelatedPropertiesToContract = function (contract, desc, address) {
contract.address = address;
Object.defineProperty(contract, 'topic', {
get: function() {
return utils.filterEvents(desc).map(function (e) {
return abi.eventSignatureFromAscii(e.name);
});
}
});
};
var addEventsToContract = function (contract, desc, address) {
// create contract events
utils.filterEvents(desc).forEach(function (e) {
var impl = function () {
var params = Array.prototype.slice.call(arguments);
var signature = abi.eventSignatureFromAscii(e.name);
var event = eventImpl(address, signature, e);
var o = event.apply(null, params);
return web3.eth.watch(o);
};
// this property should be used by eth.filter to check if object is an event
impl._isEvent = true;
var displayName = utils.extractDisplayName(e.name);
var typeName = utils.extractTypeName(e.name);
if (contract[displayName] === undefined) {
contract[displayName] = impl;
}
contract[displayName][typeName] = impl;
});
};
/**
* 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'); // myMethod call (implicit, default)
* myContract.call().myMethod('this is test string param for call'); // myMethod call (explicit)
* myContract.transact().myMethod('this is test string param for 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) {
// workaround for invalid assumption that method.name is the full anonymous prototype of the method.
// it's not. it's just the name. the rest of the code assumes it's actually the anonymous
// prototype, so we make it so as a workaround.
// TODO: we may not want to modify input params, maybe use copy instead?
desc.forEach(function (method) {
if (method.name.indexOf('(') === -1) {
var displayName = method.name;
var typeName = method.inputs.map(function(i){return i.type; }).join();
method.name = displayName + '(' + typeName + ')';
}
});
var result = {};
addFunctionRelatedPropertiesToContract(result);
addFunctionsToContract(result, desc, address);
addEventRelatedPropertiesToContract(result, desc, address);
addEventsToContract(result, desc, address);
return result;
};

69
libjsqrc/ethereumjs/lib/event.js

@ -0,0 +1,69 @@
/*
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 event.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2014
*/
var abi = require('./abi');
var utils = require('./utils');
var inputWithName = function (inputs, name) {
var index = utils.findIndex(inputs, function (input) {
return input.name === name;
});
if (index === -1) {
console.error('indexed param with name ' + name + ' not found');
return undefined;
}
return inputs[index];
};
var indexedParamsToTopics = function (event, indexed) {
// sort keys?
return Object.keys(indexed).map(function (key) {
var inputs = [inputWithName(event.inputs, key)];
var value = indexed[key];
if (value instanceof Array) {
return value.map(function (v) {
return abi.formatInput(inputs, [v]);
});
}
return abi.formatInput(inputs, [value]);
});
};
var implementationOfEvent = function (address, signature, event) {
// valid options are 'earliest', 'latest', 'offset' and 'max', as defined for 'eth.watch'
return function (indexed, options) {
var o = options || {};
o.address = address;
o.topic = [];
o.topic.push(signature);
if (indexed) {
o.topic = o.topic.concat(indexedParamsToTopics(event, indexed));
}
return o;
};
};
module.exports = implementationOfEvent;

28
libjsqrc/ethereumjs/lib/filter.js

@ -27,7 +27,29 @@ 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
/// TODO: change 'options' name cause it may be not the best matching one, since we have events
var Filter = function(options, impl) {
if (typeof options !== "string") {
// topics property is deprecated, warn about it!
if (options.topics) {
console.warn('"topics" is deprecated, use "topic" instead');
}
// evaluate lazy properties
options = {
to: options.to,
topic: options.topic,
earliest: options.earliest,
latest: options.latest,
max: options.max,
skip: options.skip,
address: options.address
};
}
this.impl = impl;
this.callbacks = [];
@ -47,8 +69,10 @@ Filter.prototype.changed = function(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);
for (var i = 0; i < this.callbacks.length; i++) {
for (var j = 0; j < messages.length; j++) {
this.callbacks[i].call(this, messages[j]);
}
}
};

154
libjsqrc/ethereumjs/lib/formatters.js

@ -0,0 +1,154 @@
/*
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 formatters.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
if (process.env.NODE_ENV !== 'build') {
var BigNumber = require('bignumber.js'); // jshint ignore:line
}
var utils = require('./utils');
var c = require('./const');
/// @param string string to be padded
/// @param number of characters that result string should have
/// @param sign, by default 0
/// @returns right aligned string
var padLeft = function (string, chars, sign) {
return new Array(chars - string.length + 1).join(sign ? sign : "0") + string;
};
/// Formats input value to byte representation of int
/// If value is negative, return it's two's complement
/// If the value is floating point, round it down
/// @returns right-aligned byte representation of int
var formatInputInt = function (value) {
var padding = c.ETH_PADDING * 2;
if (value instanceof BigNumber || typeof value === 'number') {
if (typeof value === 'number')
value = new BigNumber(value);
BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE);
value = value.round();
if (value.lessThan(0))
value = new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(value).plus(1);
value = value.toString(16);
}
else if (value.indexOf('0x') === 0)
value = value.substr(2);
else if (typeof value === 'string')
value = formatInputInt(new BigNumber(value));
else
value = (+value).toString(16);
return padLeft(value, padding);
};
/// Formats input value to byte representation of string
/// @returns left-algined byte representation of string
var formatInputString = function (value) {
return utils.fromAscii(value, c.ETH_PADDING).substr(2);
};
/// Formats input value to byte representation of bool
/// @returns right-aligned byte representation bool
var formatInputBool = function (value) {
return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0');
};
/// 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)));
};
/// 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';
};
/// Formats input right-aligned input bytes to int
/// @returns right-aligned input bytes formatted to int
var formatOutputInt = function (value) {
value = value || "0";
// check if it's negative number
// it it is, return two's complement
if (signedIsNegative(value)) {
return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1);
}
return new BigNumber(value, 16);
};
/// Formats big right-aligned input bytes to uint
/// @returns right-aligned input bytes formatted to uint
var formatOutputUInt = function (value) {
value = value || "0";
return new BigNumber(value, 16);
};
/// @returns input bytes formatted to real
var formatOutputReal = function (value) {
return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128));
};
/// @returns input bytes formatted to ureal
var formatOutputUReal = function (value) {
return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128));
};
/// @returns right-aligned input bytes formatted to hex
var formatOutputHash = function (value) {
return "0x" + value;
};
/// @returns right-aligned input bytes formatted to bool
var formatOutputBool = function (value) {
return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false;
};
/// @returns left-aligned input bytes formatted to ascii string
var formatOutputString = function (value) {
return utils.toAscii(value);
};
/// @returns right-aligned input bytes formatted to address
var formatOutputAddress = function (value) {
return "0x" + value.slice(value.length - 40, value.length);
};
module.exports = {
formatInputInt: formatInputInt,
formatInputString: formatInputString,
formatInputBool: formatInputBool,
formatInputReal: formatInputReal,
formatOutputInt: formatOutputInt,
formatOutputUInt: formatOutputUInt,
formatOutputReal: formatOutputReal,
formatOutputUReal: formatOutputUReal,
formatOutputHash: formatOutputHash,
formatOutputBool: formatOutputBool,
formatOutputString: formatOutputString,
formatOutputAddress: formatOutputAddress
};

4
libjsqrc/ethereumjs/lib/httpsync.js

@ -21,6 +21,10 @@
* @date 2014
*/
if (process.env.NODE_ENV !== 'build') {
var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line
}
var HttpSyncProvider = function (host) {
this.handlers = [];
this.host = host || 'http://localhost:8080';

18
libjsqrc/ethereumjs/lib/local.js

@ -0,0 +1,18 @@
var addressName = {"0x12378912345789": "Gav", "0x57835893478594739854": "Jeff"};
var nameAddress = {};
for (var prop in addressName) {
if (addressName.hasOwnProperty(prop)) {
nameAddress[addressName[prop]] = prop;
}
}
var local = {
addressBook:{
byName: addressName,
byAddress: nameAddress
}
};
if (typeof(module) !== "undefined")
module.exports = local;

16
libjsqrc/ethereumjs/lib/providermanager.js

@ -49,15 +49,15 @@ var ProviderManager = function() {
result = JSON.parse(result);
// dont call the callback if result is an error, empty array or false
if (result.error || (result.result instanceof Array ? result.result.length === 0 : !result.result)) {
// dont call the callback if result is not an array, or empty one
if (result.error || !(result.result instanceof Array) || result.result.length === 0) {
return;
}
data.callback(result);
data.callback(result.result);
});
}
setTimeout(poll, 12000);
setTimeout(poll, 1000);
};
poll();
};
@ -70,12 +70,18 @@ ProviderManager.prototype.send = function(data) {
if (this.provider === undefined) {
console.error('provider is not set');
return undefined;
return null;
}
//TODO: handle error here?
var result = this.provider.send(data);
result = JSON.parse(result);
if (result.error) {
console.log(result.error);
return null;
}
return result.result;
};

79
libjsqrc/ethereumjs/lib/types.js

@ -0,0 +1,79 @@
/*
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 types.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
var f = require('./formatters');
/// @param expected type prefix (string)
/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false
var prefixedType = function (prefix) {
return function (type) {
return type.indexOf(prefix) === 0;
};
};
/// @param expected type name (string)
/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false
var namedType = function (name) {
return function (type) {
return name === type;
};
};
/// Setups input formatters for solidity types
/// @returns an array of input formatters
var inputTypes = function () {
return [
{ type: prefixedType('uint'), format: f.formatInputInt },
{ type: prefixedType('int'), format: f.formatInputInt },
{ type: prefixedType('hash'), format: f.formatInputInt },
{ type: prefixedType('string'), format: f.formatInputString },
{ type: prefixedType('real'), format: f.formatInputReal },
{ type: prefixedType('ureal'), format: f.formatInputReal },
{ type: namedType('address'), format: f.formatInputInt },
{ type: namedType('bool'), format: f.formatInputBool }
];
};
/// Setups output formaters for solidity types
/// @returns an array of output formatters
var outputTypes = function () {
return [
{ type: prefixedType('uint'), format: f.formatOutputUInt },
{ type: prefixedType('int'), format: f.formatOutputInt },
{ type: prefixedType('hash'), format: f.formatOutputHash },
{ type: prefixedType('string'), format: f.formatOutputString },
{ type: prefixedType('real'), format: f.formatOutputReal },
{ type: prefixedType('ureal'), format: f.formatOutputUReal },
{ type: namedType('address'), format: f.formatOutputAddress },
{ type: namedType('bool'), format: f.formatOutputBool }
];
};
module.exports = {
prefixedType: prefixedType,
namedType: namedType,
inputTypes: inputTypes,
outputTypes: outputTypes
};

113
libjsqrc/ethereumjs/lib/utils.js

@ -0,0 +1,113 @@
/*
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 utils.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
* @date 2015
*/
/// Finds first index of array element matching pattern
/// @param array
/// @param callback pattern
/// @returns index of element
var findIndex = function (array, callback) {
var end = false;
var i = 0;
for (; i < array.length && !end; i++) {
end = callback(array[i]);
}
return end ? i - 1 : -1;
};
/// @returns ascii string representation of hex value prefixed with 0x
var toAscii = function(hex) {
// Find termination
var str = "";
var i = 0, l = hex.length;
if (hex.substring(0, 2) === '0x') {
i = 2;
}
for (; i < l; i+=2) {
var code = parseInt(hex.substr(i, 2), 16);
if (code === 0) {
break;
}
str += String.fromCharCode(code);
}
return str;
};
var toHex = function(str) {
var hex = "";
for(var i = 0; i < str.length; i++) {
var n = str.charCodeAt(i).toString(16);
hex += n.length < 2 ? '0' + n : n;
}
return hex;
};
/// @returns hex representation (prefixed by 0x) of ascii string
var fromAscii = function(str, pad) {
pad = pad === undefined ? 0 : pad;
var hex = toHex(str);
while (hex.length < pad*2)
hex += "00";
return "0x" + hex;
};
/// @returns display name for function/event eg. multiply(uint256) -> multiply
var extractDisplayName = function (name) {
var length = name.indexOf('(');
return length !== -1 ? name.substr(0, length) : name;
};
/// @returns overloaded part of function/event name
var extractTypeName = function (name) {
/// TODO: make it invulnerable
var length = name.indexOf('(');
return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)) : "";
};
/// Filters all function from input abi
/// @returns abi array with filtered objects of type 'function'
var filterFunctions = function (json) {
return json.filter(function (current) {
return current.type === 'function';
});
};
/// Filters all events form input abi
/// @returns abi array with filtered objects of type 'event'
var filterEvents = function (json) {
return json.filter(function (current) {
return current.type === 'event';
});
};
module.exports = {
findIndex: findIndex,
toAscii: toAscii,
fromAscii: fromAscii,
extractDisplayName: extractDisplayName,
extractTypeName: extractTypeName,
filterFunctions: filterFunctions,
filterEvents: filterEvents
};

62
libjsqrc/ethereumjs/lib/web3.js

@ -27,6 +27,8 @@ if (process.env.NODE_ENV !== 'build') {
var BigNumber = require('bignumber.js');
}
var utils = require('./utils');
var ETH_UNITS = [
'wei',
'Kwei',
@ -98,7 +100,6 @@ var ethProperties = function () {
{ name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' },
{ name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' },
{ name: 'gasPrice', getter: 'eth_gasPrice' },
{ name: 'account', getter: 'eth_account' },
{ name: 'accounts', getter: 'eth_accounts' },
{ name: 'peerCount', getter: 'eth_peerCount' },
{ name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' },
@ -145,7 +146,7 @@ var shhWatchMethods = function () {
return [
{ name: 'newFilter', call: 'shh_newFilter' },
{ name: 'uninstallFilter', call: 'shh_uninstallFilter' },
{ name: 'getMessage', call: 'shh_getMessages' }
{ name: 'getMessages', call: 'shh_getMessages' }
];
};
@ -193,47 +194,17 @@ var web3 = {
_events: {},
providers: {},
toHex: function(str) {
var hex = "";
for(var i = 0; i < str.length; i++) {
var n = str.charCodeAt(i).toString(16);
hex += n.length < 2 ? '0' + n : n;
}
return hex;
},
/// @returns ascii string representation of hex value prefixed with 0x
toAscii: function(hex) {
// Find termination
var str = "";
var i = 0, l = hex.length;
if (hex.substring(0, 2) === '0x')
i = 2;
for(; i < l; i+=2) {
var code = parseInt(hex.substr(i, 2), 16);
if(code === 0) {
break;
}
str += String.fromCharCode(code);
}
return str;
},
toAscii: utils.toAscii,
/// @returns hex representation (prefixed by 0x) of ascii string
fromAscii: function(str, pad) {
pad = pad === undefined ? 0 : pad;
var hex = this.toHex(str);
while(hex.length < pad*2)
hex += "00";
return "0x" + hex;
},
fromAscii: utils.fromAscii,
/// @returns decimal representaton of hex value prefixed by 0x
toDecimal: function (val) {
return (new BigNumber(val.substring(2), 16).toString(10));
// remove 0x and place 0, if it's required
val = val.length > 2 ? val.substring(2) : "0";
return (new BigNumber(val, 16).toString(10));
},
/// @returns hex representation (prefixed by 0x) of decimal value
@ -277,8 +248,15 @@ var web3 = {
return ret;
};
},
watch: function (params) {
return new web3.filter(params, ethWatch);
/// @param filter may be a string, object or event
/// @param indexed is optional, this is an object with optional event indexed params
/// @param options is optional, this is an object with optional event options ('max'...)
watch: function (filter, indexed, options) {
if (filter._isEvent) {
return filter(indexed, options);
}
return new web3.filter(filter, ethWatch);
}
},
@ -287,8 +265,10 @@ var web3 = {
/// shh object prototype
shh: {
watch: function (params) {
return new web3.filter(params, shhWatch);
/// @param filter may be a string, object or event
watch: function (filter, indexed) {
return new web3.filter(filter, shhWatch);
}
},

3
libjsqrc/ethereumjs/package.json

@ -1,14 +1,13 @@
{
"name": "ethereum.js",
"namespace": "ethereum",
"version": "0.0.8",
"version": "0.0.11",
"description": "Ethereum Compatible JavaScript API",
"main": "./index.js",
"directories": {
"lib": "./lib"
},
"dependencies": {
"es6-promise": "*",
"ws": "*",
"xmlhttprequest": "*",
"bignumber.js": ">=2.0.0"

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

@ -5,6 +5,7 @@ var clone = function (object) { return JSON.parse(JSON.stringify(object)); };
var description = [{
"name": "test",
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
@ -339,10 +340,12 @@ describe('abi', function() {
// given
var d = [{
name: "test",
type: "function",
inputs: [{ type: "int" }],
outputs: [{ type: "int" }]
},{
name: "test2",
type: "function",
inputs: [{ type: "string" }],
outputs: [{ type: "string" }]
}];
@ -775,10 +778,12 @@ describe('abi', function() {
// given
var d = [{
name: "test",
type: "function",
inputs: [{ type: "int" }],
outputs: [{ type: "int" }]
},{
name: "test2",
type: "function",
inputs: [{ type: "string" }],
outputs: [{ type: "string" }]
}];
@ -823,6 +828,38 @@ describe('abi', function() {
});
it('should parse 0x value', function () {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'int' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x")[0], 0);
});
it('should parse 0x value', function () {
// given
var d = clone(description);
d[0].outputs = [
{ type: 'uint' }
];
// when
var parser = abi.outputParser(d);
// then
assert.equal(parser.test("0x")[0], 0);
});
});
});

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

@ -1,4 +1,3 @@
require('es6-promise').polyfill();
var assert = require('assert');
var web3 = require('../index.js');

201
libjsqrc/ethereumjs/test/eth.contract.js

@ -0,0 +1,201 @@
var assert = require('assert');
var contract = require('../lib/contract.js');
describe('contract', function() {
it('should create simple contract with one method from abi with explicit type name', function () {
// given
var description = [{
"name": "test(uint256)",
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
// when
var con = contract(null, description);
// then
assert.equal('function', typeof con.test);
assert.equal('function', typeof con.test['uint256']);
});
it('should create simple contract with one method from abi with implicit type name', function () {
// given
var description = [{
"name": "test",
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
// when
var con = contract(null, description);
// then
assert.equal('function', typeof con.test);
assert.equal('function', typeof con.test['uint256']);
});
it('should create contract with multiple methods', function () {
// given
var description = [{
"name": "test",
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
],
}, {
"name": "test2",
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
// when
var con = contract(null, description);
// then
assert.equal('function', typeof con.test);
assert.equal('function', typeof con.test['uint256']);
assert.equal('function', typeof con.test2);
assert.equal('function', typeof con.test2['uint256']);
});
it('should create contract with overloaded methods', function () {
// given
var description = [{
"name": "test",
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
],
}, {
"name": "test",
"type": "function",
"inputs": [{
"name": "a",
"type": "string"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
// when
var con = contract(null, description);
// then
assert.equal('function', typeof con.test);
assert.equal('function', typeof con.test['uint256']);
assert.equal('function', typeof con.test['string']);
});
it('should create contract with no methods', function () {
// given
var description = [{
"name": "test(uint256)",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
// when
var con = contract(null, description);
// then
assert.equal('undefined', typeof con.test);
});
it('should create contract with one event', function () {
// given
var description = [{
"name": "test",
"type": "event",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
// when
var con = contract(null, description);
// then
assert.equal('function', typeof con.test);
assert.equal('function', typeof con.test['uint256']);
});
});

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

@ -1,5 +1,3 @@
require('es6-promise').polyfill();
var assert = require('assert');
var web3 = require('../index.js');
var u = require('./utils.js');
@ -26,7 +24,6 @@ describe('web3', function() {
u.propertyExists(web3.eth, 'listening');
u.propertyExists(web3.eth, 'mining');
u.propertyExists(web3.eth, 'gasPrice');
u.propertyExists(web3.eth, 'account');
u.propertyExists(web3.eth, 'accounts');
u.propertyExists(web3.eth, 'peerCount');
u.propertyExists(web3.eth, 'defaultBlock');

124
libjsqrc/ethereumjs/test/event.js

@ -0,0 +1,124 @@
var assert = require('assert');
var event = require('../lib/event.js');
var f = require('../lib/formatters.js');
describe('event', function () {
it('should create basic filter input object', function () {
// given
var address = '0x012345';
var signature = '0x987654';
var e = {
name: 'Event',
inputs: [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}]
};
// when
var impl = event(address, signature, e);
var result = impl();
// then
assert.equal(result.address, address);
assert.equal(result.topic.length, 1);
assert.equal(result.topic[0], signature);
});
it('should create filter input object with options', function () {
// given
var address = '0x012345';
var signature = '0x987654';
var options = {
earliest: 1,
latest: 2,
offset: 3,
max: 4
};
var e = {
name: 'Event',
inputs: [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}]
};
// when
var impl = event(address, signature, e);
var result = impl({}, options);
// then
assert.equal(result.address, address);
assert.equal(result.topic.length, 1);
assert.equal(result.topic[0], signature);
assert.equal(result.earliest, options.earliest);
assert.equal(result.latest, options.latest);
assert.equal(result.offset, options.offset);
assert.equal(result.max, options.max);
});
it('should create filter input object with indexed params', function () {
// given
var address = '0x012345';
var signature = '0x987654';
var options = {
earliest: 1,
latest: 2,
offset: 3,
max: 4
};
var e = {
name: 'Event',
inputs: [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}]
};
// when
var impl = event(address, signature, e);
var result = impl({a: 4}, options);
// then
assert.equal(result.address, address);
assert.equal(result.topic.length, 2);
assert.equal(result.topic[0], signature);
assert.equal(result.topic[1], f.formatInputInt(4));
assert.equal(result.earliest, options.earliest);
assert.equal(result.latest, options.latest);
assert.equal(result.offset, options.offset);
assert.equal(result.max, options.max);
});
it('should create filter input object with an array of indexed params', function () {
// given
var address = '0x012345';
var signature = '0x987654';
var options = {
earliest: 1,
latest: 2,
offset: 3,
max: 4
};
var e = {
name: 'Event',
inputs: [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}]
};
// when
var impl = event(address, signature, e);
var result = impl({a: [4, 69]}, options);
// then
assert.equal(result.address, address);
assert.equal(result.topic.length, 2);
assert.equal(result.topic[0], signature);
assert.equal(result.topic[1][0], f.formatInputInt(4));
assert.equal(result.topic[1][1], f.formatInputInt(69));
assert.equal(result.earliest, options.earliest);
assert.equal(result.latest, options.latest);
assert.equal(result.offset, options.offset);
assert.equal(result.max, options.max);
});
});

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

@ -1,5 +1,3 @@
require('es6-promise').polyfill();
var assert = require('assert');
var web3 = require('../index.js');
var u = require('./utils.js');

49
libjsqrc/ethereumjs/test/utils.filters.js

@ -0,0 +1,49 @@
var assert = require('assert');
var utils = require('../lib/utils.js');
describe('utils', function() {
it('should filter functions and events from input array properly', function () {
// given
var description = [{
"name": "test",
"type": "function",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
],
}, {
"name": "test2",
"type": "event",
"inputs": [{
"name": "a",
"type": "uint256"
}
],
"outputs": [
{
"name": "d",
"type": "uint256"
}
]
}];
// when
var events = utils.filterEvents(description);
var functions = utils.filterFunctions(description);
// then
assert.equal(events.length, 1);
assert.equal(events[0].name, 'test2');
assert.equal(functions.length, 1);
assert.equal(functions[0].name, 'test');
});
});

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

@ -1,5 +1,3 @@
require('es6-promise').polyfill();
var assert = require('assert');
var web3 = require('../index.js');
var u = require('./utils.js');
@ -8,8 +6,5 @@ describe('web3', function() {
u.methodExists(web3, 'sha3');
u.methodExists(web3, 'toAscii');
u.methodExists(web3, 'fromAscii');
u.methodExists(web3, 'toFixed');
u.methodExists(web3, 'fromFixed');
u.methodExists(web3, 'offset');
});

1
libjsqrc/js.qrc

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

14
libjsqrc/natspec.js

@ -29,6 +29,16 @@ var getContractMethods = function (address, abi) {
return web3.eth.contract(address, abi);
};
/// Function called to get all contract method input variables
/// @returns hashmap with all contract's method input variables
var getContractInputParams = function (abi, methodName, params) {
var method = web3.abi.getMethodWithName(abi, methodName);
return method.inputs.reduce(function (acc, current, index) {
acc[current.name] = params[index];
return acc;
}, {});
};
/// Should be called to evaluate single expression
/// Is internally using javascript's 'eval' method
/// Should be checked if it is safe
@ -37,12 +47,16 @@ var evaluateExpression = function (expression) {
var self = this;
var abi = web3._currentContractAbi;
var address = web3._currentContractAddress;
var methodName = web3._currentContractMethodName;
var params = web3._currentContractMethodParams;
var storage = getContractProperties(address, abi);
var methods = getContractMethods(address, abi);
var inputParams = getContractInputParams(abi, methodName, params);
copyToContext(storage, self);
copyToContext(methods, self);
copyToContext(inputParams, self);
// TODO: test if it is safe
var evaluatedExpression = "";

4
libjsqrc/setup.js

@ -22,10 +22,6 @@
navigator.qt = _web3;
if (window.Promise === undefined) {
window.Promise = ES6Promise.Promise;
}
var web3 = require('web3');
web3.setProvider(new web3.providers.QtSyncProvider());

1
libqwebthree/QWebThree.h

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

222
libsolidity/AST.cpp

@ -41,38 +41,55 @@ TypeError ASTNode::createTypeError(string const& _description) const
return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description);
}
TypePointer ContractDefinition::getType(ContractDefinition const* _currentContract) const
{
return make_shared<TypeType>(make_shared<ContractType>(*this), _currentContract);
}
void ContractDefinition::checkTypeRequirements()
{
for (ASTPointer<InheritanceSpecifier> const& base: getBaseContracts())
base->checkTypeRequirements();
for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: getBaseContracts())
baseSpecifier->checkTypeRequirements();
checkIllegalOverrides();
FunctionDefinition const* constructor = getConstructor();
if (constructor && !constructor->getReturnParameters().empty())
BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError(
"Non-empty \"returns\" directive for constructor."));
"Non-empty \"returns\" directive for constructor."));
FunctionDefinition const* fallbackFunction = getFallbackFunction();
if (fallbackFunction && fallbackFunction->getScope() == this && !fallbackFunction->getParameters().empty())
BOOST_THROW_EXCEPTION(fallbackFunction->getParameterList().createTypeError(
"Fallback function cannot take parameters."));
for (ASTPointer<ModifierDefinition> const& modifier: getFunctionModifiers())
modifier->checkTypeRequirements();
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
function->checkTypeRequirements();
// check for hash collisions in function signatures
set<FixedHash<4>> hashes;
for (auto const& hashAndFunction: getInterfaceFunctionList())
for (auto const& it: getInterfaceFunctionList())
{
FixedHash<4> const& hash = hashAndFunction.first;
FixedHash<4> const& hash = it.first;
if (hashes.count(hash))
BOOST_THROW_EXCEPTION(createTypeError("Function signature hash collision for " +
hashAndFunction.second->getCanonicalSignature()));
BOOST_THROW_EXCEPTION(createTypeError(
std::string("Function signature hash collision for ") +
it.second->getCanonicalSignature()));
hashes.insert(hash);
}
}
map<FixedHash<4>, FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const
map<FixedHash<4>, FunctionTypePointer> ContractDefinition::getInterfaceFunctions() const
{
vector<pair<FixedHash<4>, FunctionDefinition const*>> exportedFunctionList = getInterfaceFunctionList();
map<FixedHash<4>, FunctionDefinition const*> exportedFunctions(exportedFunctionList.begin(),
exportedFunctionList.end());
auto exportedFunctionList = getInterfaceFunctionList();
map<FixedHash<4>, FunctionTypePointer> exportedFunctions;
for (auto const& it: exportedFunctionList)
exportedFunctions.insert(it);
solAssert(exportedFunctionList.size() == exportedFunctions.size(),
"Hash collision at Function Definition Hash calculation");
@ -87,17 +104,33 @@ FunctionDefinition const* ContractDefinition::getConstructor() const
return nullptr;
}
FunctionDefinition const* ContractDefinition::getFallbackFunction() const
{
for (ContractDefinition const* contract: getLinearizedBaseContracts())
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions())
if (f->getName().empty())
return f.get();
return nullptr;
}
void ContractDefinition::checkIllegalOverrides() const
{
// TODO unify this at a later point. for this we need to put the constness and the access specifier
// into the types
map<string, FunctionDefinition const*> functions;
map<string, ModifierDefinition const*> modifiers;
// 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()];
continue; // constructors can neither be overridden nor override anything
string const& name = function->getName();
if (modifiers.count(name))
BOOST_THROW_EXCEPTION(modifiers[name]->createTypeError("Override changes function to modifier."));
FunctionDefinition const*& override = functions[name];
if (!override)
override = function.get();
else if (override->isPublic() != function->isPublic() ||
@ -105,22 +138,62 @@ void ContractDefinition::checkIllegalOverrides() const
FunctionType(*override) != FunctionType(*function))
BOOST_THROW_EXCEPTION(override->createTypeError("Override changes extended function signature."));
}
for (ASTPointer<ModifierDefinition> const& modifier: contract->getFunctionModifiers())
{
string const& name = modifier->getName();
if (functions.count(name))
BOOST_THROW_EXCEPTION(functions[name]->createTypeError("Override changes modifier to function."));
ModifierDefinition const*& override = modifiers[name];
if (!override)
override = modifier.get();
else if (ModifierType(*override) != ModifierType(*modifier))
BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier signature."));
}
}
}
vector<pair<FixedHash<4>, FunctionDefinition const*>> const& ContractDefinition::getInterfaceFunctionList() const
std::vector<ASTPointer<EventDefinition>> const& ContractDefinition::getInterfaceEvents() const
{
if (!m_interfaceEvents)
{
set<string> eventsSeen;
m_interfaceEvents.reset(new std::vector<ASTPointer<EventDefinition>>());
for (ContractDefinition const* contract: getLinearizedBaseContracts())
for (ASTPointer<EventDefinition> const& e: contract->getEvents())
if (eventsSeen.count(e->getName()) == 0)
{
eventsSeen.insert(e->getName());
m_interfaceEvents->push_back(e);
}
}
return *m_interfaceEvents;
}
vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getInterfaceFunctionList() const
{
if (!m_interfaceFunctionList)
{
set<string> functionsSeen;
m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionDefinition const*>>());
m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionTypePointer>>());
for (ContractDefinition const* contract: getLinearizedBaseContracts())
{
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions())
if (f->isPublic() && !f->isConstructor() && functionsSeen.count(f->getName()) == 0)
if (f->isPublic() && !f->isConstructor() && !f->getName().empty() && functionsSeen.count(f->getName()) == 0)
{
functionsSeen.insert(f->getName());
FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
m_interfaceFunctionList->push_back(make_pair(hash, f.get()));
m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*f, false)));
}
for (ASTPointer<VariableDeclaration> const& v: contract->getStateVariables())
if (v->isPublic() && functionsSeen.count(v->getName()) == 0)
{
FunctionType ftype(*v);
functionsSeen.insert(v->getName());
FixedHash<4> hash(dev::sha3(ftype.getCanonicalSignature(v->getName())));
m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*v)));
}
}
}
return *m_interfaceFunctionList;
}
@ -141,6 +214,11 @@ void InheritanceSpecifier::checkTypeRequirements()
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in constructer call."));
}
TypePointer StructDefinition::getType(ContractDefinition const*) const
{
return make_shared<TypeType>(make_shared<StructType>(*this));
}
void StructDefinition::checkMemberTypes() const
{
for (ASTPointer<VariableDeclaration> const& member: getMembers())
@ -169,18 +247,73 @@ void StructDefinition::checkRecursion() const
}
}
TypePointer FunctionDefinition::getType(ContractDefinition const*) const
{
return make_shared<FunctionType>(*this);
}
void FunctionDefinition::checkTypeRequirements()
{
for (ASTPointer<VariableDeclaration> const& var: getParameters() + getReturnParameters())
if (!var->getType()->canLiveOutsideStorage())
BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
for (ASTPointer<ModifierInvocation> const& modifier: m_functionModifiers)
modifier->checkTypeRequirements();
m_body->checkTypeRequirements();
}
string FunctionDefinition::getCanonicalSignature() const
{
return getName() + FunctionType(*this).getCanonicalSignature();
return FunctionType(*this).getCanonicalSignature(getName());
}
Declaration::LValueType VariableDeclaration::getLValueType() const
{
if (dynamic_cast<FunctionDefinition const*>(getScope()) || dynamic_cast<ModifierDefinition const*>(getScope()))
return Declaration::LValueType::LOCAL;
else
return Declaration::LValueType::STORAGE;
}
TypePointer ModifierDefinition::getType(ContractDefinition const*) const
{
return make_shared<ModifierType>(*this);
}
void ModifierDefinition::checkTypeRequirements()
{
m_body->checkTypeRequirements();
}
void ModifierInvocation::checkTypeRequirements()
{
m_modifierName->checkTypeRequirements();
for (ASTPointer<Expression> const& argument: m_arguments)
argument->checkTypeRequirements();
ModifierDefinition const* modifier = dynamic_cast<ModifierDefinition const*>(m_modifierName->getReferencedDeclaration());
solAssert(modifier, "Function modifier not found.");
vector<ASTPointer<VariableDeclaration>> const& parameters = modifier->getParameters();
if (parameters.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for modifier invocation."));
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameters[i]->getType()))
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in modifier invocation."));
}
void EventDefinition::checkTypeRequirements()
{
int numIndexed = 0;
for (ASTPointer<VariableDeclaration> const& var: getParameters())
{
if (var->isIndexed())
numIndexed++;
if (!var->getType()->canLiveOutsideStorage())
BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
}
if (numIndexed > 3)
BOOST_THROW_EXCEPTION(createTypeError("More than 3 indexed arguments for event."));
}
void Block::checkTypeRequirements()
@ -218,7 +351,8 @@ void Return::checkTypeRequirements()
{
if (!m_expression)
return;
solAssert(m_returnParameters, "Return parameters not assigned.");
if (!m_returnParameters)
BOOST_THROW_EXCEPTION(createTypeError("Return arguments not allowed."));
if (m_returnParameters->getParameters().size() != 1)
BOOST_THROW_EXCEPTION(createTypeError("Different number of arguments in return statement "
"than in returns declaration."));
@ -340,8 +474,7 @@ void FunctionCall::checkTypeRequirements()
//@todo for structs, we have to check the number of arguments to be equal to the
// number of non-mapping members
if (m_arguments.size() != 1)
BOOST_THROW_EXCEPTION(createTypeError("More than one argument for "
"explicit type conersion."));
BOOST_THROW_EXCEPTION(createTypeError("More than one argument for explicit type conversion."));
if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType()))
BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed."));
m_type = type.getActualType();
@ -394,7 +527,7 @@ void MemberAccess::checkTypeRequirements()
BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not "
"visible in " + type.toString()));
//@todo later, this will not always be STORAGE
m_lvalue = type.getCategory() == Type::Category::STRUCT ? LValueType::STORAGE : LValueType::NONE;
m_lvalue = type.getCategory() == Type::Category::STRUCT ? Declaration::LValueType::STORAGE : Declaration::LValueType::NONE;
}
void IndexAccess::checkTypeRequirements()
@ -406,52 +539,17 @@ void IndexAccess::checkTypeRequirements()
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType());
m_index->expectType(*type.getKeyType());
m_type = type.getValueType();
m_lvalue = LValueType::STORAGE;
m_lvalue = Declaration::LValueType::STORAGE;
}
void Identifier::checkTypeRequirements()
{
solAssert(m_referencedDeclaration, "Identifier not resolved.");
VariableDeclaration const* variable = dynamic_cast<VariableDeclaration const*>(m_referencedDeclaration);
if (variable)
{
if (!variable->getType())
BOOST_THROW_EXCEPTION(createTypeError("Variable referenced before type could be determined."));
m_type = variable->getType();
m_lvalue = variable->isLocalVariable() ? LValueType::LOCAL : LValueType::STORAGE;
return;
}
//@todo can we unify these with TypeName::toType()?
StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(m_referencedDeclaration);
if (structDef)
{
// note that we do not have a struct type here
m_type = make_shared<TypeType>(make_shared<StructType>(*structDef));
return;
}
FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(m_referencedDeclaration);
if (functionDef)
{
// a function reference is not a TypeType, because calling a TypeType converts to the type.
// Calling a function (e.g. function(12), otherContract.function(34)) does not do a type
// conversion.
m_type = make_shared<FunctionType>(*functionDef);
return;
}
ContractDefinition const* contractDef = dynamic_cast<ContractDefinition const*>(m_referencedDeclaration);
if (contractDef)
{
m_type = make_shared<TypeType>(make_shared<ContractType>(*contractDef), m_currentContract);
return;
}
MagicVariableDeclaration const* magicVariable = dynamic_cast<MagicVariableDeclaration const*>(m_referencedDeclaration);
if (magicVariable)
{
m_type = magicVariable->getType();
return;
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Declaration reference of unknown/forbidden type."));
m_lvalue = m_referencedDeclaration->getLValueType();
m_type = m_referencedDeclaration->getType(m_currentContract);
if (!m_type)
BOOST_THROW_EXCEPTION(createTypeError("Declaration referenced before type could be determined."));
}
void ElementaryTypeNameExpression::checkTypeRequirements()

235
libsolidity/AST.h

@ -132,6 +132,8 @@ private:
class Declaration: public ASTNode
{
public:
enum class LValueType { NONE, LOCAL, STORAGE };
Declaration(Location const& _location, ASTPointer<ASTString> const& _name):
ASTNode(_location), m_name(_name), m_scope(nullptr) {}
@ -142,17 +144,55 @@ public:
Declaration const* getScope() const { return m_scope; }
void setScope(Declaration const* _scope) { m_scope = _scope; }
/// @returns the type of expressions referencing this declaration.
/// The current contract has to be given since this context can change the type, especially of
/// contract types.
virtual TypePointer getType(ContractDefinition const* m_currentContract = nullptr) const = 0;
/// @returns the lvalue type of expressions referencing this declaration
virtual LValueType getLValueType() const { return LValueType::NONE; }
private:
ASTPointer<ASTString> m_name;
Declaration const* m_scope;
};
/**
* Abstract class that is added to each AST node that can store local variables.
*/
class VariableScope
{
public:
void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }
private:
std::vector<VariableDeclaration const*> m_localVariables;
};
/**
* Abstract class that is added to each AST node that can receive documentation.
*/
class Documented
{
public:
explicit Documented(ASTPointer<ASTString> const& _documentation): m_documentation(_documentation) {}
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
protected:
ASTPointer<ASTString> m_documentation;
};
/// @}
/**
* Definition of a contract. This is the only AST nodes where child nodes are not visited in
* document order. It first visits all struct declarations, then all variable declarations and
* finally all function declarations.
*/
class ContractDefinition: public Declaration
class ContractDefinition: public Declaration, public Documented
{
public:
ContractDefinition(Location const& _location,
@ -161,13 +201,16 @@ public:
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions):
Declaration(_location, _name),
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers,
std::vector<ASTPointer<EventDefinition>> const& _events):
Declaration(_location, _name), Documented(_documentation),
m_baseContracts(_baseContracts),
m_definedStructs(_definedStructs),
m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions),
m_documentation(_documentation)
m_functionModifiers(_functionModifiers),
m_events(_events)
{}
virtual void accept(ASTVisitor& _visitor) override;
@ -176,41 +219,46 @@ public:
std::vector<ASTPointer<InheritanceSpecifier>> const& getBaseContracts() const { return m_baseContracts; }
std::vector<ASTPointer<StructDefinition>> const& getDefinedStructs() const { return m_definedStructs; }
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; }
std::vector<ASTPointer<ModifierDefinition>> const& getFunctionModifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; }
std::vector<ASTPointer<EventDefinition>> const& getEvents() const { return m_events; }
std::vector<ASTPointer<EventDefinition>> const& getInterfaceEvents() const;
virtual TypePointer getType(ContractDefinition const* m_currentContract) const override;
/// Checks that there are no illegal overrides, that the constructor does not have a "returns"
/// and calls checkTypeRequirements on all its functions.
void checkTypeRequirements();
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
/// @returns a map of canonical function signatures to FunctionDefinitions
/// as intended for use by the ABI.
std::map<FixedHash<4>, FunctionDefinition const*> getInterfaceFunctions() const;
std::map<FixedHash<4>, FunctionTypePointer> 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;
/// Returns the fallback function or nullptr if no constructor was specified.
FunctionDefinition const* getFallbackFunction() const;
private:
void checkIllegalOverrides() const;
std::vector<std::pair<FixedHash<4>, FunctionDefinition const*>> const& getInterfaceFunctionList() const;
std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& getInterfaceFunctionList() const;
std::vector<ASTPointer<InheritanceSpecifier>> m_baseContracts;
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
ASTPointer<ASTString> m_documentation;
std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers;
std::vector<ASTPointer<EventDefinition>> m_events;
std::vector<ContractDefinition const*> m_linearizedBaseContracts;
mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionDefinition const*>>> m_interfaceFunctionList;
mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList;
mutable std::unique_ptr<std::vector<ASTPointer<EventDefinition>>> m_interfaceEvents;
};
class InheritanceSpecifier: public ASTNode
@ -245,6 +293,8 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& getMembers() const { return m_members; }
virtual TypePointer getType(ContractDefinition const*) const override;
/// Checks that the members do not include any recursive structs and have valid types
/// (e.g. no functions).
void checkValidityOfMembers() const;
@ -276,7 +326,7 @@ private:
std::vector<ASTPointer<VariableDeclaration>> m_parameters;
};
class FunctionDefinition: public Declaration
class FunctionDefinition: public Declaration, public VariableScope, public Documented
{
public:
FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name,
@ -285,14 +335,16 @@ public:
ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
bool _isDeclaredConst,
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body):
Declaration(_location, _name), m_isPublic(_isPublic), m_isConstructor(_isConstructor),
Declaration(_location, _name), Documented(_documentation),
m_isPublic(_isPublic), m_isConstructor(_isConstructor),
m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst),
m_functionModifiers(_modifiers),
m_returnParameters(_returnParameters),
m_body(_body),
m_documentation(_documentation)
m_body(_body)
{}
virtual void accept(ASTVisitor& _visitor) override;
@ -301,17 +353,14 @@ public:
bool isPublic() const { return m_isPublic; }
bool isConstructor() const { return m_isConstructor; }
bool isDeclaredConst() const { return m_isDeclaredConst; }
std::vector<ASTPointer<ModifierInvocation>> const& getModifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
ParameterList const& getParameterList() const { return *m_parameters; }
std::vector<ASTPointer<VariableDeclaration>> const& getReturnParameters() const { return m_returnParameters->getParameters(); }
ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; }
Block const& getBody() const { return *m_body; }
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }
virtual TypePointer getType(ContractDefinition const*) const override;
/// Checks that all parameters have allowed types and calls checkTypeRequirements on the body.
void checkTypeRequirements();
@ -326,11 +375,9 @@ private:
bool m_isConstructor;
ASTPointer<ParameterList> m_parameters;
bool m_isDeclaredConst;
std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers;
ASTPointer<ParameterList> m_returnParameters;
ASTPointer<Block> m_body;
ASTPointer<ASTString> m_documentation;
std::vector<VariableDeclaration const*> m_localVariables;
};
/**
@ -341,8 +388,10 @@ class VariableDeclaration: public Declaration
{
public:
VariableDeclaration(Location const& _location, ASTPointer<TypeName> const& _type,
ASTPointer<ASTString> const& _name):
Declaration(_location, _name), m_typeName(_type) {}
ASTPointer<ASTString> const& _name, bool _isPublic, bool _isStateVar = false,
bool _isIndexed = false):
Declaration(_location, _name), m_typeName(_type),
m_isPublic(_isPublic), m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
@ -350,17 +399,106 @@ public:
/// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly
/// declared and there is no assignment to the variable that fixes the type.
std::shared_ptr<Type const> const& getType() const { return m_type; }
TypePointer getType(ContractDefinition const* = nullptr) const { return m_type; }
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
virtual LValueType getLValueType() const override;
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
bool isPublic() const { return m_isPublic; }
bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; }
private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
bool m_isPublic; ///< Whether there is an accessor for it or not
bool m_isStateVariable; ///< Whether or not this is a contract state variable
bool m_isIndexed; ///< Whether this is an indexed variable (used by events).
std::shared_ptr<Type const> m_type; ///< derived type, initially empty
};
/**
* Definition of a function modifier.
*/
class ModifierDefinition: public Declaration, public VariableScope, public Documented
{
public:
ModifierDefinition(Location const& _location,
ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
ASTPointer<Block> const& _body):
Declaration(_location, _name), Documented(_documentation),
m_parameters(_parameters), m_body(_body) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
ParameterList const& getParameterList() const { return *m_parameters; }
Block const& getBody() const { return *m_body; }
virtual TypePointer getType(ContractDefinition const* = nullptr) const override;
void checkTypeRequirements();
private:
ASTPointer<ParameterList> m_parameters;
ASTPointer<Block> m_body;
};
/**
* Invocation/usage of a modifier in a function header.
*/
class ModifierInvocation: public ASTNode
{
public:
ModifierInvocation(Location const& _location, ASTPointer<Identifier> const& _name,
std::vector<ASTPointer<Expression>> _arguments):
ASTNode(_location), m_modifierName(_name), m_arguments(_arguments) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
ASTPointer<Identifier> const& getName() const { return m_modifierName; }
std::vector<ASTPointer<Expression>> const& getArguments() const { return m_arguments; }
void checkTypeRequirements();
private:
ASTPointer<Identifier> m_modifierName;
std::vector<ASTPointer<Expression>> m_arguments;
};
/**
* Definition of a (loggable) event.
*/
class EventDefinition: public Declaration, public VariableScope, public Documented
{
public:
EventDefinition(Location const& _location,
ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters):
Declaration(_location, _name), Documented(_documentation), m_parameters(_parameters) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
ParameterList const& getParameterList() const { return *m_parameters; }
virtual TypePointer getType(ContractDefinition const* = nullptr) const override
{
return std::make_shared<FunctionType>(*this);
}
void checkTypeRequirements();
private:
ASTPointer<ParameterList> m_parameters;
};
/**
* Pseudo AST node that is used as declaration for "this", "msg", "tx", "block" and the global
* functions when such an identifier is encountered. Will never have a valid location in the source code.
@ -375,7 +513,7 @@ public:
virtual void accept(ASTConstVisitor&) const override { BOOST_THROW_EXCEPTION(InternalCompilerError()
<< errinfo_comment("MagicVariableDeclaration used inside real AST.")); }
std::shared_ptr<Type const> const& getType() const { return m_type; }
virtual TypePointer getType(ContractDefinition const* = nullptr) const override { return m_type; }
private:
std::shared_ptr<Type const> m_type;
@ -502,6 +640,21 @@ private:
std::vector<ASTPointer<Statement>> m_statements;
};
/**
* Special placeholder statement denoted by "_" used in function modifiers. This is replaced by
* the original function when the modifier is applied.
*/
class PlaceholderStatement: public Statement
{
public:
PlaceholderStatement(Location const& _location): Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override { }
};
/**
* If-statement with an optional "else" part. Note that "else if" is modeled by having a new
* if-statement as the false (else) body.
@ -618,12 +771,8 @@ public:
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
void setFunctionReturnParameters(ParameterList const& _parameters) { m_returnParameters = &_parameters; }
ParameterList const& getFunctionReturnParameters() const
{
solAssert(m_returnParameters, "");
return *m_returnParameters;
}
void setFunctionReturnParameters(ParameterList const* _parameters) { m_returnParameters = _parameters; }
ParameterList const* getFunctionReturnParameters() const { return m_returnParameters; }
Expression const* getExpression() const { return m_expression.get(); }
private:
@ -686,16 +835,13 @@ private:
*/
class Expression: public ASTNode
{
protected:
enum class LValueType { NONE, LOCAL, STORAGE };
public:
Expression(Location const& _location): ASTNode(_location), m_lvalue(LValueType::NONE), m_lvalueRequested(false) {}
Expression(Location const& _location): ASTNode(_location) {}
virtual void checkTypeRequirements() = 0;
std::shared_ptr<Type const> const& getType() const { return m_type; }
bool isLValue() const { return m_lvalue != LValueType::NONE; }
bool isLocalLValue() const { return m_lvalue == LValueType::LOCAL; }
bool isLValue() const { return m_lvalue != Declaration::LValueType::NONE; }
bool isLocalLValue() const { return m_lvalue == Declaration::LValueType::LOCAL; }
/// Helper function, infer the type via @ref checkTypeRequirements and then check that it
/// is implicitly convertible to @a _expectedType. If not, throw exception.
@ -712,9 +858,9 @@ protected:
std::shared_ptr<Type const> m_type;
//! If this expression is an lvalue (i.e. something that can be assigned to) and is stored
//! locally or in storage. This is set during calls to @a checkTypeRequirements()
LValueType m_lvalue;
Declaration::LValueType m_lvalue = Declaration::LValueType::NONE;
//! Whether the outer expression requested the address (true) or the value (false) of this expression.
bool m_lvalueRequested;
bool m_lvalueRequested = false;
};
/// Assignment, can also be a compound assignment.
@ -979,5 +1125,6 @@ private:
/// @}
}
}

6
libsolidity/ASTForward.h

@ -43,6 +43,9 @@ class StructDefinition;
class ParameterList;
class FunctionDefinition;
class VariableDeclaration;
class ModifierDefinition;
class ModifierInvocation;
class EventDefinition;
class MagicVariableDeclaration;
class TypeName;
class ElementaryTypeName;
@ -50,6 +53,7 @@ class UserDefinedTypeName;
class Mapping;
class Statement;
class Block;
class PlaceholderStatement;
class IfStatement;
class BreakableStatement;
class WhileStatement;
@ -72,6 +76,8 @@ class Identifier;
class ElementaryTypeNameExpression;
class Literal;
class VariableScope;
// Used as pointers to AST nodes, to be replaced by more clever pointers, e.g. pointers which do
// not do reference counting but point to a special memory area that is completely released
// explicitly.

3
libsolidity/ASTJsonConverter.cpp

@ -118,9 +118,10 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node)
bool ASTJsonConverter::visit(VariableDeclaration const& _node)
{
bool isLocalVariable = (_node.getLValueType() == VariableDeclaration::LValueType::LOCAL);
addJsonNode("VariableDeclaration",
{ make_pair("name", _node.getName()),
make_pair("local", boost::lexical_cast<std::string>(_node.isLocalVariable()))},
make_pair("local", boost::lexical_cast<std::string>(isLocalVariable))},
true);
return true;
}

60
libsolidity/ASTPrinter.cpp

@ -57,6 +57,13 @@ bool ASTPrinter::visit(ContractDefinition const& _node)
return goDeeper();
}
bool ASTPrinter::visit(InheritanceSpecifier const& _node)
{
writeLine("InheritanceSpecifier \"" + _node.getName()->getName() + "\"");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(StructDefinition const& _node)
{
writeLine("StructDefinition \"" + _node.getName() + "\"");
@ -87,6 +94,27 @@ bool ASTPrinter::visit(VariableDeclaration const& _node)
return goDeeper();
}
bool ASTPrinter::visit(ModifierDefinition const& _node)
{
writeLine("ModifierDefinition \"" + _node.getName() + "\"");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(ModifierInvocation const& _node)
{
writeLine("ModifierInvocation \"" + _node.getName()->getName() + "\"");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(EventDefinition const& _node)
{
writeLine("EventDefinition \"" + _node.getName() + "\"");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(TypeName const& _node)
{
writeLine("TypeName");
@ -129,6 +157,13 @@ bool ASTPrinter::visit(Block const& _node)
return goDeeper();
}
bool ASTPrinter::visit(PlaceholderStatement const& _node)
{
writeLine("PlaceholderStatement");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(IfStatement const& _node)
{
writeLine("IfStatement");
@ -302,6 +337,11 @@ void ASTPrinter::endVisit(ContractDefinition const&)
m_indentation--;
}
void ASTPrinter::endVisit(InheritanceSpecifier const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(StructDefinition const&)
{
m_indentation--;
@ -322,6 +362,21 @@ void ASTPrinter::endVisit(VariableDeclaration const&)
m_indentation--;
}
void ASTPrinter::endVisit(ModifierDefinition const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(ModifierInvocation const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(EventDefinition const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(TypeName const&)
{
m_indentation--;
@ -352,6 +407,11 @@ void ASTPrinter::endVisit(Block const&)
m_indentation--;
}
void ASTPrinter::endVisit(PlaceholderStatement const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(IfStatement const&)
{
m_indentation--;

10
libsolidity/ASTPrinter.h

@ -44,16 +44,21 @@ public:
bool visit(ImportDirective const& _node) override;
bool visit(ContractDefinition const& _node) override;
bool visit(InheritanceSpecifier 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(ModifierDefinition const& _node) override;
bool visit(ModifierInvocation const& _node) override;
bool visit(EventDefinition 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(PlaceholderStatement const& _node) override;
bool visit(IfStatement const& _node) override;
bool visit(BreakableStatement const& _node) override;
bool visit(WhileStatement const& _node) override;
@ -78,16 +83,21 @@ public:
void endVisit(ImportDirective const&) override;
void endVisit(ContractDefinition const&) override;
void endVisit(InheritanceSpecifier const&) override;
void endVisit(StructDefinition const&) override;
void endVisit(ParameterList const&) override;
void endVisit(FunctionDefinition const&) override;
void endVisit(VariableDeclaration const&) override;
void endVisit(ModifierDefinition const&) override;
void endVisit(ModifierInvocation const&) override;
void endVisit(EventDefinition 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(PlaceholderStatement const&) override;
void endVisit(IfStatement const&) override;
void endVisit(BreakableStatement const&) override;
void endVisit(WhileStatement const&) override;

20
libsolidity/ASTVisitor.h

@ -45,16 +45,21 @@ public:
virtual bool visit(SourceUnit&) { return true; }
virtual bool visit(ImportDirective&) { return true; }
virtual bool visit(ContractDefinition&) { return true; }
virtual bool visit(InheritanceSpecifier&) { return true; }
virtual bool visit(StructDefinition&) { return true; }
virtual bool visit(ParameterList&) { return true; }
virtual bool visit(FunctionDefinition&) { return true; }
virtual bool visit(VariableDeclaration&) { return true; }
virtual bool visit(ModifierDefinition&) { return true; }
virtual bool visit(ModifierInvocation&) { return true; }
virtual bool visit(EventDefinition&) { return true; }
virtual bool visit(TypeName&) { return true; }
virtual bool visit(ElementaryTypeName&) { return true; }
virtual bool visit(UserDefinedTypeName&) { return true; }
virtual bool visit(Mapping&) { return true; }
virtual bool visit(Statement&) { return true; }
virtual bool visit(Block&) { return true; }
virtual bool visit(PlaceholderStatement&) { return true; }
virtual bool visit(IfStatement&) { return true; }
virtual bool visit(BreakableStatement&) { return true; }
virtual bool visit(WhileStatement&) { return true; }
@ -81,16 +86,21 @@ public:
virtual void endVisit(SourceUnit&) { }
virtual void endVisit(ImportDirective&) { }
virtual void endVisit(ContractDefinition&) { }
virtual void endVisit(InheritanceSpecifier&) { }
virtual void endVisit(StructDefinition&) { }
virtual void endVisit(ParameterList&) { }
virtual void endVisit(FunctionDefinition&) { }
virtual void endVisit(VariableDeclaration&) { }
virtual void endVisit(ModifierDefinition&) { }
virtual void endVisit(ModifierInvocation&) { }
virtual void endVisit(EventDefinition&) { }
virtual void endVisit(TypeName&) { }
virtual void endVisit(ElementaryTypeName&) { }
virtual void endVisit(UserDefinedTypeName&) { }
virtual void endVisit(Mapping&) { }
virtual void endVisit(Statement&) { }
virtual void endVisit(Block&) { }
virtual void endVisit(PlaceholderStatement&) { }
virtual void endVisit(IfStatement&) { }
virtual void endVisit(BreakableStatement&) { }
virtual void endVisit(WhileStatement&) { }
@ -121,16 +131,21 @@ public:
virtual bool visit(SourceUnit const&) { return true; }
virtual bool visit(ImportDirective const&) { return true; }
virtual bool visit(ContractDefinition const&) { return true; }
virtual bool visit(InheritanceSpecifier const&) { return true; }
virtual bool visit(StructDefinition const&) { return true; }
virtual bool visit(ParameterList const&) { return true; }
virtual bool visit(FunctionDefinition const&) { return true; }
virtual bool visit(VariableDeclaration const&) { return true; }
virtual bool visit(ModifierDefinition const&) { return true; }
virtual bool visit(ModifierInvocation const&) { return true; }
virtual bool visit(EventDefinition const&) { return true; }
virtual bool visit(TypeName const&) { return true; }
virtual bool visit(ElementaryTypeName const&) { return true; }
virtual bool visit(UserDefinedTypeName const&) { return true; }
virtual bool visit(Mapping const&) { return true; }
virtual bool visit(Statement const&) { return true; }
virtual bool visit(Block const&) { return true; }
virtual bool visit(PlaceholderStatement const&) { return true; }
virtual bool visit(IfStatement const&) { return true; }
virtual bool visit(BreakableStatement const&) { return true; }
virtual bool visit(WhileStatement const&) { return true; }
@ -157,16 +172,21 @@ public:
virtual void endVisit(SourceUnit const&) { }
virtual void endVisit(ImportDirective const&) { }
virtual void endVisit(ContractDefinition const&) { }
virtual void endVisit(InheritanceSpecifier const&) { }
virtual void endVisit(StructDefinition const&) { }
virtual void endVisit(ParameterList const&) { }
virtual void endVisit(FunctionDefinition const&) { }
virtual void endVisit(VariableDeclaration const&) { }
virtual void endVisit(ModifierDefinition const&) { }
virtual void endVisit(ModifierInvocation const&) { }
virtual void endVisit(EventDefinition const&) { }
virtual void endVisit(TypeName const&) { }
virtual void endVisit(ElementaryTypeName const&) { }
virtual void endVisit(UserDefinedTypeName const&) { }
virtual void endVisit(Mapping const&) { }
virtual void endVisit(Statement const&) { }
virtual void endVisit(Block const&) { }
virtual void endVisit(PlaceholderStatement const&) { }
virtual void endVisit(IfStatement const&) { }
virtual void endVisit(BreakableStatement const&) { }
virtual void endVisit(WhileStatement const&) { }

72
libsolidity/AST_accept.h

@ -64,6 +64,8 @@ void ContractDefinition::accept(ASTVisitor& _visitor)
listAccept(m_baseContracts, _visitor);
listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor);
listAccept(m_events, _visitor);
listAccept(m_functionModifiers, _visitor);
listAccept(m_definedFunctions, _visitor);
}
_visitor.endVisit(*this);
@ -76,6 +78,8 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const
listAccept(m_baseContracts, _visitor);
listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor);
listAccept(m_events, _visitor);
listAccept(m_functionModifiers, _visitor);
listAccept(m_definedFunctions, _visitor);
}
_visitor.endVisit(*this);
@ -142,6 +146,7 @@ void FunctionDefinition::accept(ASTVisitor& _visitor)
m_parameters->accept(_visitor);
if (m_returnParameters)
m_returnParameters->accept(_visitor);
listAccept(m_functionModifiers, _visitor);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
@ -154,6 +159,7 @@ void FunctionDefinition::accept(ASTConstVisitor& _visitor) const
m_parameters->accept(_visitor);
if (m_returnParameters)
m_returnParameters->accept(_visitor);
listAccept(m_functionModifiers, _visitor);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
@ -175,6 +181,60 @@ void VariableDeclaration::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this);
}
void ModifierDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_parameters->accept(_visitor);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
}
void ModifierDefinition::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_parameters->accept(_visitor);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
}
void ModifierInvocation::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_modifierName->accept(_visitor);
listAccept(m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
void ModifierInvocation::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_modifierName->accept(_visitor);
listAccept(m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
void EventDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
m_parameters->accept(_visitor);
_visitor.endVisit(*this);
}
void EventDefinition::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
m_parameters->accept(_visitor);
_visitor.endVisit(*this);
}
void TypeName::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
@ -245,6 +305,18 @@ void Block::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this);
}
void PlaceholderStatement::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void PlaceholderStatement::accept(ASTConstVisitor& _visitor) const
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void IfStatement::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))

4
libsolidity/BaseTypes.h

@ -41,6 +41,8 @@ struct Location
start(_start), end(_end), sourceName(_sourceName) { }
Location(): start(-1), end(-1) { }
bool isEmpty() const { return start == -1 && end == -1; }
int start;
int end;
std::shared_ptr<std::string const> sourceName;
@ -49,6 +51,8 @@ struct Location
/// Stream output for Location (used e.g. in boost exceptions).
inline std::ostream& operator<<(std::ostream& _out, Location const& _location)
{
if (_location.isEmpty())
return _out << "NO_LOCATION_SPECIFIED";
return _out << *_location.sourceName << "[" << _location.start << "," << _location.end << ")";
}

104
libsolidity/CallGraph.cpp

@ -1,104 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Callgraph of functions inside a contract.
*/
#include <libsolidity/AST.h>
#include <libsolidity/CallGraph.h>
using namespace std;
namespace dev
{
namespace solidity
{
void CallGraph::addNode(ASTNode const& _node)
{
_node.accept(*this);
}
set<FunctionDefinition const*> const& CallGraph::getCalls()
{
computeCallGraph();
return m_functionsSeen;
}
void CallGraph::computeCallGraph()
{
while (!m_workQueue.empty())
{
m_workQueue.front()->accept(*this);
m_workQueue.pop();
}
}
bool CallGraph::visit(Identifier const& _identifier)
{
FunctionDefinition const* fun = dynamic_cast<FunctionDefinition const*>(_identifier.getReferencedDeclaration());
if (fun)
{
if (m_overrideResolver)
fun = (*m_overrideResolver)(fun->getName());
solAssert(fun, "Error finding override for function " + fun->getName());
addFunction(*fun);
}
return true;
}
bool CallGraph::visit(FunctionDefinition const& _function)
{
addFunction(_function);
return true;
}
bool CallGraph::visit(MemberAccess const& _memberAccess)
{
// used for "BaseContract.baseContractFunction"
if (_memberAccess.getExpression().getType()->getCategory() == Type::Category::TYPE)
{
TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.getExpression().getType());
if (type.getMembers().getMemberType(_memberAccess.getMemberName()))
{
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*type.getActualType())
.getContractDefinition();
for (ASTPointer<FunctionDefinition> const& function: contract.getDefinedFunctions())
if (function->getName() == _memberAccess.getMemberName())
{
addFunction(*function);
return true;
}
}
}
return true;
}
void CallGraph::addFunction(FunctionDefinition const& _function)
{
if (!m_functionsSeen.count(&_function))
{
m_functionsSeen.insert(&_function);
m_workQueue.push(&_function);
}
}
}
}

64
libsolidity/CallGraph.h

@ -1,64 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Callgraph of functions inside a contract.
*/
#include <set>
#include <queue>
#include <functional>
#include <boost/range/iterator_range.hpp>
#include <libsolidity/ASTVisitor.h>
namespace dev
{
namespace solidity
{
/**
* Can be used to compute the graph of calls (or rather references) between functions of the same
* contract. Current functionality is limited to computing all functions that are directly
* or indirectly called by some functions.
*/
class CallGraph: private ASTConstVisitor
{
public:
using OverrideResolver = std::function<FunctionDefinition const*(std::string const&)>;
CallGraph(OverrideResolver const& _overrideResolver): m_overrideResolver(&_overrideResolver) {}
void addNode(ASTNode const& _node);
std::set<FunctionDefinition const*> const& getCalls();
private:
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(Identifier const& _identifier) override;
virtual bool visit(MemberAccess const& _memberAccess) override;
void computeCallGraph();
void addFunction(FunctionDefinition const& _function);
OverrideResolver const* m_overrideResolver;
std::set<FunctionDefinition const*> m_functionsSeen;
std::queue<FunctionDefinition const*> m_workQueue;
};
}
}

240
libsolidity/Compiler.cpp

@ -28,7 +28,6 @@
#include <libsolidity/Compiler.h>
#include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerUtils.h>
#include <libsolidity/CallGraph.h>
using namespace std;
@ -40,17 +39,14 @@ void Compiler::compileContract(ContractDefinition const& _contract,
{
m_context = CompilerContext(); // clear it just in case
initializeContext(_contract, _contracts);
for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts())
for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
if (!function->isConstructor())
m_context.addFunction(*function);
appendFunctionSelector(_contract);
for (ContractDefinition const* contract: _contract.getLinearizedBaseContracts())
for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
if (!function->isConstructor())
function->accept(*this);
set<Declaration const*> functions = m_context.getFunctionsWithoutCode();
while (!functions.empty())
{
for (Declaration const* function: functions)
function->accept(*this);
functions = m_context.getFunctionsWithoutCode();
}
// Swap the runtime context with the creation-time context
swap(m_context, m_runtimeContext);
@ -62,6 +58,7 @@ void Compiler::initializeContext(ContractDefinition const& _contract,
map<ContractDefinition const*, bytes const*> const& _contracts)
{
m_context.setCompiledContracts(_contracts);
m_context.setInheritanceHierarchy(_contract.getLinearizedBaseContracts());
registerStateVariables(_contract);
}
@ -69,54 +66,18 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp
{
// arguments for base constructors, filled in derived-to-base order
map<ContractDefinition const*, vector<ASTPointer<Expression>> const*> baseArguments;
set<FunctionDefinition const*> neededFunctions;
set<ASTNode const*> nodesUsedInConstructors;
// Determine the arguments that are used for the base constructors and also which functions
// are needed at compile time.
// Determine the arguments that are used for the base constructors.
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)
{
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);
// Call constructors in base-to-derived order.
// The Constructor for the most derived contract is called later.
@ -138,10 +99,14 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp
m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY;
m_context << u256(0) << eth::Instruction::RETURN;
// note that we have to explicitly include all used functions because of absolute jump
// labels
for (FunctionDefinition const* fun: neededFunctions)
fun->accept(*this);
// note that we have to include the functions again because of absolute jump labels
set<Declaration const*> functions = m_context.getFunctionsWithoutCode();
while (!functions.empty())
{
for (Declaration const* function: functions)
function->accept(*this);
functions = m_context.getFunctionsWithoutCode();
}
}
void Compiler::appendBaseConstructorCall(FunctionDefinition const& _constructor,
@ -150,11 +115,7 @@ void Compiler::appendBaseConstructorCall(FunctionDefinition const& _constructor,
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]);
}
compileExpression(*_arguments[i], constructorType.getParameterTypes()[i]);
m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor));
m_context << returnLabel;
}
@ -166,71 +127,69 @@ void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
unsigned argumentSize = 0;
for (ASTPointer<VariableDeclaration> const& var: _constructor.getParameters())
argumentSize += CompilerUtils::getPaddedSize(var->getType()->getCalldataEncodedSize());
if (argumentSize > 0)
{
m_context << u256(argumentSize);
m_context.appendProgramSize();
m_context << u256(CompilerUtils::dataStartOffset); // copy it to byte four as expected for ABI calls
m_context << eth::Instruction::CODECOPY;
appendCalldataUnpacker(_constructor, true);
appendCalldataUnpacker(FunctionType(_constructor).getParameterTypes(), true);
}
m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor));
m_context << returnTag;
}
set<FunctionDefinition const*> Compiler::getFunctionsCalled(set<ASTNode const*> const& _nodes,
function<FunctionDefinition const*(string const&)> const& _resolveOverrides)
{
CallGraph callgraph(_resolveOverrides);
for (ASTNode const* node: _nodes)
callgraph.addNode(*node);
return callgraph.getCalls();
}
void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
{
map<FixedHash<4>, FunctionDefinition const*> interfaceFunctions = _contract.getInterfaceFunctions();
map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.getInterfaceFunctions();
map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints;
// retrieve the function signature hash from the calldata
m_context << u256(1) << u256(0);
CompilerUtils(m_context).loadFromMemory(0, 4, false, true);
if (!interfaceFunctions.empty())
CompilerUtils(m_context).loadFromMemory(0, 4, false, true);
// stack now is: 1 0 <funhash>
// for (auto it = interfaceFunctions.cbegin(); it != interfaceFunctions.cend(); ++it)
for (auto const& it: interfaceFunctions)
{
callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag()));
m_context << eth::dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << eth::Instruction::EQ;
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first));
}
m_context << eth::Instruction::STOP; // function not found
if (FunctionDefinition const* fallback = _contract.getFallbackFunction())
{
eth::AssemblyItem returnTag = m_context.pushNewTag();
fallback->accept(*this);
m_context << returnTag;
appendReturnValuePacker(FunctionType(*fallback).getReturnParameterTypes());
}
else
m_context << eth::Instruction::STOP; // function not found
for (auto const& it: interfaceFunctions)
{
FunctionDefinition const& function = *it.second;
FunctionTypePointer const& functionType = it.second;
m_context << callDataUnpackerEntryPoints.at(it.first);
eth::AssemblyItem returnTag = m_context.pushNewTag();
appendCalldataUnpacker(function);
m_context.appendJumpTo(m_context.getFunctionEntryLabel(function));
appendCalldataUnpacker(functionType->getParameterTypes());
m_context.appendJumpTo(m_context.getFunctionEntryLabel(it.second->getDeclaration()));
m_context << returnTag;
appendReturnValuePacker(function);
appendReturnValuePacker(functionType->getReturnParameterTypes());
}
}
unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory)
unsigned Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory)
{
// We do not check the calldata size, everything is zero-padded.
unsigned dataOffset = CompilerUtils::dataStartOffset; // the 4 bytes of the function hash signature
//@todo this can be done more efficiently, saving some CALLDATALOAD calls
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
for (TypePointer const& type: _typeParameters)
{
unsigned const c_numBytes = var->getType()->getCalldataEncodedSize();
unsigned const c_numBytes = type->getCalldataEncodedSize();
if (c_numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(var->getLocation())
<< errinfo_comment("Type " + var->getType()->toString() + " not yet supported."));
bool const c_leftAligned = var->getType()->getCategory() == Type::Category::STRING;
<< errinfo_comment("Type " + type->toString() + " not yet supported."));
bool const c_leftAligned = type->getCategory() == Type::Category::STRING;
bool const c_padToWords = true;
dataOffset += CompilerUtils(m_context).loadFromMemory(dataOffset, c_numBytes, c_leftAligned,
!_fromMemory, c_padToWords);
@ -238,26 +197,26 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b
return dataOffset;
}
void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
{
//@todo this can be also done more efficiently
unsigned dataOffset = 0;
vector<ASTPointer<VariableDeclaration>> const& parameters = _function.getReturnParameters();
unsigned stackDepth = CompilerUtils(m_context).getSizeOnStack(parameters);
for (unsigned i = 0; i < parameters.size(); ++i)
unsigned stackDepth = 0;
for (TypePointer const& type: _typeParameters)
stackDepth += type->getSizeOnStack();
for (TypePointer const& type: _typeParameters)
{
Type const& paramType = *parameters[i]->getType();
unsigned numBytes = paramType.getCalldataEncodedSize();
unsigned numBytes = type->getCalldataEncodedSize();
if (numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(parameters[i]->getLocation())
<< errinfo_comment("Type " + paramType.toString() + " not yet supported."));
CompilerUtils(m_context).copyToStackTop(stackDepth, paramType);
ExpressionCompiler::appendTypeConversion(m_context, paramType, paramType, true);
bool const c_leftAligned = paramType.getCategory() == Type::Category::STRING;
<< errinfo_comment("Type " + type->toString() + " not yet supported."));
CompilerUtils(m_context).copyToStackTop(stackDepth, *type);
ExpressionCompiler::appendTypeConversion(m_context, *type, *type, true);
bool const c_leftAligned = type->getCategory() == Type::Category::STRING;
bool const c_padToWords = true;
dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, c_leftAligned, c_padToWords);
stackDepth -= paramType.getSizeOnStack();
stackDepth -= type->getSizeOnStack();
}
// note that the stack is not cleaned up here
m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN;
@ -270,30 +229,54 @@ void Compiler::registerStateVariables(ContractDefinition const& _contract)
m_context.addStateVariable(*variable);
}
bool Compiler::visit(VariableDeclaration const& _variableDeclaration)
{
solAssert(_variableDeclaration.isStateVariable(), "Compiler visit to non-state variable declaration.");
m_context.startFunction(_variableDeclaration);
m_breakTags.clear();
m_continueTags.clear();
m_context << m_context.getFunctionEntryLabel(_variableDeclaration);
ExpressionCompiler::appendStateVariableAccessor(m_context, _variableDeclaration);
unsigned sizeOnStack = _variableDeclaration.getType()->getSizeOnStack();
solAssert(sizeOnStack <= 15, "Stack too deep.");
m_context << eth::dupInstruction(sizeOnStack + 1) << eth::Instruction::JUMP;
return false;
}
bool Compiler::visit(FunctionDefinition const& _function)
{
//@todo to simplify this, the calling convention could by changed such that
// caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn]
// although note that this reduces the size of the visible stack
m_context.startNewFunction();
m_context.startFunction(_function);
m_returnTag = m_context.newTag();
m_breakTags.clear();
m_continueTags.clear();
m_context << m_context.getFunctionEntryLabel(_function);
m_stackCleanupForReturn = 0;
m_currentFunction = &_function;
m_modifierDepth = 0;
// stack upon entry: [return address] [arg0] [arg1] ... [argn]
// reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp]
unsigned parametersSize = CompilerUtils::getSizeOnStack(_function.getParameters());
m_context.adjustStackOffset(parametersSize);
for (ASTPointer<VariableDeclaration const> const& variable: _function.getParameters())
m_context.addVariable(*variable);
{
m_context.addVariable(*variable, parametersSize);
parametersSize -= variable->getType()->getSizeOnStack();
}
for (ASTPointer<VariableDeclaration const> const& variable: _function.getReturnParameters())
m_context.addAndInitializeVariable(*variable);
for (VariableDeclaration const* localVariable: _function.getLocalVariables())
m_context.addAndInitializeVariable(*localVariable);
_function.getBody().accept(*this);
appendModifierOrFunctionCode();
m_context << m_returnTag;
@ -420,13 +403,15 @@ bool Compiler::visit(Return const& _return)
//@todo modifications are needed to make this work with functions returning multiple values
if (Expression const* expression = _return.getExpression())
{
compileExpression(*expression);
VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters().getParameters().front();
ExpressionCompiler::appendTypeConversion(m_context, *expression->getType(), *firstVariable.getType());
solAssert(_return.getFunctionReturnParameters(), "Invalid return parameters pointer.");
VariableDeclaration const& firstVariable = *_return.getFunctionReturnParameters()->getParameters().front();
compileExpression(*expression, firstVariable.getType());
CompilerUtils(m_context).moveToStackVariable(firstVariable);
}
for (unsigned i = 0; i < m_stackCleanupForReturn; ++i)
m_context << eth::Instruction::POP;
m_context.appendJumpTo(m_returnTag);
m_context.adjustStackOffset(m_stackCleanupForReturn);
return false;
}
@ -434,10 +419,7 @@ bool Compiler::visit(VariableDefinition const& _variableDefinition)
{
if (Expression const* expression = _variableDefinition.getExpression())
{
compileExpression(*expression);
ExpressionCompiler::appendTypeConversion(m_context,
*expression->getType(),
*_variableDefinition.getDeclaration().getType());
compileExpression(*expression, _variableDefinition.getDeclaration().getType());
CompilerUtils(m_context).moveToStackVariable(_variableDefinition.getDeclaration());
}
return false;
@ -451,9 +433,51 @@ bool Compiler::visit(ExpressionStatement const& _expressionStatement)
return false;
}
void Compiler::compileExpression(Expression const& _expression)
bool Compiler::visit(PlaceholderStatement const&)
{
++m_modifierDepth;
appendModifierOrFunctionCode();
--m_modifierDepth;
return true;
}
void Compiler::appendModifierOrFunctionCode()
{
solAssert(m_currentFunction, "");
if (m_modifierDepth >= m_currentFunction->getModifiers().size())
m_currentFunction->getBody().accept(*this);
else
{
ASTPointer<ModifierInvocation> const& modifierInvocation = m_currentFunction->getModifiers()[m_modifierDepth];
ModifierDefinition const& modifier = m_context.getFunctionModifier(modifierInvocation->getName()->getName());
solAssert(modifier.getParameters().size() == modifierInvocation->getArguments().size(), "");
for (unsigned i = 0; i < modifier.getParameters().size(); ++i)
{
m_context.addVariable(*modifier.getParameters()[i]);
compileExpression(*modifierInvocation->getArguments()[i],
modifier.getParameters()[i]->getType());
}
for (VariableDeclaration const* localVariable: modifier.getLocalVariables())
m_context.addAndInitializeVariable(*localVariable);
unsigned const c_stackSurplus = CompilerUtils::getSizeOnStack(modifier.getParameters()) +
CompilerUtils::getSizeOnStack(modifier.getLocalVariables());
m_stackCleanupForReturn += c_stackSurplus;
modifier.getBody().accept(*this);
for (unsigned i = 0; i < c_stackSurplus; ++i)
m_context << eth::Instruction::POP;
m_stackCleanupForReturn -= c_stackSurplus;
}
}
void Compiler::compileExpression(Expression const& _expression, TypePointer const& _targetType)
{
ExpressionCompiler::compileExpression(m_context, _expression, m_optimize);
if (_targetType)
ExpressionCompiler::appendTypeConversion(m_context, *_expression.getType(), *_targetType);
}
}

26
libsolidity/Compiler.h

@ -31,7 +31,8 @@ namespace solidity {
class Compiler: private ASTConstVisitor
{
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::map<ContractDefinition const*, bytes const*> const& _contracts);
@ -49,18 +50,15 @@ private:
void appendBaseConstructorCall(FunctionDefinition const& _constructor,
std::vector<ASTPointer<Expression>> const& _arguments);
void appendConstructorCall(FunctionDefinition const& _constructor);
/// Recursively searches the call graph and returns all functions referenced inside _nodes.
/// _resolveOverride is called to resolve virtual function overrides.
std::set<FunctionDefinition const*> getFunctionsCalled(std::set<ASTNode const*> const& _nodes,
std::function<FunctionDefinition const*(std::string const&)> const& _resolveOverride);
void appendFunctionSelector(ContractDefinition const& _contract);
/// Creates code that unpacks the arguments for the given function, from memory if
/// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes.
unsigned appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory = false);
void appendReturnValuePacker(FunctionDefinition const& _function);
/// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers.
/// From memory if @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes.
unsigned appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false);
void appendReturnValuePacker(TypePointers const& _typeParameters);
void registerStateVariables(ContractDefinition const& _contract);
virtual bool visit(VariableDeclaration const& _variableDeclaration) override;
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(IfStatement const& _ifStatement) override;
virtual bool visit(WhileStatement const& _whileStatement) override;
@ -70,8 +68,13 @@ private:
virtual bool visit(Return const& _return) override;
virtual bool visit(VariableDefinition const& _variableDefinition) override;
virtual bool visit(ExpressionStatement const& _expressionStatement) override;
virtual bool visit(PlaceholderStatement const&) override;
void compileExpression(Expression const& _expression);
/// Appends one layer of function modifier code of the current function, or the function
/// body itself if the last modifier was reached.
void appendModifierOrFunctionCode();
void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
bool const m_optimize;
CompilerContext m_context;
@ -79,6 +82,9 @@ private:
std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement
eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement
unsigned m_modifierDepth = 0;
FunctionDefinition const* m_currentFunction;
unsigned m_stackCleanupForReturn; ///< this number of stack elements need to be removed before jump to m_returnTag
};
}

91
libsolidity/CompilerContext.cpp

@ -43,10 +43,19 @@ void CompilerContext::addStateVariable(VariableDeclaration const& _declaration)
m_stateVariablesSize += _declaration.getType()->getStorageSize();
}
void CompilerContext::addVariable(VariableDeclaration const& _declaration)
void CompilerContext::startFunction(Declaration const& _function)
{
m_localVariables[&_declaration] = m_localVariablesSize;
m_localVariablesSize += _declaration.getType()->getSizeOnStack();
m_functionsWithCode.insert(&_function);
m_localVariables.clear();
m_asm.setDeposit(0);
*this << getFunctionEntryLabel(_function);
}
void CompilerContext::addVariable(VariableDeclaration const& _declaration,
unsigned _offsetToCurrent)
{
solAssert(m_asm.deposit() >= 0 && unsigned(m_asm.deposit()) >= _offsetToCurrent, "");
m_localVariables[&_declaration] = unsigned(m_asm.deposit()) - _offsetToCurrent;
}
void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _declaration)
@ -56,14 +65,6 @@ void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _decla
int const size = _declaration.getType()->getSizeOnStack();
for (int i = 0; i < size; ++i)
*this << u256(0);
m_asm.adjustDeposit(-size);
}
void CompilerContext::addFunction(FunctionDefinition const& _function)
{
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
@ -75,38 +76,82 @@ bytes const& CompilerContext::getCompiledContract(const ContractDefinition& _con
bool CompilerContext::isLocalVariable(Declaration const* _declaration) const
{
return m_localVariables.count(_declaration) > 0;
return m_localVariables.count(_declaration);
}
eth::AssemblyItem CompilerContext::getFunctionEntryLabel(Declaration const& _declaration)
{
auto res = m_functionEntryLabels.find(&_declaration);
if (res == m_functionEntryLabels.end())
{
eth::AssemblyItem tag(m_asm.newTag());
m_functionEntryLabels.insert(make_pair(&_declaration, tag));
return tag.tag();
}
else
return res->second.tag();
}
eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefinition const& _function)
{
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
for (ContractDefinition const* contract: m_inheritanceHierarchy)
for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
if (!function->isConstructor() && function->getName() == _function.getName())
return getFunctionEntryLabel(*function);
solAssert(false, "Virtual function " + _function.getName() + " not found.");
return m_asm.newTag(); // not reached
}
eth::AssemblyItem CompilerContext::getSuperFunctionEntryLabel(string const& _name, ContractDefinition const& _base)
{
// search for first contract after _base
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
auto it = find(m_inheritanceHierarchy.begin(), m_inheritanceHierarchy.end(), &_base);
solAssert(it != m_inheritanceHierarchy.end(), "Base not found in inheritance hierarchy.");
for (++it; it != m_inheritanceHierarchy.end(); ++it)
for (ASTPointer<FunctionDefinition> const& function: (*it)->getDefinedFunctions())
if (!function->isConstructor() && function->getName() == _name)
return getFunctionEntryLabel(*function);
solAssert(false, "Super function " + _name + " not found.");
return m_asm.newTag(); // not reached
}
eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition const& _function) const
set<Declaration const*> CompilerContext::getFunctionsWithoutCode()
{
auto res = m_functionEntryLabels.find(&_function);
solAssert(res != m_functionEntryLabels.end(), "Function entry label not found.");
return res->second.tag();
set<Declaration const*> functions;
for (auto const& it: m_functionEntryLabels)
if (m_functionsWithCode.count(it.first) == 0)
functions.insert(it.first);
return move(functions);
}
eth::AssemblyItem CompilerContext::getVirtualFunctionEntryLabel(FunctionDefinition const& _function) const
ModifierDefinition const& CompilerContext::getFunctionModifier(string const& _name) const
{
auto res = m_virtualFunctionEntryLabels.find(_function.getName());
solAssert(res != m_virtualFunctionEntryLabels.end(), "Function entry label not found.");
return res->second.tag();
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
for (ContractDefinition const* contract: m_inheritanceHierarchy)
for (ASTPointer<ModifierDefinition> const& modifier: contract->getFunctionModifiers())
if (modifier->getName() == _name)
return *modifier.get();
BOOST_THROW_EXCEPTION(InternalCompilerError()
<< errinfo_comment("Function modifier " + _name + " not found."));
}
unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const
{
auto res = m_localVariables.find(&_declaration);
solAssert(res != m_localVariables.end(), "Variable not found on stack.");
return m_localVariablesSize - res->second - 1;
return res->second;
}
unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const
{
return _baseOffset + m_asm.deposit();
return m_asm.deposit() - _baseOffset - 1;
}
unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const
{
return -baseToCurrentStackOffset(-_offset);
return m_asm.deposit() - _offset - 1;
}
u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const

32
libsolidity/CompilerContext.h

@ -41,10 +41,8 @@ class CompilerContext
public:
void addMagicGlobal(MagicVariableDeclaration const& _declaration);
void addStateVariable(VariableDeclaration const& _declaration);
void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); }
void addVariable(VariableDeclaration const& _declaration);
void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
void addAndInitializeVariable(VariableDeclaration const& _declaration);
void addFunction(FunctionDefinition const& _function);
void setCompiledContracts(std::map<ContractDefinition const*, bytes const*> const& _contracts) { m_compiledContracts = _contracts; }
bytes const& getCompiledContract(ContractDefinition const& _contract) const;
@ -52,14 +50,24 @@ public:
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration) != 0; }
bool isFunctionDefinition(Declaration const* _declaration) const { return m_functionEntryLabels.count(_declaration) != 0; }
bool isLocalVariable(Declaration const* _declaration) const;
bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration) != 0; }
eth::AssemblyItem getFunctionEntryLabel(FunctionDefinition const& _function) const;
eth::AssemblyItem getFunctionEntryLabel(Declaration const& _declaration);
void setInheritanceHierarchy(std::vector<ContractDefinition const*> const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; }
/// @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.
eth::AssemblyItem getVirtualFunctionEntryLabel(FunctionDefinition const& _function);
/// @returns the entry label of function with the given name from the most derived class just
/// above _base in the current inheritance hierarchy.
eth::AssemblyItem getSuperFunctionEntryLabel(std::string const& _name, ContractDefinition const& _base);
/// @returns the set of functions for which we still need to generate code
std::set<Declaration const*> getFunctionsWithoutCode();
/// Resets function specific members, inserts the function entry label and marks the function
/// as "having code".
void startFunction(Declaration const& _function);
ModifierDefinition const& getFunctionModifier(std::string const& _name) const;
/// Returns the distance of the given local variable from the bottom of the stack (of the current function).
unsigned getBaseStackOffsetOfVariable(Declaration const& _declaration) const;
/// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns
/// the distance of that variable from the current top of the stack.
@ -114,12 +122,12 @@ private:
std::map<Declaration const*, u256> m_stateVariables;
/// Offsets of local variables on the stack (relative to stack base).
std::map<Declaration const*, unsigned> m_localVariables;
/// Sum of stack sizes of local variables
unsigned m_localVariablesSize;
/// Labels pointing to the entry points of funcitons.
/// Labels pointing to the entry points of functions.
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;
/// Set of functions for which we did not yet generate code.
std::set<Declaration const*> m_functionsWithCode;
/// List of current inheritance hierarchy from derived to base.
std::vector<ContractDefinition const*> m_inheritanceHierarchy;
};
}

94
libsolidity/CompilerStack.cpp

@ -40,6 +40,27 @@ namespace dev
namespace solidity
{
const map<string, string> StandardSources = map<string, string>{
{"coin", R"(import "CoinReg";import "Config";import "configUser";contract coin is configUser{function coin(string3 name, uint denom) {CoinReg(Config(configAddr()).lookup(3)).register(name, denom);}})"},
{"Coin", R"(contract Coin{function isApprovedFor(address _target,address _proxy)constant returns(bool _r){}function isApproved(address _proxy)constant returns(bool _r){}function sendCoinFrom(address _from,uint256 _val,address _to){}function coinBalanceOf(address _a)constant returns(uint256 _r){}function sendCoin(uint256 _val,address _to){}function coinBalance()constant returns(uint256 _r){}function approve(address _a){}})"},
{"CoinReg", R"(contract CoinReg{function count()constant returns(uint256 r){}function info(uint256 i)constant returns(address addr,string3 name,uint256 denom){}function register(string3 name,uint256 denom){}function unregister(){}})"},
{"configUser", R"(contract configUser{function configAddr()constant returns(address a){ return 0xc6d9d2cd449a754c494264e1809c50e34d64562b;}})"},
{"Config", R"(contract Config{function lookup(uint256 service)constant returns(address a){}function kill(){}function unregister(uint256 id){}function register(uint256 id,address service){}})"},
{"mortal", R"(import "owned";contract mortal is owned {function kill() { if (msg.sender == owner) suicide(owner); }})"},
{"named", R"(import "Config";import "NameReg";import "configUser";contract named is configUser {function named(string32 name) {NameReg(Config(configAddr()).lookup(1)).register(name);}})"},
{"NameReg", R"(contract NameReg{function register(string32 name){}function addressOf(string32 name)constant returns(address addr){}function unregister(){}function nameOf(address addr)constant returns(string32 name){}})"},
{"owned", R"(contract owned{function owned(){owner = msg.sender;}modifier onlyowner(){if(msg.sender==owner)_}address owner;})"},
{"service", R"(import "Config";import "configUser";contract service is configUser{function service(uint _n){Config(configAddr()).register(_n, this);}})"},
{"std", R"(import "owned";import "mortal";import "Config";import "configUser";import "NameReg";import "named";)"}
};
CompilerStack::CompilerStack(bool _addStandardSources):
m_addStandardSources(_addStandardSources), m_parseSuccessful(false)
{
if (m_addStandardSources)
addSources(StandardSources);
}
bool CompilerStack::addSource(string const& _name, string const& _content)
{
bool existed = m_sources.count(_name) != 0;
@ -73,6 +94,7 @@ void CompilerStack::parse()
{
m_globalContext->setCurrentContract(*contract);
resolver.updateDeclaration(*m_globalContext->getCurrentThis());
resolver.updateDeclaration(*m_globalContext->getCurrentSuper());
resolver.resolveNamesAndTypes(*contract);
m_contracts[contract->getName()].contract = contract;
}
@ -91,7 +113,6 @@ void CompilerStack::parse()
void CompilerStack::parse(string const& _sourceCode)
{
setSource(_sourceCode);
addSources(StandardSources);
parse();
}
@ -105,44 +126,21 @@ vector<string> CompilerStack::getContractNames() const
return contractNames;
}
void CompilerStack::compile(bool _optimize)
{
if (!m_parseSuccessful)
parse();
map<ContractDefinition const*, bytes const*> contractBytecode;
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
compiler->compileContract(*contract, contractBytecode);
Contract& compiledContract = m_contracts[contract->getName()];
compiledContract.bytecode = compiler->getAssembledBytecode();
compiledContract.runtimeBytecode = compiler->getRuntimeBytecode();
compiledContract.compiler = move(compiler);
contractBytecode[compiledContract.contract] = &compiledContract.bytecode;
}
}
const map<string, string> StandardSources = map<string, string>{
/* { "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", "import \"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", "import \"Config\";\nimport \"NameReg\";\ncontract named {function named(string32 name) {NameReg(Config().lookup(1)).register(name);}}" },
{ "std", "import \"owned\";\nimport \"mortal\";\nimport \"Config\";\nimport \"NameReg\";\nimport \"named\";\n" },
*/};
////// BEGIN: TEMPORARY ONLY
///
/// NOTE: THIS INVALIDATES SOURCE POINTERS AND CAN CRASH THE COMPILER
///
/// remove once import works properly and we have genesis contracts
string CompilerStack::expanded(string const& _sourceCode)
{
const map<string, string> c_standardSources = map<string, string>{
{ "Config", "contract Config{function lookup(uint256 service)constant returns(address a){}function kill(){}function unregister(uint256 id){}function register(uint256 id,address service){}}" },
{ "Coin", "contract Coin{function isApprovedFor(address _target,address _proxy)constant returns(bool _r){}function isApproved(address _proxy)constant returns(bool _r){}function sendCoinFrom(address _from,uint256 _val,address _to){}function coinBalanceOf(address _a)constant returns(uint256 _r){}function sendCoin(uint256 _val,address _to){}function coinBalance()constant returns(uint256 _r){}function approve(address _a){}}"},
{ "CoinReg", "contract CoinReg{function count()constant returns(uint256 r){}function info(uint256 i)constant returns(address addr,string3 name,uint256 denom){}function register(string3 name,uint256 denom){}function unregister(){}}" },
{ "coin", "#require CoinReg\ncontract coin {function coin(string3 name, uint denom) {CoinReg(Config().lookup(3)).register(name, denom);}}" },
{ "service", "#require Config\ncontract service{function service(uint _n){Config().register(_n, this);}}" },
{ "owned", "contract owned{function owned(){owner = msg.sender;}address owner;}" },
{ "owned", "contract owned{function owned(){owner = msg.sender;}modifier onlyowner(){if(msg.sender==owner)_}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 {function named(string32 name) {NameReg(Config().lookup(1)).register(name);}}" },
@ -180,6 +178,26 @@ string CompilerStack::expanded(string const& _sourceCode)
////// END: TEMPORARY ONLY
void CompilerStack::compile(bool _optimize)
{
if (!m_parseSuccessful)
parse();
map<ContractDefinition const*, bytes const*> contractBytecode;
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
compiler->compileContract(*contract, contractBytecode);
Contract& compiledContract = m_contracts[contract->getName()];
compiledContract.bytecode = compiler->getAssembledBytecode();
compiledContract.runtimeBytecode = compiler->getRuntimeBytecode();
compiledContract.compiler = move(compiler);
contractBytecode[compiledContract.contract] = &compiledContract.bytecode;
}
}
bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
{
parse(_sourceCode);
@ -275,7 +293,11 @@ void CompilerStack::reset(bool _keepSources)
for (auto sourcePair: m_sources)
sourcePair.second.reset();
else
{
m_sources.clear();
if (m_addStandardSources)
addSources(StandardSources);
}
m_globalContext.reset();
m_sourceOrder.clear();
m_contracts.clear();
@ -317,10 +339,12 @@ CompilerStack::Contract const& CompilerStack::getContract(string const& _contrac
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found."));
string contractName = _contractName;
if (_contractName.empty())
// try to find the "last contract"
for (ASTPointer<ASTNode> const& node: m_sourceOrder.back()->ast->getNodes())
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
contractName = contract->getName();
// try to find some user-supplied contract
for (auto const& it: m_sources)
if (!StandardSources.count(it.first))
for (ASTPointer<ASTNode> const& node: it.second.ast->getNodes())
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
contractName = contract->getName();
auto it = m_contracts.find(contractName);
if (it == m_contracts.end())
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found."));

6
libsolidity/CompilerStack.h

@ -59,7 +59,8 @@ extern const std::map<std::string, std::string> StandardSources;
class CompilerStack: boost::noncopyable
{
public:
CompilerStack(): m_parseSuccessful(false) {}
/// Creates a new compiler stack. Adds standard sources if @a _addStandardSources.
explicit CompilerStack(bool _addStandardSources = false);
/// Adds a source object (e.g. file) to the parser. After this, parse has to be called again.
/// @returns true if a source object by the name already existed and was replaced.
@ -68,7 +69,7 @@ public:
void setSource(std::string const& _sourceCode);
/// Parses all source units that were added
void parse();
/// Sets the given source code as the only source unit and parses it.
/// Sets the given source code as the only source unit apart from standard sources and parses it.
void parse(std::string const& _sourceCode);
/// Returns a list of the contract names in the sources.
std::vector<std::string> getContractNames() const;
@ -151,6 +152,7 @@ private:
Contract const& getContract(std::string const& _contractName = "") const;
Source const& getSource(std::string const& _sourceName = "") const;
bool m_addStandardSources; ///< If true, standard sources are added.
bool m_parseSuccessful;
std::map<std::string const, Source> m_sources;
std::shared_ptr<GlobalContext> m_globalContext;

269
libsolidity/ExpressionCompiler.cpp

@ -23,6 +23,7 @@
#include <utility>
#include <numeric>
#include <libdevcore/Common.h>
#include <libdevcrypto/SHA3.h>
#include <libsolidity/AST.h>
#include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerContext.h>
@ -48,6 +49,12 @@ void ExpressionCompiler::appendTypeConversion(CompilerContext& _context, Type co
compiler.appendTypeConversion(_typeOnStack, _targetType, _cleanupNeeded);
}
void ExpressionCompiler::appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const& _varDecl, bool _optimize)
{
ExpressionCompiler compiler(_context, _optimize);
compiler.appendStateVariableAccessor(_varDecl);
}
bool ExpressionCompiler::visit(Assignment const& _assignment)
{
_assignment.getRightHandSide().accept(*this);
@ -60,7 +67,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
{
if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;
m_currentLValue.retrieveValue(_assignment, true);
m_currentLValue.retrieveValue(_assignment.getType(), _assignment.getLocation(), true);
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType());
if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1;
@ -101,7 +108,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
case Token::INC: // ++ (pre- or postfix)
case Token::DEC: // -- (pre- or postfix)
solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
m_currentLValue.retrieveValue(_unaryOperation);
m_currentLValue.retrieveValue(_unaryOperation.getType(), _unaryOperation.getLocation());
if (!_unaryOperation.isPrefixOperation())
{
if (m_currentLValue.storesReferenceOnStack())
@ -298,10 +305,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << eth::Instruction::SUICIDE;
break;
case Location::SHA3:
arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
// @todo move this once we actually use memory
CompilerUtils(m_context).storeInMemory(0);
appendExpressionCopyToMemory(*function.getParameterTypes().front(), *arguments.front());
m_context << u256(32) << u256(0) << eth::Instruction::SHA3;
break;
case Location::LOG0:
@ -311,14 +315,41 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
case Location::LOG4:
{
unsigned logNumber = int(function.getLocation()) - int(Location::LOG0);
for (int arg = logNumber; arg >= 0; --arg)
for (unsigned arg = logNumber; arg > 0; --arg)
{
arguments[arg]->accept(*this);
appendTypeConversion(*arguments[arg]->getType(), *function.getParameterTypes()[arg], true);
}
// @todo move this once we actually use memory
CompilerUtils(m_context).storeInMemory(0);
m_context << u256(32) << u256(0) << eth::logInstruction(logNumber);
unsigned length = appendExpressionCopyToMemory(*function.getParameterTypes().front(),
*arguments.front());
solAssert(length == 32, "Log data should be 32 bytes long (for now).");
m_context << u256(length) << u256(0) << eth::logInstruction(logNumber);
break;
}
case Location::EVENT:
{
_functionCall.getExpression().accept(*this);
auto const& event = dynamic_cast<EventDefinition const&>(function.getDeclaration());
// Copy all non-indexed arguments to memory (data)
unsigned numIndexed = 0;
unsigned memLength = 0;
for (unsigned arg = 0; arg < arguments.size(); ++arg)
if (!event.getParameters()[arg]->isIndexed())
memLength += appendExpressionCopyToMemory(*function.getParameterTypes()[arg],
*arguments[arg], memLength);
// All indexed arguments go to the stack
for (unsigned arg = arguments.size(); arg > 0; --arg)
if (event.getParameters()[arg - 1]->isIndexed())
{
++numIndexed;
arguments[arg - 1]->accept(*this);
appendTypeConversion(*arguments[arg - 1]->getType(),
*function.getParameterTypes()[arg - 1], true);
}
m_context << u256(h256::Arith(dev::sha3(function.getCanonicalSignature(event.getName()))));
++numIndexed;
solAssert(numIndexed <= 4, "Too many indexed arguments.");
m_context << u256(memLength) << u256(0) << eth::logInstruction(numIndexed);
break;
}
case Location::BLOCKHASH:
@ -359,15 +390,25 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
{
case Type::Category::CONTRACT:
{
bool alsoSearchInteger = false;
ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.getExpression().getType());
u256 identifier = type.getFunctionIdentifier(member);
if (identifier != Invalid256)
if (type.isSuper())
m_context << m_context.getSuperFunctionEntryLabel(member, type.getContractDefinition()).pushTag();
else
{
appendTypeConversion(type, IntegerType(0, IntegerType::Modifier::ADDRESS), true);
m_context << identifier;
break;
// ordinary contract type
u256 identifier = type.getFunctionIdentifier(member);
if (identifier != Invalid256)
{
appendTypeConversion(type, IntegerType(0, IntegerType::Modifier::ADDRESS), true);
m_context << identifier;
}
else
// not found in contract, search in members inherited from address
alsoSearchInteger = true;
}
// fall-through to "integer" otherwise (address)
if (!alsoSearchInteger)
break;
}
case Type::Category::INTEGER:
if (member == "balance")
@ -443,14 +484,13 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
{
_indexAccess.getBaseExpression().accept(*this);
_indexAccess.getIndexExpression().accept(*this);
appendTypeConversion(*_indexAccess.getIndexExpression().getType(),
*dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType(),
true);
TypePointer const& keyType = dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType();
unsigned length = appendExpressionCopyToMemory(*keyType, _indexAccess.getIndexExpression());
solAssert(length == 32, "Mapping key has to take 32 bytes in memory (for now).");
// @todo move this once we actually use memory
CompilerUtils(m_context).storeInMemory(0);
CompilerUtils(m_context).storeInMemory(32);
m_context << u256(64) << u256(0) << eth::Instruction::SHA3;
length += CompilerUtils(m_context).storeInMemory(length);
m_context << u256(length) << u256(0) << eth::Instruction::SHA3;
m_currentLValue = LValue(m_context, LValue::STORAGE, *_indexAccess.getType());
m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess);
@ -463,8 +503,10 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
Declaration const* declaration = _identifier.getReferencedDeclaration();
if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
{
if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) // must be "this"
m_context << eth::Instruction::ADDRESS;
if (magicVar->getType()->getCategory() == Type::Category::CONTRACT)
// "this" or "super"
if (!dynamic_cast<ContractType const&>(*magicVar->getType()).isSuper())
m_context << eth::Instruction::ADDRESS;
}
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag();
@ -477,6 +519,10 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
{
// no-op
}
else if (dynamic_cast<EventDefinition const*>(declaration))
{
// no-op
}
else
{
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context."));
@ -628,38 +674,65 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
return;
Type::Category stackTypeCategory = _typeOnStack.getCategory();
Type::Category targetTypeCategory = _targetType.getCategory();
if (stackTypeCategory == Type::Category::INTEGER || stackTypeCategory == Type::Category::CONTRACT ||
stackTypeCategory == Type::Category::INTEGER_CONSTANT)
if (stackTypeCategory == Type::Category::STRING)
{
solAssert(targetTypeCategory == Type::Category::INTEGER || targetTypeCategory == Type::Category::CONTRACT, "");
IntegerType addressType(0, IntegerType::Modifier::ADDRESS);
IntegerType const& targetType = targetTypeCategory == Type::Category::INTEGER
? dynamic_cast<IntegerType const&>(_targetType) : addressType;
if (stackTypeCategory == Type::Category::INTEGER_CONSTANT)
if (targetTypeCategory == Type::Category::INTEGER)
{
IntegerConstantType const& constType = dynamic_cast<IntegerConstantType const&>(_typeOnStack);
// We know that the stack is clean, we only have to clean for a narrowing conversion
// where cleanup is forced.
if (targetType.getNumBits() < constType.getIntegerType()->getNumBits() && _cleanupNeeded)
appendHighBitsCleanup(targetType);
// conversion from string to hash. no need to clean the high bit
// only to shift right because of opposite alignment
IntegerType const& targetIntegerType = dynamic_cast<IntegerType const&>(_targetType);
StaticStringType const& typeOnStack = dynamic_cast<StaticStringType const&>(_typeOnStack);
solAssert(targetIntegerType.isHash(), "Only conversion between String and Hash is allowed.");
solAssert(targetIntegerType.getNumBits() == typeOnStack.getNumBytes() * 8, "The size should be the same.");
m_context << (u256(1) << (256 - typeOnStack.getNumBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
}
else
{
IntegerType const& typeOnStack = stackTypeCategory == Type::Category::INTEGER
? dynamic_cast<IntegerType const&>(_typeOnStack) : addressType;
// Widening: clean up according to source type width
// Non-widening and force: clean up according to target type bits
if (targetType.getNumBits() > typeOnStack.getNumBits())
appendHighBitsCleanup(typeOnStack);
else if (_cleanupNeeded)
appendHighBitsCleanup(targetType);
solAssert(targetTypeCategory == Type::Category::STRING, "Invalid type conversion requested.");
// nothing to do, strings are high-order-bit-aligned
//@todo clear lower-order bytes if we allow explicit conversion to shorter strings
}
}
else if (stackTypeCategory == Type::Category::STRING)
else if (stackTypeCategory == Type::Category::INTEGER || stackTypeCategory == Type::Category::CONTRACT ||
stackTypeCategory == Type::Category::INTEGER_CONSTANT)
{
solAssert(targetTypeCategory == Type::Category::STRING, "");
// nothing to do, strings are high-order-bit-aligned
//@todo clear lower-order bytes if we allow explicit conversion to shorter strings
if (targetTypeCategory == Type::Category::STRING && stackTypeCategory == Type::Category::INTEGER)
{
// conversion from hash to string. no need to clean the high bit
// only to shift left because of opposite alignment
StaticStringType const& targetStringType = dynamic_cast<StaticStringType const&>(_targetType);
IntegerType const& typeOnStack = dynamic_cast<IntegerType const&>(_typeOnStack);
solAssert(typeOnStack.isHash(), "Only conversion between String and Hash is allowed.");
solAssert(typeOnStack.getNumBits() == targetStringType.getNumBytes() * 8, "The size should be the same.");
m_context << (u256(1) << (256 - typeOnStack.getNumBits())) << eth::Instruction::MUL;
}
else
{
solAssert(targetTypeCategory == Type::Category::INTEGER || targetTypeCategory == Type::Category::CONTRACT, "");
IntegerType addressType(0, IntegerType::Modifier::ADDRESS);
IntegerType const& targetType = targetTypeCategory == Type::Category::INTEGER
? dynamic_cast<IntegerType const&>(_targetType) : addressType;
if (stackTypeCategory == Type::Category::INTEGER_CONSTANT)
{
IntegerConstantType const& constType = dynamic_cast<IntegerConstantType const&>(_typeOnStack);
// We know that the stack is clean, we only have to clean for a narrowing conversion
// where cleanup is forced.
if (targetType.getNumBits() < constType.getIntegerType()->getNumBits() && _cleanupNeeded)
appendHighBitsCleanup(targetType);
}
else
{
IntegerType const& typeOnStack = stackTypeCategory == Type::Category::INTEGER
? dynamic_cast<IntegerType const&>(_typeOnStack) : addressType;
// Widening: clean up according to source type width
// Non-widening and force: clean up according to target type bits
if (targetType.getNumBits() > typeOnStack.getNumBits())
appendHighBitsCleanup(typeOnStack);
else if (_cleanupNeeded)
appendHighBitsCleanup(targetType);
}
}
}
else if (_typeOnStack != _targetType)
// All other types should not be convertible to non-equal types.
@ -746,22 +819,32 @@ unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _typ
{
unsigned length = 0;
for (unsigned i = 0; i < _arguments.size(); ++i)
{
_arguments[i]->accept(*this);
appendTypeConversion(*_arguments[i]->getType(), *_types[i], true);
unsigned const c_numBytes = _types[i]->getCalldataEncodedSize();
if (c_numBytes == 0 || c_numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(_arguments[i]->getLocation())
<< errinfo_comment("Type " + _types[i]->toString() + " not yet supported."));
bool const c_leftAligned = _types[i]->getCategory() == Type::Category::STRING;
bool const c_padToWords = true;
length += CompilerUtils(m_context).storeInMemory(_memoryOffset + length, c_numBytes,
c_leftAligned, c_padToWords);
}
length += appendExpressionCopyToMemory(*_types[i], *_arguments[i], _memoryOffset + length);
return length;
}
unsigned ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType,
Expression const& _expression, unsigned _memoryOffset)
{
_expression.accept(*this);
appendTypeConversion(*_expression.getType(), _expectedType, true);
unsigned const c_numBytes = CompilerUtils::getPaddedSize(_expectedType.getCalldataEncodedSize());
if (c_numBytes == 0 || c_numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Type " + _expectedType.toString() + " not yet supported."));
bool const c_leftAligned = _expectedType.getCategory() == Type::Category::STRING;
bool const c_padToWords = true;
return CompilerUtils(m_context).storeInMemory(_memoryOffset, c_numBytes, c_leftAligned, c_padToWords);
}
void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl)
{
m_currentLValue.fromStateVariable(_varDecl, _varDecl.getType());
solAssert(m_currentLValue.isInStorage(), "");
m_currentLValue.retrieveValue(_varDecl.getType(), Location(), true);
}
ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,
unsigned _baseStackOffset):
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset)
@ -774,7 +857,7 @@ ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType
m_size = unsigned(_dataType.getSizeOnStack());
}
void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bool _remove) const
void ExpressionCompiler::LValue::retrieveValue(TypePointer const& _type, Location const& _location, bool _remove) const
{
switch (m_type)
{
@ -782,42 +865,47 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
{
unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset));
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(_location)
<< errinfo_comment("Stack too deep."));
for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::dupInstruction(stackPos + 1);
break;
}
case STORAGE:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
if (!_remove)
*m_context << eth::Instruction::DUP1;
if (m_size == 1)
*m_context << eth::Instruction::SLOAD;
else
for (unsigned i = 0; i < m_size; ++i)
{
*m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1;
if (i + 1 < m_size)
*m_context << u256(1) << eth::Instruction::ADD;
else
*m_context << eth::Instruction::POP;
}
retrieveValueFromStorage(_type, _remove);
break;
case MEMORY:
if (!_expression.getType()->isValueType())
if (!_type->isValueType())
break; // no distinction between value and reference for non-value types
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Location type not yet implemented."));
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Unsupported location type."));
break;
}
}
void ExpressionCompiler::LValue::retrieveValueFromStorage(TypePointer const& _type, bool _remove) const
{
if (!_type->isValueType())
return; // no distinction between value and reference for non-value types
if (!_remove)
*m_context << eth::Instruction::DUP1;
if (m_size == 1)
*m_context << eth::Instruction::SLOAD;
else
for (unsigned i = 0; i < m_size; ++i)
{
*m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1;
if (i + 1 < m_size)
*m_context << u256(1) << eth::Instruction::ADD;
else
*m_context << eth::Instruction::POP;
}
}
void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool _move) const
{
switch (m_type)
@ -832,7 +920,7 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
for (unsigned i = 0; i < m_size; ++i)
*m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP;
if (!_move)
retrieveValue(_expression);
retrieveValue(_expression.getType(), _expression.getLocation());
break;
}
case LValue::STORAGE:
@ -919,11 +1007,19 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co
{
if (!_expression.lvalueRequested())
{
retrieveValue(_expression, true);
retrieveValue(_expression.getType(), _expression.getLocation(), true);
reset();
}
}
void ExpressionCompiler::LValue::fromStateVariable(Declaration const& _varDecl, TypePointer const& _type)
{
m_type = STORAGE;
solAssert(_type->getStorageSize() <= numeric_limits<unsigned>::max(), "The storage size of " + _type->toString() + " should fit in an unsigned");
*m_context << m_context->getStorageLocationOfVariable(_varDecl);
m_size = unsigned(_type->getStorageSize());
}
void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration)
{
if (m_context->isLocalVariable(&_declaration))
@ -934,10 +1030,7 @@ void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, D
}
else if (m_context->isStateVariable(&_declaration))
{
m_type = STORAGE;
solAssert(_identifier.getType()->getStorageSize() <= numeric_limits<unsigned>::max(), "The storage size of " + _identifier.getType()->toString() + " should fit in unsigned");
m_size = unsigned(_identifier.getType()->getStorageSize());
*m_context << m_context->getStorageLocationOfVariable(_declaration);
fromStateVariable(_declaration, _identifier.getType());
}
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_identifier.getLocation())

22
libsolidity/ExpressionCompiler.h

@ -53,6 +53,8 @@ public:
/// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type.
static void appendTypeConversion(CompilerContext& _context, Type const& _typeOnStack,
Type const& _targetType, bool _cleanupNeeded = false);
/// Appends code for a State Variable accessor function
static void appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const& _varDecl, bool _optimize = false);
private:
explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false):
@ -92,8 +94,16 @@ private:
bool bare = false);
/// Appends code that copies the given arguments to memory (with optional offset).
/// @returns the number of bytes copied to memory
unsigned appendArgumentCopyToMemory(TypePointers const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments,
unsigned appendArgumentCopyToMemory(TypePointers const& _types,
std::vector<ASTPointer<Expression const>> const& _arguments,
unsigned _memoryOffset = 0);
/// Appends code that evaluates a single expression and copies it to memory (with optional offset).
/// @returns the number of bytes copied to memory
unsigned appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression,
unsigned _memoryOffset = 0);
/// Appends code for a State Variable accessor function
void appendStateVariableAccessor(VariableDeclaration const& _varDecl);
/**
* Helper class to store and retrieve lvalues to and from various locations.
@ -111,6 +121,8 @@ private:
/// Set type according to the declaration and retrieve the reference.
/// @a _expression is the current expression
void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration);
/// Convenience function to set type for a state variable and retrieve the reference
void fromStateVariable(Declaration const& _varDecl, TypePointer const& _type);
void reset() { m_type = NONE; m_baseStackOffset = 0; m_size = 0; }
bool isValid() const { return m_type != NONE; }
@ -123,8 +135,9 @@ private:
/// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true,
/// also removes the reference from the stack (note that is does not reset the type to @a NONE).
/// @a _expression is the current expression, used for error reporting.
void retrieveValue(Expression const& _expression, bool _remove = false) const;
/// @a _type is the type of the current expression and @ _location its location, used for error reporting.
/// @a _location can be a nullptr for expressions that don't have an actual ASTNode equivalent
void retrieveValue(TypePointer const& _type, Location const& _location, bool _remove = false) const;
/// Stores a value (from the stack directly beneath the reference, which is assumed to
/// be on the top of the stack, if any) in the lvalue and removes the reference.
/// Also removes the stored value from the stack if @a _move is
@ -138,6 +151,9 @@ private:
void retrieveValueIfLValueNotRequested(Expression const& _expression);
private:
/// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue
void retrieveValueFromStorage(TypePointer const& _type, bool _remove = false) const;
CompilerContext* m_context;
LValueType m_type = NONE;
/// If m_type is STACK, this is base stack offset (@see

8
libsolidity/GlobalContext.cpp

@ -83,5 +83,13 @@ MagicVariableDeclaration const* GlobalContext::getCurrentThis() const
}
MagicVariableDeclaration const* GlobalContext::getCurrentSuper() const
{
if (!m_superPointer[m_currentContract])
m_superPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(
"super", make_shared<ContractType>(*m_currentContract, true));
return m_superPointer[m_currentContract].get();
}
}
}

2
libsolidity/GlobalContext.h

@ -48,6 +48,7 @@ public:
GlobalContext();
void setCurrentContract(ContractDefinition const& _contract);
MagicVariableDeclaration const* getCurrentThis() const;
MagicVariableDeclaration const* getCurrentSuper() const;
/// @returns a vector of all implicit global declarations excluding "this".
std::vector<Declaration const*> getDeclarations() const;
@ -56,6 +57,7 @@ private:
std::vector<std::shared_ptr<MagicVariableDeclaration const>> m_magicVariables;
ContractDefinition const* m_currentContract = nullptr;
std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration const>> mutable m_thisPointer;
std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration const>> mutable m_superPointer;
};
}

81
libsolidity/InterfaceHandler.cpp

@ -37,34 +37,52 @@ std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefiniti
std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinition const& _contractDef)
{
Json::Value methods(Json::arrayValue);
Json::Value abi(Json::arrayValue);
for (auto const& it: _contractDef.getInterfaceFunctions())
{
Json::Value method;
Json::Value inputs(Json::arrayValue);
Json::Value outputs(Json::arrayValue);
auto populateParameters = [](std::vector<ASTPointer<VariableDeclaration>> const& _vars)
auto populateParameters = [](vector<string> const& _paramNames, vector<string> const& _paramTypes)
{
Json::Value params(Json::arrayValue);
for (ASTPointer<VariableDeclaration> const& var: _vars)
solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match");
for (unsigned i = 0; i < _paramNames.size(); ++i)
{
Json::Value input;
input["name"] = var->getName();
input["type"] = var->getType()->toString();
params.append(input);
Json::Value param;
param["name"] = _paramNames[i];
param["type"] = _paramTypes[i];
params.append(param);
}
return params;
};
method["name"] = it.second->getName();
method["constant"] = it.second->isDeclaredConst();
method["inputs"] = populateParameters(it.second->getParameters());
method["outputs"] = populateParameters(it.second->getReturnParameters());
methods.append(method);
Json::Value method;
method["type"] = "function";
method["name"] = it.second->getDeclaration().getName();
method["constant"] = it.second->isConstant();
method["inputs"] = populateParameters(it.second->getParameterNames(),
it.second->getParameterTypeNames());
method["outputs"] = populateParameters(it.second->getReturnParameterNames(),
it.second->getReturnParameterTypeNames());
abi.append(method);
}
for (auto const& it: _contractDef.getInterfaceEvents())
{
Json::Value event;
event["type"] = "event";
event["name"] = it->getName();
Json::Value params(Json::arrayValue);
for (auto const& p: it->getParameters())
{
Json::Value input;
input["name"] = p->getName();
input["type"] = p->getType()->toString();
input["indexed"] = p->isIndexed();
params.append(input);
}
event["inputs"] = params;
abi.append(event);
}
return std::unique_ptr<std::string>(new std::string(m_writer.write(methods)));
return std::unique_ptr<std::string>(new std::string(m_writer.write(abi)));
}
unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition const& _contractDef)
@ -72,21 +90,34 @@ unique_ptr<string> InterfaceHandler::getABISolidityInterface(ContractDefinition
string ret = "contract " + _contractDef.getName() + "{";
for (auto const& it: _contractDef.getInterfaceFunctions())
{
FunctionDefinition const* f = it.second;
auto populateParameters = [](vector<ASTPointer<VariableDeclaration>> const& _vars)
auto populateParameters = [](vector<string> const& _paramNames,
vector<string> const& _paramTypes)
{
string r = "";
for (ASTPointer<VariableDeclaration> const& var: _vars)
r += (r.size() ? "," : "(") + var->getType()->toString() + " " + var->getName();
solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match");
for (unsigned i = 0; i < _paramNames.size(); ++i)
r += (r.size() ? "," : "(") + _paramTypes[i] + " " + _paramNames[i];
return r.size() ? r + ")" : "()";
};
ret += "function " + f->getName() + populateParameters(f->getParameters()) + (f->isDeclaredConst() ? "constant " : "");
if (f->getReturnParameters().size())
ret += "returns" + populateParameters(f->getReturnParameters());
ret += "function " + it.second->getDeclaration().getName() +
populateParameters(it.second->getParameterNames(), it.second->getParameterTypeNames()) +
(it.second->isConstant() ? "constant " : "");
if (it.second->getReturnParameterTypes().size())
ret += "returns" + populateParameters(it.second->getReturnParameterNames(), it.second->getReturnParameterTypeNames());
else if (ret.back() == ' ')
ret.pop_back();
ret += "{}";
}
for (auto const& it: _contractDef.getInterfaceEvents())
{
std::string params;
for (auto const& p: it->getParameters())
params += (params.empty() ? "(" : ",") + p->getType()->toString() + (p->isIndexed() ? " indexed " : " ") + p->getName();
if (!params.empty())
params += ")";
ret += "event " + it->getName() + params + ";";
}
return unique_ptr<string>(new string(ret + "}"));
}

43
libsolidity/NameAndTypeResolver.cpp

@ -60,6 +60,13 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
ReferencesResolver resolver(*structDef, *this, &_contract, nullptr);
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
ReferencesResolver resolver(*variable, *this, &_contract, nullptr);
for (ASTPointer<EventDefinition> const& event: _contract.getEvents())
ReferencesResolver resolver(*event, *this, &_contract, nullptr);
for (ASTPointer<ModifierDefinition> const& modifier: _contract.getFunctionModifiers())
{
m_currentScope = &m_scopes[modifier.get()];
ReferencesResolver resolver(*modifier, *this, &_contract, nullptr);
}
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
{
m_currentScope = &m_scopes[function.get()];
@ -111,7 +118,7 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract)
{
// 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});
list<list<ContractDefinition const*>> input(1, {});
for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: _contract.getBaseContracts())
{
ASTPointer<Identifier> baseName = baseSpecifier->getName();
@ -119,14 +126,15 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract)
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);
// "push_front" has the effect that bases mentioned later can overwrite members of bases
// mentioned earlier
input.back().push_front(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()));
}
input.back().push_front(&_contract);
vector<ContractDefinition const*> result = cThreeMerge(input);
if (result.empty())
BOOST_THROW_EXCEPTION(_contract.createTypeError("Linearization of inheritance graph impossible"));
@ -226,6 +234,19 @@ void DeclarationRegistrationHelper::endVisit(FunctionDefinition&)
closeCurrentScope();
}
bool DeclarationRegistrationHelper::visit(ModifierDefinition& _modifier)
{
registerDeclaration(_modifier, true);
m_currentFunction = &_modifier;
return true;
}
void DeclarationRegistrationHelper::endVisit(ModifierDefinition&)
{
m_currentFunction = nullptr;
closeCurrentScope();
}
void DeclarationRegistrationHelper::endVisit(VariableDefinition& _variableDefinition)
{
// Register the local variables with the function
@ -240,6 +261,17 @@ bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration)
return true;
}
bool DeclarationRegistrationHelper::visit(EventDefinition& _event)
{
registerDeclaration(_event, true);
return true;
}
void DeclarationRegistrationHelper::endVisit(EventDefinition&)
{
closeCurrentScope();
}
void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration)
{
map<ASTNode const*, DeclarationContainer>::iterator iter;
@ -292,8 +324,7 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
bool ReferencesResolver::visit(Return& _return)
{
solAssert(m_returnParameters, "Return parameters not set.");
_return.setFunctionReturnParameters(*m_returnParameters);
_return.setFunctionReturnParameters(m_returnParameters);
return true;
}

22
libsolidity/NameAndTypeResolver.h

@ -94,14 +94,18 @@ public:
DeclarationRegistrationHelper(std::map<ASTNode const*, DeclarationContainer>& _scopes, ASTNode& _astRoot);
private:
bool visit(ContractDefinition& _contract);
void endVisit(ContractDefinition& _contract);
bool visit(StructDefinition& _struct);
void endVisit(StructDefinition& _struct);
bool visit(FunctionDefinition& _function);
void endVisit(FunctionDefinition& _function);
void endVisit(VariableDefinition& _variableDefinition);
bool visit(VariableDeclaration& _declaration);
bool visit(ContractDefinition& _contract) override;
void endVisit(ContractDefinition& _contract) override;
bool visit(StructDefinition& _struct) override;
void endVisit(StructDefinition& _struct) override;
bool visit(FunctionDefinition& _function) override;
void endVisit(FunctionDefinition& _function) override;
bool visit(ModifierDefinition& _modifier) override;
void endVisit(ModifierDefinition& _modifier) override;
void endVisit(VariableDefinition& _variableDefinition) override;
bool visit(VariableDeclaration& _declaration) override;
bool visit(EventDefinition& _event) override;
void endVisit(EventDefinition& _event) override;
void enterNewSubScope(Declaration const& _declaration);
void closeCurrentScope();
@ -109,7 +113,7 @@ private:
std::map<ASTNode const*, DeclarationContainer>& m_scopes;
Declaration const* m_currentScope;
FunctionDefinition* m_currentFunction;
VariableScope* m_currentFunction;
};
/**

166
libsolidity/Parser.cpp

@ -121,6 +121,8 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
vector<ASTPointer<StructDefinition>> structs;
vector<ASTPointer<VariableDeclaration>> stateVariables;
vector<ASTPointer<FunctionDefinition>> functions;
vector<ASTPointer<ModifierDefinition>> modifiers;
vector<ASTPointer<EventDefinition>> events;
if (m_scanner->getCurrentToken() == Token::IS)
do
{
@ -148,23 +150,29 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING ||
Token::isElementaryTypeName(currentToken))
{
bool const allowVar = false;
stateVariables.push_back(parseVariableDeclaration(allowVar));
VarDeclParserOptions options;
options.isPublic = visibilityIsPublic;
options.isStateVariable = true;
stateVariables.push_back(parseVariableDeclaration(options));
expectToken(Token::SEMICOLON);
}
else if (currentToken == Token::MODIFIER)
modifiers.push_back(parseModifierDefinition());
else if (currentToken == Token::EVENT)
events.push_back(parseEventDefinition());
else
BOOST_THROW_EXCEPTION(createParserError("Function, variable or struct declaration expected."));
BOOST_THROW_EXCEPTION(createParserError("Function, variable, struct or modifier declaration expected."));
}
nodeFactory.markEndPosition();
expectToken(Token::RBRACE);
return nodeFactory.createNode<ContractDefinition>(name, docString, baseContracts, structs,
stateVariables, functions);
stateVariables, functions, modifiers, events);
}
ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<Identifier> name = ASTNodeFactory(*this).createNode<Identifier>(expectIdentifierToken());
ASTPointer<Identifier> name(parseIdentifier());
vector<ASTPointer<Expression>> arguments;
if (m_scanner->getCurrentToken() == Token::LPAREN)
{
@ -186,13 +194,25 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic, A
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::FUNCTION);
ASTPointer<ASTString> name(expectIdentifierToken());
ASTPointer<ASTString> name;
if (m_scanner->getCurrentToken() == Token::LPAREN)
name = make_shared<ASTString>(); // anonymous function
else
name = expectIdentifierToken();
ASTPointer<ParameterList> parameters(parseParameterList());
bool isDeclaredConst = false;
if (m_scanner->getCurrentToken() == Token::CONST)
vector<ASTPointer<ModifierInvocation>> modifiers;
while (true)
{
isDeclaredConst = true;
m_scanner->next();
if (m_scanner->getCurrentToken() == Token::CONST)
{
isDeclaredConst = true;
m_scanner->next();
}
else if (m_scanner->getCurrentToken() == Token::IDENTIFIER)
modifiers.push_back(parseModifierInvocation());
else
break;
}
ASTPointer<ParameterList> returnParameters;
if (m_scanner->getCurrentToken() == Token::RETURNS)
@ -202,18 +222,13 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic, A
returnParameters = parseParameterList(permitEmptyParameterList);
}
else
{
// create an empty parameter list at a zero-length location
ASTNodeFactory nodeFactory(*this);
nodeFactory.setLocationEmpty();
returnParameters = nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
}
returnParameters = createEmptyParameterList();
ASTPointer<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block);
bool const c_isConstructor = (_contractName && *name == *_contractName);
return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, c_isConstructor, docstring,
parameters,
isDeclaredConst, returnParameters, block);
parameters, isDeclaredConst, modifiers,
returnParameters, block);
}
ASTPointer<StructDefinition> Parser::parseStructDefinition()
@ -225,8 +240,7 @@ ASTPointer<StructDefinition> Parser::parseStructDefinition()
expectToken(Token::LBRACE);
while (m_scanner->getCurrentToken() != Token::RBRACE)
{
bool const allowVar = false;
members.push_back(parseVariableDeclaration(allowVar));
members.push_back(parseVariableDeclaration());
expectToken(Token::SEMICOLON);
}
nodeFactory.markEndPosition();
@ -234,12 +248,85 @@ ASTPointer<StructDefinition> Parser::parseStructDefinition()
return nodeFactory.createNode<StructDefinition>(name, members);
}
ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(bool _allowVar)
ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(VarDeclParserOptions const& _options)
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<TypeName> type = parseTypeName(_options.allowVar);
bool isIndexed = false;
if (_options.allowIndexed && m_scanner->getCurrentToken() == Token::INDEXED)
{
isIndexed = true;
m_scanner->next();
}
nodeFactory.markEndPosition();
return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken(),
_options.isPublic, _options.isStateVariable,
isIndexed);
}
ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
{
ScopeGuard resetModifierFlag([this]() { m_insideModifier = false; });
m_insideModifier = true;
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring;
if (m_scanner->getCurrentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::MODIFIER);
ASTPointer<ASTString> name(expectIdentifierToken());
ASTPointer<ParameterList> parameters;
if (m_scanner->getCurrentToken() == Token::LPAREN)
parameters = parseParameterList();
else
parameters = createEmptyParameterList();
ASTPointer<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block);
return nodeFactory.createNode<ModifierDefinition>(name, docstring, parameters, block);
}
ASTPointer<EventDefinition> Parser::parseEventDefinition()
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docstring;
if (m_scanner->getCurrentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->getCurrentCommentLiteral());
expectToken(Token::EVENT);
ASTPointer<ASTString> name(expectIdentifierToken());
ASTPointer<ParameterList> parameters;
if (m_scanner->getCurrentToken() == Token::LPAREN)
parameters = parseParameterList(true, true);
else
parameters = createEmptyParameterList();
nodeFactory.markEndPosition();
expectToken(Token::SEMICOLON);
return nodeFactory.createNode<EventDefinition>(name, docstring, parameters);
}
ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<Identifier> name(parseIdentifier());
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<ModifierInvocation>(name, arguments);
}
ASTPointer<Identifier> Parser::parseIdentifier()
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<TypeName> type = parseTypeName(_allowVar);
nodeFactory.markEndPosition();
return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken());
return nodeFactory.createNode<Identifier>(expectIdentifierToken());
}
ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
@ -290,19 +377,20 @@ ASTPointer<Mapping> Parser::parseMapping()
return nodeFactory.createNode<Mapping>(keyType, valueType);
}
ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty)
ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty, bool _allowIndexed)
{
ASTNodeFactory nodeFactory(*this);
vector<ASTPointer<VariableDeclaration>> parameters;
VarDeclParserOptions options;
options.allowIndexed = _allowIndexed;
expectToken(Token::LPAREN);
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN)
{
bool const allowVar = false;
parameters.push_back(parseVariableDeclaration(allowVar));
parameters.push_back(parseVariableDeclaration(options));
while (m_scanner->getCurrentToken() != Token::RPAREN)
{
expectToken(Token::COMMA);
parameters.push_back(parseVariableDeclaration(allowVar));
parameters.push_back(parseVariableDeclaration(options));
}
}
nodeFactory.markEndPosition();
@ -354,8 +442,16 @@ ASTPointer<Statement> Parser::parseStatement()
nodeFactory.setEndPositionFromNode(expression);
}
statement = nodeFactory.createNode<Return>(expression);
break;
}
break;
case Token::IDENTIFIER:
if (m_insideModifier && m_scanner->getCurrentLiteral() == "_")
{
statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>();
m_scanner->next();
return statement;
}
// fall-through
default:
statement = parseVarDefOrExprStmt();
}
@ -436,8 +532,9 @@ ASTPointer<Statement> Parser::parseVarDefOrExprStmt()
ASTPointer<VariableDefinition> Parser::parseVariableDefinition()
{
ASTNodeFactory nodeFactory(*this);
bool const allowVar = true;
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(allowVar);
VarDeclParserOptions options;
options.allowVar = true;
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options);
ASTPointer<Expression> value;
if (m_scanner->getCurrentToken() == Token::ASSIGN)
{
@ -521,8 +618,8 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression()
if (m_scanner->getCurrentToken() == Token::NEW)
{
expectToken(Token::NEW);
ASTPointer<Identifier> contractName = ASTNodeFactory(*this).createNode<Identifier>(expectIdentifierToken());
nodeFactory.markEndPosition();
ASTPointer<Identifier> contractName(parseIdentifier());
nodeFactory.setEndPositionFromNode(contractName);
expression = nodeFactory.createNode<NewExpression>(contractName);
}
else
@ -666,6 +763,13 @@ ASTPointer<ASTString> Parser::getLiteralAndAdvance()
return identifier;
}
ASTPointer<ParameterList> Parser::createEmptyParameterList()
{
ASTNodeFactory nodeFactory(*this);
nodeFactory.setLocationEmpty();
return nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
}
ParserError Parser::createParserError(string const& _description) const
{
return ParserError() << errinfo_sourceLocation(Location(getPosition(), getPosition(), getSourceName()))

21
libsolidity/Parser.h

@ -45,6 +45,14 @@ private:
/// End position of the current token
int getEndPosition() const;
struct VarDeclParserOptions {
VarDeclParserOptions() {}
bool allowVar = false;
bool isPublic = false;
bool isStateVariable = false;
bool allowIndexed = false;
};
///@{
///@name Parsing functions for the AST nodes
ASTPointer<ImportDirective> parseImportDirective();
@ -52,10 +60,14 @@ private:
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic, ASTString const* _contractName);
ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<VariableDeclaration> parseVariableDeclaration(bool _allowVar);
ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions());
ASTPointer<ModifierDefinition> parseModifierDefinition();
ASTPointer<EventDefinition> parseEventDefinition();
ASTPointer<ModifierInvocation> parseModifierInvocation();
ASTPointer<Identifier> parseIdentifier();
ASTPointer<TypeName> parseTypeName(bool _allowVar);
ASTPointer<Mapping> parseMapping();
ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true);
ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true, bool _allowIndexed = false);
ASTPointer<Block> parseBlock();
ASTPointer<Statement> parseStatement();
ASTPointer<IfStatement> parseIfStatement();
@ -85,11 +97,16 @@ private:
ASTPointer<ASTString> getLiteralAndAdvance();
///@}
/// Creates an empty ParameterList at the current location (used if parameters can be omitted).
ASTPointer<ParameterList> createEmptyParameterList();
/// Creates a @ref ParserError exception and annotates it with the current position and the
/// given @a _description.
ParserError createParserError(std::string const& _description) const;
std::shared_ptr<Scanner> m_scanner;
/// Flag that signifies whether '_' is parsed as a PlaceholderStatement or a regular identifier.
bool m_insideModifier = false;
};
}

3
libsolidity/Token.h

@ -153,12 +153,15 @@ namespace solidity
K(DEFAULT, "default", 0) \
K(DO, "do", 0) \
K(ELSE, "else", 0) \
K(EVENT, "event", 0) \
K(IS, "is", 0) \
K(INDEXED, "indexed", 0) \
K(FOR, "for", 0) \
K(FUNCTION, "function", 0) \
K(IF, "if", 0) \
K(IMPORT, "import", 0) \
K(MAPPING, "mapping", 0) \
K(MODIFIER, "modifier", 0) \
K(NEW, "new", 0) \
K(PUBLIC, "public", 0) \
K(PRIVATE, "private", 0) \

164
libsolidity/Types.cpp

@ -140,6 +140,11 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (_convertTo.getCategory() == Category::STRING)
{
StaticStringType const& convertTo = dynamic_cast<StaticStringType const&>(_convertTo);
return isHash() && (m_bits == convertTo.getNumBytes() * 8);
}
return _convertTo.getCategory() == getCategory() || _convertTo.getCategory() == Category::CONTRACT;
}
@ -367,6 +372,17 @@ bool StaticStringType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return convertTo.m_bytes >= m_bytes;
}
bool StaticStringType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (_convertTo.getCategory() == Category::INTEGER)
{
IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo);
if (convertTo.isHash() && (m_bytes * 8 == convertTo.getNumBits()))
return true;
}
return isImplicitlyConvertibleTo(_convertTo);
}
bool StaticStringType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
@ -434,7 +450,9 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
if (_convertTo.getCategory() == Category::CONTRACT)
{
auto const& bases = getContractDefinition().getLinearizedBaseContracts();
return find(bases.begin(), bases.end(),
if (m_super && bases.size() <= 1)
return false;
return find(m_super ? ++bases.begin() : bases.begin(), bases.end(),
&dynamic_cast<ContractType const&>(_convertTo).getContractDefinition()) != bases.end();
}
return false;
@ -456,12 +474,12 @@ bool ContractType::operator==(Type const& _other) const
if (_other.getCategory() != getCategory())
return false;
ContractType const& other = dynamic_cast<ContractType const&>(_other);
return other.m_contract == m_contract;
return other.m_contract == m_contract && other.m_super == m_super;
}
string ContractType::toString() const
{
return "contract " + m_contract.getName();
return "contract " + string(m_super ? "super " : "") + m_contract.getName();
}
MemberList const& ContractType::getMembers() const
@ -472,8 +490,16 @@ MemberList const& ContractType::getMembers() const
// All address members and all interface functions
map<string, shared_ptr<Type const>> members(IntegerType::AddressMemberList.begin(),
IntegerType::AddressMemberList.end());
for (auto const& it: m_contract.getInterfaceFunctions())
members[it.second->getName()] = make_shared<FunctionType>(*it.second, false);
if (m_super)
{
for (ContractDefinition const* base: m_contract.getLinearizedBaseContracts())
for (ASTPointer<FunctionDefinition> const& function: base->getDefinedFunctions())
if (!function->isConstructor() && !function->getName().empty())
members.insert(make_pair(function->getName(), make_shared<FunctionType>(*function, true)));
}
else
for (auto const& it: m_contract.getInterfaceFunctions())
members[it.second->getDeclaration().getName()] = it.second;
m_members.reset(new MemberList(members));
}
return *m_members;
@ -495,9 +521,9 @@ shared_ptr<FunctionType const> const& ContractType::getConstructorType() const
u256 ContractType::getFunctionIdentifier(string const& _functionName) const
{
auto interfaceFunctions = m_contract.getInterfaceFunctions();
for (auto it = interfaceFunctions.cbegin(); it != interfaceFunctions.cend(); ++it)
if (it->second->getName() == _functionName)
return FixedHash<4>::Arith(it->first);
for (auto const& it: m_contract.getInterfaceFunctions())
if (it.second->getDeclaration().getName() == _functionName)
return FixedHash<4>::Arith(it.first);
return Invalid256;
}
@ -563,18 +589,64 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const
}
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL)
m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL),
m_isConstant(_function.isDeclaredConst()),
m_declaration(&_function)
{
TypePointers params;
vector<string> paramNames;
TypePointers retParams;
vector<string> retParamNames;
params.reserve(_function.getParameters().size());
paramNames.reserve(_function.getParameters().size());
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
{
paramNames.push_back(var->getName());
params.push_back(var->getType());
}
retParams.reserve(_function.getReturnParameters().size());
retParamNames.reserve(_function.getReturnParameters().size());
for (ASTPointer<VariableDeclaration> const& var: _function.getReturnParameters())
{
retParamNames.push_back(var->getName());
retParams.push_back(var->getType());
}
swap(params, m_parameterTypes);
swap(paramNames, m_parameterNames);
swap(retParams, m_returnParameterTypes);
swap(retParamNames, m_returnParameterNames);
}
FunctionType::FunctionType(VariableDeclaration const& _varDecl):
m_location(Location::EXTERNAL), m_isConstant(true), m_declaration(&_varDecl)
{
TypePointers params({});
vector<string> paramNames({});
TypePointers retParams({_varDecl.getType()});
vector<string> retParamNames({ _varDecl.getName()});
// for now, no input parameters LTODO: change for some things like mapping
swap(params, m_parameterTypes);
swap(paramNames, m_parameterNames);
swap(retParams, m_returnParameterTypes);
swap(retParamNames, m_returnParameterNames);
}
FunctionType::FunctionType(const EventDefinition& _event):
m_location(Location::EVENT), m_declaration(&_event)
{
TypePointers params;
vector<string> paramNames;
params.reserve(_event.getParameters().size());
paramNames.reserve(_event.getParameters().size());
for (ASTPointer<VariableDeclaration> const& var: _event.getParameters())
{
paramNames.push_back(var->getName());
params.push_back(var->getType());
}
swap(params, m_parameterTypes);
swap(paramNames, m_parameterNames);
}
bool FunctionType::operator==(Type const& _other) const
@ -585,6 +657,9 @@ bool FunctionType::operator==(Type const& _other) const
if (m_location != other.m_location)
return false;
if (m_isConstant != other.isConstant())
return false;
if (m_parameterTypes.size() != other.m_parameterTypes.size() ||
m_returnParameterTypes.size() != other.m_returnParameterTypes.size())
return false;
@ -656,9 +731,15 @@ MemberList const& FunctionType::getMembers() const
}
}
string FunctionType::getCanonicalSignature() const
string FunctionType::getCanonicalSignature(std::string const& _name) const
{
string ret = "(";
std::string funcName = _name;
if (_name == "")
{
solAssert(m_declaration != nullptr, "Function type without name needs a declaration");
funcName = m_declaration->getName();
}
string ret = funcName + "(";
for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it)
ret += (*it)->toString() + (it + 1 == m_parameterTypes.cend() ? "" : ",");
@ -681,6 +762,33 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con
m_gasSet || _setGas, m_valueSet || _setValue);
}
vector<string> const FunctionType::getParameterTypeNames() const
{
vector<string> names;
for (TypePointer const& t: m_parameterTypes)
names.push_back(t->toString());
return names;
}
vector<string> const FunctionType::getReturnParameterTypeNames() const
{
vector<string> names;
for (TypePointer const& t: m_returnParameterTypes)
names.push_back(t->toString());
return names;
}
ASTPointer<ASTString> FunctionType::getDocumentation() const
{
auto function = dynamic_cast<Documented const*>(m_declaration);
if (function)
return function->getDocumentation();
return ASTPointer<ASTString>();
}
bool MappingType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
@ -716,7 +824,7 @@ MemberList const& TypeType::getMembers() const
// 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())
if (!f->isConstructor() && !f->getName().empty())
members[f->getName()] = make_shared<FunctionType>(*f);
}
m_members.reset(new MemberList(members));
@ -724,6 +832,38 @@ MemberList const& TypeType::getMembers() const
return *m_members;
}
ModifierType::ModifierType(const ModifierDefinition& _modifier)
{
TypePointers params;
params.reserve(_modifier.getParameters().size());
for (ASTPointer<VariableDeclaration> const& var: _modifier.getParameters())
params.push_back(var->getType());
swap(params, m_parameterTypes);
}
bool ModifierType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
return false;
ModifierType const& other = dynamic_cast<ModifierType const&>(_other);
if (m_parameterTypes.size() != other.m_parameterTypes.size())
return false;
auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; };
if (!equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(),
other.m_parameterTypes.cbegin(), typeCompare))
return false;
return true;
}
string ModifierType::toString() const
{
string name = "modifier (";
for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it)
name += (*it)->toString() + (it + 1 == m_parameterTypes.end() ? "" : ",");
return name + ")";
}
MagicType::MagicType(MagicType::Kind _kind):
m_kind(_kind)

74
libsolidity/Types.h

@ -41,6 +41,7 @@ namespace solidity
class Type; // forward
class FunctionType; // forward
using TypePointer = std::shared_ptr<Type const>;
using FunctionTypePointer = std::shared_ptr<FunctionType const>;
using TypePointers = std::vector<TypePointer>;
/**
@ -75,7 +76,7 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this<Type
public:
enum class Category
{
INTEGER, INTEGER_CONSTANT, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE, MAGIC
INTEGER, INTEGER_CONSTANT, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE, MODIFIER, MAGIC
};
///@{
@ -233,9 +234,10 @@ public:
/// if no type fits.
static std::shared_ptr<StaticStringType> smallestTypeForLiteral(std::string const& _literal);
StaticStringType(int _bytes);
explicit StaticStringType(int _bytes);
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool operator==(Type const& _other) const override;
virtual unsigned getCalldataEncodedSize() const override { return m_bytes; }
@ -276,7 +278,8 @@ class ContractType: public Type
{
public:
virtual Category getCategory() const override { return Category::CONTRACT; }
ContractType(ContractDefinition const& _contract): m_contract(_contract) {}
explicit ContractType(ContractDefinition const& _contract, bool _super = false):
m_contract(_contract), m_super(_super) {}
/// Contracts can be implicitly converted to super classes and to addresses.
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
/// Contracts can be converted to themselves and to integers.
@ -288,11 +291,12 @@ public:
virtual MemberList const& getMembers() const override;
bool isSuper() const { return m_super; }
ContractDefinition const& getContractDefinition() const { return m_contract; }
/// Returns the function type of the constructor. Note that the location part of the function type
/// is not used, as this type cannot be the type of a variable or expression.
std::shared_ptr<FunctionType const> const& getConstructorType() const;
FunctionTypePointer const& getConstructorType() const;
/// @returns the identifier of the function with the given name or Invalid256 if such a name does
/// not exist.
@ -300,8 +304,11 @@ public:
private:
ContractDefinition const& m_contract;
/// If true, it is the "super" type of the current contract, i.e. it contains only inherited
/// members.
bool m_super;
/// Type of the constructor, @see getConstructorType. Lazily initialized.
mutable std::shared_ptr<FunctionType const> m_constructorType;
mutable FunctionTypePointer m_constructorType;
/// List of member types, will be lazy-initialized because of recursive references.
mutable std::unique_ptr<MemberList> m_members;
};
@ -313,7 +320,7 @@ class StructType: public Type
{
public:
virtual Category getCategory() const override { return Category::STRUCT; }
StructType(StructDefinition const& _struct): m_struct(_struct) {}
explicit StructType(StructDefinition const& _struct): m_struct(_struct) {}
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const override;
@ -343,15 +350,18 @@ public:
/// INTERNAL: jump tag, EXTERNAL: contract address + function identifier,
/// BARE: contract address (non-abi contract call)
/// OTHERS: special virtual function, nothing on the stack
/// @todo This documentation is outdated, and Location should rather be named "Type"
enum class Location { INTERNAL, EXTERNAL, CREATION, SEND,
SHA3, SUICIDE,
ECRECOVER, SHA256, RIPEMD160,
LOG0, LOG1, LOG2, LOG3, LOG4,
LOG0, LOG1, LOG2, LOG3, LOG4, EVENT,
SET_GAS, SET_VALUE, BLOCKHASH,
BARE };
virtual Category getCategory() const override { return Category::FUNCTION; }
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
explicit FunctionType(VariableDeclaration const& _varDecl);
explicit FunctionType(EventDefinition const& _event);
FunctionType(strings const& _parameterTypes, strings const& _returnParameterTypes,
Location _location = Location::INTERNAL):
FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes),
@ -363,7 +373,11 @@ public:
m_location(_location), m_gasSet(_gasSet), m_valueSet(_valueSet) {}
TypePointers const& getParameterTypes() const { return m_parameterTypes; }
std::vector<std::string> const& getParameterNames() const { return m_parameterNames; }
std::vector<std::string> const getParameterTypeNames() const;
TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; }
std::vector<std::string> const& getReturnParameterNames() const { return m_returnParameterNames; }
std::vector<std::string> const getReturnParameterTypeNames() const;
virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override;
@ -374,7 +388,20 @@ public:
virtual MemberList const& getMembers() const override;
Location const& getLocation() const { return m_location; }
std::string getCanonicalSignature() const;
/// @returns the canonical signature of this function type given the function name
/// If @a _name is not provided (empty string) then the @c m_declaration member of the
/// function type is used
std::string getCanonicalSignature(std::string const& _name = "") const;
Declaration const& getDeclaration() const
{
solAssert(m_declaration, "Requested declaration from a FunctionType that has none");
return *m_declaration;
}
bool hasDeclaration() const { return !!m_declaration; }
bool isConstant() const { return m_isConstant; }
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> getDocumentation() const;
bool gasSet() const { return m_gasSet; }
bool valueSet() const { return m_valueSet; }
@ -388,10 +415,14 @@ private:
TypePointers m_parameterTypes;
TypePointers m_returnParameterTypes;
std::vector<std::string> m_parameterNames;
std::vector<std::string> m_returnParameterNames;
Location const m_location;
bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack
bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
bool m_isConstant;
mutable std::unique_ptr<MemberList> m_members;
Declaration const* m_declaration = nullptr;
};
/**
@ -442,7 +473,7 @@ class TypeType: public Type
{
public:
virtual Category getCategory() const override { return Category::TYPE; }
TypeType(TypePointer const& _actualType, ContractDefinition const* _currentContract = nullptr):
explicit TypeType(TypePointer const& _actualType, ContractDefinition const* _currentContract = nullptr):
m_actualType(_actualType), m_currentContract(_currentContract) {}
TypePointer const& getActualType() const { return m_actualType; }
@ -451,6 +482,7 @@ public:
virtual bool canBeStored() const override { return false; }
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 unsigned getSizeOnStack() const override { return 0; }
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
virtual MemberList const& getMembers() const override;
@ -463,6 +495,28 @@ private:
};
/**
* The type of a function modifier. Not used for anything for now.
*/
class ModifierType: public Type
{
public:
virtual Category getCategory() const override { return Category::MODIFIER; }
explicit ModifierType(ModifierDefinition const& _modifier);
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
virtual bool canBeStored() const override { return false; }
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 unsigned getSizeOnStack() const override { return 0; }
virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override;
private:
TypePointers m_parameterTypes;
};
/**
* Special type for magic variables (block, msg, tx), similar to a struct but without any reference
* (it always references a global singleton by name).
@ -473,7 +527,7 @@ public:
enum class Kind { BLOCK, MSG, TX };
virtual Category getCategory() const override { return Category::MAGIC; }
MagicType(Kind _kind);
explicit MagicType(Kind _kind);
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
{

6
libsolidity/grammar.txt

@ -1,14 +1,14 @@
ContractDefinition = 'contract' Identifier
( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )?
'{' ContractPart* '}'
ContractPart = VariableDeclaration ';' | StructDefinition |
ContractPart = VariableDeclaration ';' | StructDefinition | ModifierDefinition |
FunctionDefinition | 'public:' | 'private:'
InheritanceSpecifier = Identifier ( '(' Expression ( ',' Expression )* ')' )?
StructDefinition = 'struct' Identifier '{'
( VariableDeclaration (';' VariableDeclaration)* )? '}
FunctionDefinition = 'function' Identifier ParameterList 'const'?
ModifierDefinition = 'modifier' Identifier ParameterList? Block
FunctionDefinition = 'function' Identifier ParameterList ( Identifier | 'constant' )*
( 'returns' ParameterList )? Block
ParameterList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')'
// semantic restriction: mappings and structs (recursively) containing mappings

46
libweb3jsonrpc/CorsHttpServer.cpp

@ -1,46 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file CorsHttpServer.cpp
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2014
*/
#include "CorsHttpServer.h"
namespace jsonrpc
{
int HttpServer::callback(struct mg_connection*)
{
return 0;
}
bool CorsHttpServer::SendResponse(std::string const& _response, void* _addInfo)
{
struct mg_connection* conn = (struct mg_connection*) _addInfo;
if (mg_printf(conn, "HTTP/1.1 200 OK\r\n"
"Content-Type: application/json\r\n"
"Content-Length: %d\r\n"
"Access-Control-Allow-Origin: *\r\n"
"Access-Control-Allow-Headers: Content-Type\r\n"
"\r\n"
"%s",(int)_response.length(), _response.c_str()) > 0)
return true;
return false;
}
}

35
libweb3jsonrpc/CorsHttpServer.h

@ -1,35 +0,0 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file CorsHttpServer.h
* @author Marek Kotewicz <marek@ethdev.com>
* @date 2014
*/
#include <jsonrpccpp/server/connectors/httpserver.h>
namespace jsonrpc
{
class CorsHttpServer: public HttpServer
{
public:
using HttpServer::HttpServer;
bool virtual SendResponse(std::string const& _response, void* _addInfo = NULL);
};
}

38
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -62,7 +62,7 @@ static Json::Value toJson(dev::eth::Transaction const& _t)
res["hash"] = toJS(_t.sha3());
res["input"] = jsFromBinary(_t.data());
res["to"] = toJS(_t.receiveAddress());
res["from"] = toJS(_t.sender());
res["from"] = toJS(_t.safeSender());
res["gas"] = (int)_t.gas();
res["gasPrice"] = toJS(_t.gasPrice());
res["nonce"] = toJS(_t.nonce());
@ -77,7 +77,7 @@ static Json::Value toJson(dev::eth::LocalisedLogEntry const& _e)
res["data"] = jsFromBinary(_e.data);
res["address"] = toJS(_e.address);
for (auto const& t: _e.topics)
res["topics"].append(toJS(t));
res["topic"].append(toJS(t));
res["number"] = _e.number;
return res;
}
@ -123,16 +123,18 @@ static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to
else if (_json["address"].isString())
filter.address(jsToAddress(_json["address"].asString()));
}
if (!_json["topics"].empty())
if (!_json["topic"].empty() && _json["topic"].isArray())
{
if (_json["topics"].isArray())
unsigned i = 0;
for (auto t: _json["topic"])
{
for (auto i: _json["topics"])
if (i.isString())
filter.topic(jsToU256(i.asString()));
if (t.isArray())
for (auto tt: t)
filter.topic(i, jsToFixed<32>(tt.asString()));
else if (t.isString())
filter.topic(i, jsToFixed<32>(t.asString()));
i++;
}
else if(_json["topics"].isString())
filter.topic(jsToU256(_json["topics"].asString()));
}
return filter;
}
@ -171,9 +173,9 @@ static shh::Envelope toSealed(Json::Value const& _json, shh::Message const& _m,
return _m.seal(_from, bt, ttl, workToProve);
}
static pair<shh::TopicMask, Public> toWatch(Json::Value const& _json)
static pair<shh::FullTopic, Public> toWatch(Json::Value const& _json)
{
shh::BuildTopicMask bt;
shh::BuildTopic bt;
Public to;
if (_json["to"].isString())
@ -188,7 +190,7 @@ static pair<shh::TopicMask, Public> toWatch(Json::Value const& _json)
if (i.isString())
bt.shift(jsToBytes(i.asString()));
}
return make_pair(bt.toTopicMask(), to);
return make_pair(bt, to);
}
static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message const& _m)
@ -199,8 +201,8 @@ static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message
res["sent"] = (int)_e.sent();
res["ttl"] = (int)_e.ttl();
res["workProved"] = (int)_e.workProved();
for (auto const& t: _e.topics())
res["topics"].append(toJS(t));
for (auto const& t: _e.topic())
res["topic"].append(toJS(t));
res["payload"] = toJS(_m.payload());
res["from"] = toJS(_m.from());
res["to"] = toJS(_m.to());
@ -574,12 +576,12 @@ Json::Value WebThreeStubServerBase::shh_changed(int const& _id)
if (pub)
{
cwarn << "Silently decrypting message from identity" << pub.abridged() << ": User validation hook goes here.";
m = e.open(m_ids[pub]);
if (!m)
continue;
m = e.open(face()->fullTopic(_id), m_ids[pub]);
}
else
m = e.open();
m = e.open(face()->fullTopic(_id));
if (!m)
continue;
ret.append(toJson(h, e, m));
}

24
libwhisper/Common.cpp

@ -28,12 +28,26 @@ using namespace dev;
using namespace dev::p2p;
using namespace dev::shh;
Topic BuildTopic::toTopic() const
CollapsedTopicPart dev::shh::collapse(FullTopicPart const& _p)
{
Topic ret;
return CollapsedTopicPart(sha3(_p));
}
CollapsedTopic dev::shh::collapse(FullTopic const& _fullTopic)
{
CollapsedTopic ret;
ret.reserve(_fullTopic.size());
for (auto const& ft: _fullTopic)
ret.push_back(collapse(ft));
return ret;
}
CollapsedTopic BuildTopic::toTopic() const
{
CollapsedTopic ret;
ret.reserve(m_parts.size());
for (auto const& h: m_parts)
ret.push_back(TopicPart(h));
ret.push_back(collapse(h));
return ret;
}
@ -56,7 +70,7 @@ bool TopicFilter::matches(Envelope const& _e) const
{
for (unsigned i = 0; i < t.size(); ++i)
{
for (auto et: _e.topics())
for (auto et: _e.topic())
if (((t[i].first ^ et) & t[i].second) == 0)
goto NEXT_TOPICPART;
// failed to match topicmask against any topics: move on to next mask
@ -75,7 +89,7 @@ TopicMask BuildTopicMask::toTopicMask() const
TopicMask ret;
ret.reserve(m_parts.size());
for (auto const& h: m_parts)
ret.push_back(make_pair(TopicPart(h), ~TopicPart()));
ret.push_back(make_pair(collapse(h), ~CollapsedTopicPart()));
return ret;
}

20
libwhisper/Common.h

@ -59,9 +59,14 @@ enum WhisperPacket
PacketCount
};
using TopicPart = FixedHash<4>;
using CollapsedTopicPart = FixedHash<4>;
using FullTopicPart = h256;
using Topic = std::vector<TopicPart>;
using CollapsedTopic = std::vector<CollapsedTopicPart>;
using FullTopic = h256s;
CollapsedTopicPart collapse(FullTopicPart const& _fullTopicPart);
CollapsedTopic collapse(FullTopic const& _fullTopic);
class BuildTopic
{
@ -74,8 +79,10 @@ public:
BuildTopic& shiftRaw(h256 const& _part) { m_parts.push_back(_part); return *this; }
operator Topic() const { return toTopic(); }
Topic toTopic() const;
operator CollapsedTopic() const { return toTopic(); }
operator FullTopic() const { return toFullTopic(); }
CollapsedTopic toTopic() const;
FullTopic toFullTopic() const { return m_parts; }
protected:
BuildTopic& shiftBytes(bytes const& _b);
@ -83,13 +90,14 @@ protected:
h256s m_parts;
};
using TopicMask = std::vector<std::pair<TopicPart, TopicPart>>;
using TopicMask = std::vector<std::pair<CollapsedTopicPart, CollapsedTopicPart>>;
using TopicMasks = std::vector<TopicMask>;
class TopicFilter
{
public:
TopicFilter() {}
TopicFilter(FullTopic const& _m) { m_topicMasks.push_back(TopicMask()); for (auto const& h: _m) m_topicMasks.back().push_back(std::make_pair(collapse(h), h ? ~CollapsedTopicPart() : CollapsedTopicPart())); }
TopicFilter(TopicMask const& _m): m_topicMasks(1, _m) {}
TopicFilter(TopicMasks const& _m): m_topicMasks(_m) {}
TopicFilter(RLP const& _r)//: m_topicMasks(_r.toVector<std::vector<>>())
@ -123,7 +131,9 @@ public:
template <class T> BuildTopicMask& operator()(T const& _t) { shift(_t); return *this; }
operator TopicMask() const { return toTopicMask(); }
operator FullTopic() const { return toFullTopic(); }
TopicMask toTopicMask() const;
FullTopic toFullTopic() const { return m_parts; }
};
}

3
libwhisper/Interface.cpp

@ -34,7 +34,6 @@ using namespace dev::shh;
#endif
#define clogS(X) dev::LogOutputStream<X, true>(false) << "| " << std::setw(2) << session()->socketId() << "] "
unsigned Interface::installWatch(TopicMask const& _mask)
Interface::~Interface()
{
return installWatch(TopicFilter(_mask));
}

20
libwhisper/Interface.h

@ -47,8 +47,9 @@ class Watch;
struct InstalledFilter
{
InstalledFilter(TopicFilter const& _f): filter(_f) {}
InstalledFilter(FullTopic const& _f): full(_f), filter(_f) {}
FullTopic full;
TopicFilter filter;
unsigned refCount = 1;
};
@ -65,12 +66,12 @@ struct ClientWatch
class Interface
{
public:
virtual ~Interface() {}
virtual ~Interface();
virtual void inject(Envelope const& _m, WhisperPeer* _from = nullptr) = 0;
unsigned installWatch(TopicMask const& _mask);
virtual unsigned installWatch(TopicFilter const& _filter) = 0;
virtual FullTopic const& fullTopic(unsigned _id) const = 0;
virtual unsigned installWatch(FullTopic const& _mask) = 0;
virtual unsigned installWatchOnId(h256 _filterId) = 0;
virtual void uninstallWatch(unsigned _watchId) = 0;
virtual h256s peekWatch(unsigned _watchId) const = 0;
@ -79,10 +80,10 @@ public:
virtual Envelope envelope(h256 _m) const = 0;
void post(bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_topic, _ttl, _workToProve)); }
void post(Public _to, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_to, _topic, _ttl, _workToProve)); }
void post(Secret _from, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_from, _topic, _ttl, _workToProve)); }
void post(Secret _from, Public _to, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_from, _to, _topic, _ttl, _workToProve)); }
void post(bytes const& _payload, FullTopic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_topic, _ttl, _workToProve)); }
void post(Public _to, bytes const& _payload, FullTopic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_to, _topic, _ttl, _workToProve)); }
void post(Secret _from, bytes const& _payload, FullTopic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_from, _topic, _ttl, _workToProve)); }
void post(Secret _from, Public _to, bytes const& _payload, FullTopic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).sealTo(_from, _to, _topic, _ttl, _workToProve)); }
};
struct WatshhChannel: public dev::LogChannel { static const char* name() { return "shh"; } static const int verbosity = 1; };
@ -104,8 +105,7 @@ class Watch: public boost::noncopyable
public:
Watch() {}
Watch(Interface& _c, TopicMask const& _f): m_c(&_c), m_id(_c.installWatch(_f)) {}
Watch(Interface& _c, TopicFilter const& _tf): m_c(&_c), m_id(_c.installWatch(_tf)) {}
Watch(Interface& _c, FullTopic const& _f): m_c(&_c), m_id(_c.installWatch(_f)) {}
~Watch() { if (m_c) m_c->uninstallWatch(m_id); }
h256s check() { return m_c ? m_c->checkWatch(m_id) : h256s(); }

66
libwhisper/Message.cpp

@ -26,7 +26,7 @@ using namespace dev;
using namespace dev::p2p;
using namespace dev::shh;
Message::Message(Envelope const& _e, Secret const& _s)
Message::Message(Envelope const& _e, FullTopic const& _fk, Secret const& _s)
{
try
{
@ -34,8 +34,39 @@ Message::Message(Envelope const& _e, Secret const& _s)
if (_s)
if (!decrypt(_s, &(_e.data()), b))
return;
if (populate(_s ? b : _e.data()))
m_to = KeyPair(_s).pub();
else{}
else
{
// public - need to get the key through combining with the topic/topicIndex we know.
unsigned topicIndex = 0;
Secret topicSecret;
// determine topicSecret/topicIndex from knowledge of the collapsed topics (which give the order) and our full-size filter topic.
CollapsedTopic knownTopic = collapse(_fk);
for (unsigned ti = 0; ti < _fk.size() && !topicSecret; ++ti)
for (unsigned i = 0; i < _e.topic().size(); ++i)
if (_e.topic()[i] == knownTopic[ti])
{
topicSecret = _fk[ti];
topicIndex = i;
break;
}
if (_e.data().size() < _e.topic().size() * 32)
return;
// get key from decrypted topic key: just xor
h256 tk = h256(bytesConstRef(&(_e.data())).cropped(32 * topicIndex, 32));
bytesConstRef cipherText = bytesConstRef(&(_e.data())).cropped(32 * _e.topic().size());
cnote << "Decrypting(" << topicIndex << "): " << topicSecret << tk << (topicSecret ^ tk) << toHex(cipherText);
if (!decryptSym(topicSecret ^ tk, cipherText, b))
return;
cnote << "Got: " << toHex(b);
}
if (populate(b))
if (_s)
m_to = KeyPair(_s).pub();
}
catch (...) // Invalid secret? TODO: replace ... with InvalidSecret
{
@ -63,9 +94,10 @@ bool Message::populate(bytes const& _data)
return true;
}
Envelope Message::seal(Secret _from, Topic const& _topic, unsigned _ttl, unsigned _workToProve) const
Envelope Message::seal(Secret _from, FullTopic const& _fullTopic, unsigned _ttl, unsigned _workToProve) const
{
Envelope ret(time(0) + _ttl, _ttl, _topic);
CollapsedTopic topic = collapse(_fullTopic);
Envelope ret(time(0) + _ttl, _ttl, topic);
bytes input(1 + m_payload.size());
input[0] = 0;
@ -83,7 +115,25 @@ Envelope Message::seal(Secret _from, Topic const& _topic, unsigned _ttl, unsigne
if (m_to)
encrypt(m_to, &input, ret.m_data);
else
swap(ret.m_data, input);
{
// create the shared secret and encrypt
Secret s = Secret::random();
for (h256 const& t: _fullTopic)
ret.m_data += (t ^ s).asBytes();
bytes d;
encryptSym(s, &input, d);
ret.m_data += d;
for (unsigned i = 0; i < _fullTopic.size(); ++i)
{
bytes b;
h256 tk = h256(bytesConstRef(&(ret.m_data)).cropped(32 * i, 32));
bytesConstRef cipherText = bytesConstRef(&(ret.m_data)).cropped(32 * ret.topic().size());
cnote << "Test decrypting(" << i << "): " << _fullTopic[i] << tk << (_fullTopic[i] ^ tk) << toHex(cipherText);
assert(decryptSym(_fullTopic[i] ^ tk, cipherText, b));
cnote << "Got: " << toHex(b);
}
}
ret.proveWork(_workToProve);
return ret;
@ -98,9 +148,9 @@ Envelope::Envelope(RLP const& _m)
m_nonce = _m[4].toInt<u256>();
}
Message Envelope::open(Secret const& _s) const
Message Envelope::open(FullTopic const& _ft, Secret const& _s) const
{
return Message(*this, _s);
return Message(*this, _ft, _s);
}
unsigned Envelope::workProved() const

28
libwhisper/Message.h

@ -39,6 +39,16 @@ namespace shh
class Message;
static const unsigned Undefined = (unsigned)-1;
struct FilterKey
{
FilterKey() {}
FilterKey(unsigned _tI, Secret const& _k): topicIndex(_tI), key(_k) {}
unsigned topicIndex = Undefined;
Secret key;
};
enum IncludeNonce
{
WithoutNonce = 0,
@ -61,22 +71,22 @@ public:
unsigned sent() const { return m_expiry - m_ttl; }
unsigned expiry() const { return m_expiry; }
unsigned ttl() const { return m_ttl; }
Topic const& topics() const { return m_topic; }
CollapsedTopic const& topic() const { return m_topic; }
bytes const& data() const { return m_data; }
Message open(Secret const& _s = Secret()) const;
Message open(FullTopic const& _ft, Secret const& _s = Secret()) const;
unsigned workProved() const;
void proveWork(unsigned _ms);
private:
Envelope(unsigned _exp, unsigned _ttl, Topic const& _topic): m_expiry(_exp), m_ttl(_ttl), m_topic(_topic) {}
Envelope(unsigned _exp, unsigned _ttl, CollapsedTopic const& _topic): m_expiry(_exp), m_ttl(_ttl), m_topic(_topic) {}
unsigned m_expiry = 0;
unsigned m_ttl = 0;
u256 m_nonce;
Topic m_topic;
CollapsedTopic m_topic;
bytes m_data;
};
@ -91,7 +101,7 @@ class Message
{
public:
Message() {}
Message(Envelope const& _e, Secret const& _s = Secret());
Message(Envelope const& _e, FullTopic const& _ft, Secret const& _s = Secret());
Message(bytes const& _payload): m_payload(_payload) {}
Message(bytesConstRef _payload): m_payload(_payload.toBytes()) {}
Message(bytes&& _payload) { std::swap(_payload, m_payload); }
@ -108,11 +118,11 @@ public:
operator bool() const { return !!m_payload.size() || m_from || m_to; }
/// Turn this message into a ditributable Envelope.
Envelope seal(Secret _from, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) const;
Envelope seal(Secret _from, FullTopic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) const;
// Overloads for skipping _from or specifying _to.
Envelope seal(Topic const& _topic, unsigned _ttl = 50, unsigned _workToProve = 50) const { return seal(Secret(), _topic, _workToProve, _ttl); }
Envelope sealTo(Public _to, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(Secret(), _topic, _workToProve, _ttl); }
Envelope sealTo(Secret _from, Public _to, Topic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(_from, _topic, _workToProve, _ttl); }
Envelope seal(FullTopic const& _topic, unsigned _ttl = 50, unsigned _workToProve = 50) const { return seal(Secret(), _topic, _workToProve, _ttl); }
Envelope sealTo(Public _to, FullTopic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(Secret(), _topic, _workToProve, _ttl); }
Envelope sealTo(Secret _from, Public _to, FullTopic const& _topic, unsigned _workToProve = 50, unsigned _ttl = 50) { m_to = _to; return seal(_from, _topic, _workToProve, _ttl); }
private:
bool populate(bytes const& _data);

11
libwhisper/WhisperHost.cpp

@ -49,14 +49,14 @@ void WhisperHost::streamMessage(h256 _m, RLPStream& _s) const
{
UpgradeGuard ll(l);
auto const& m = m_messages.at(_m);
cnote << "streamRLP: " << m.expiry() << m.ttl() << m.topics() << toHex(m.data());
cnote << "streamRLP: " << m.expiry() << m.ttl() << m.topic() << toHex(m.data());
m.streamRLP(_s);
}
}
void WhisperHost::inject(Envelope const& _m, WhisperPeer* _p)
{
cnote << "inject: " << _m.expiry() << _m.ttl() << _m.topics() << toHex(_m.data());
cnote << this << ": inject: " << _m.expiry() << _m.ttl() << _m.topic() << toHex(_m.data());
if (_m.expiry() <= time(0))
return;
@ -104,14 +104,15 @@ unsigned WhisperHost::installWatchOnId(h256 _h)
return ret;
}
unsigned WhisperHost::installWatch(shh::TopicFilter const& _f)
unsigned WhisperHost::installWatch(shh::FullTopic const& _ft)
{
Guard l(m_filterLock);
h256 h = _f.sha3();
InstalledFilter f(_ft);
h256 h = f.filter.sha3();
if (!m_filters.count(h))
m_filters.insert(make_pair(h, _f));
m_filters.insert(make_pair(h, f));
return installWatchOnId(h);
}

8
libwhisper/WhisperHost.h

@ -39,6 +39,8 @@ namespace dev
namespace shh
{
static const FullTopic EmptyFullTopic;
class WhisperHost: public HostCapability<WhisperPeer>, public Interface, public Worker
{
friend class WhisperPeer;
@ -47,12 +49,12 @@ public:
WhisperHost();
virtual ~WhisperHost();
unsigned protocolVersion() const { return 1; }
unsigned protocolVersion() const { return 2; }
virtual void inject(Envelope const& _e, WhisperPeer* _from = nullptr) override;
using Interface::installWatch;
virtual unsigned installWatch(TopicFilter const& _filter) override;
virtual FullTopic const& fullTopic(unsigned _id) const { try { return m_filters.at(m_watches.at(_id).id).full; } catch (...) { return EmptyFullTopic; } }
virtual unsigned installWatch(FullTopic const& _filter) override;
virtual unsigned installWatchOnId(h256 _filterId) override;
virtual void uninstallWatch(unsigned _watchId) override;
virtual h256s peekWatch(unsigned _watchId) const override { dev::Guard l(m_filterLock); try { return m_watches.at(_watchId).changes; } catch (...) { return h256s(); } }

5
libwhisper/WhisperPeer.cpp

@ -62,6 +62,9 @@ bool WhisperPeer::interpret(unsigned _id, RLP const& _r)
if (protocolVersion != version())
disable("Invalid protocol version.");
for (auto const& m: host()->all())
m_unseen.insert(make_pair(0, m.first));
if (session()->id() < host()->host()->id())
sendMessages();
break;
@ -103,7 +106,7 @@ void WhisperPeer::sendMessages()
}
}
void WhisperPeer::noteNewMessage(h256 _h, Message const& _m)
void WhisperPeer::noteNewMessage(h256 _h, Envelope const& _m)
{
Guard l(x_unseen);
m_unseen.insert(make_pair(rating(_m), _h));

4
libwhisper/WhisperPeer.h

@ -63,8 +63,8 @@ private:
void sendMessages();
unsigned rating(Message const&) const { return 0; } // TODO
void noteNewMessage(h256 _h, Message const& _m);
unsigned rating(Envelope const&) const { return 0; } // TODO
void noteNewMessage(h256 _h, Envelope const& _m);
mutable dev::Mutex x_unseen;
std::multimap<unsigned, h256> m_unseen; ///< Rated according to what they want.

10
mix/AppContext.cpp

@ -23,9 +23,11 @@
*/
#include <QMessageBox>
#include <QClipboard>
#include <QQmlComponent>
#include <QQmlContext>
#include <QQmlApplicationEngine>
#include <QQuickWindow>
#include "CodeModel.h"
#include "FileIo.h"
#include "ClientModel.h"
@ -74,6 +76,8 @@ void AppContext::load()
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
qmlRegisterType<HttpServer>("HttpServer", 1, 0, "HttpServer");
m_applicationEngine->load(QUrl("qrc:/qml/main.qml"));
QQuickWindow *window = qobject_cast<QQuickWindow *>(m_applicationEngine->rootObjects().at(0));
window->setIcon(QIcon(":/res/mix_256x256x32.png"));
appLoaded();
}
@ -94,3 +98,9 @@ void AppContext::displayMessageDialog(QString _title, QString _message)
dialogWin->findChild<QObject*>("messageContent", Qt::FindChildrenRecursively)->setProperty("text", _message);
QMetaObject::invokeMethod(dialogWin, "open");
}
void AppContext::toClipboard(QString _text)
{
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(_text);
}

2
mix/AppContext.h

@ -62,6 +62,8 @@ public:
ClientModel* clientModel() { return m_clientModel.get(); }
/// Display an alert message.
void displayMessageDialog(QString _title, QString _message);
/// Copy text to clipboard
Q_INVOKABLE void toClipboard(QString _text);
signals:
/// Triggered once components have been loaded

7
mix/AssemblyDebuggerControl.cpp

@ -29,7 +29,6 @@ using namespace dev::mix;
AssemblyDebuggerControl::AssemblyDebuggerControl(AppContext* _context):
Extension(_context, ExtensionDisplayBehavior::RightView)
{
connect(_context->clientModel(), &ClientModel::showDebuggerWindow, this, &AssemblyDebuggerControl::showDebugger, Qt::QueuedConnection);
}
QString AssemblyDebuggerControl::contentUrl() const
@ -45,9 +44,3 @@ QString AssemblyDebuggerControl::title() const
void AssemblyDebuggerControl::start() const
{
}
void AssemblyDebuggerControl::showDebugger()
{
QObject* debugPanel = m_view->findChild<QObject*>("debugPanel", Qt::FindChildrenRecursively);
QMetaObject::invokeMethod(debugPanel, "update", Q_ARG(QVariant, true));
}

4
mix/AssemblyDebuggerControl.h

@ -42,10 +42,6 @@ public:
void start() const override;
QString title() const override;
QString contentUrl() const override;
private slots:
/// Update UI with machine states result. Displayed in the right side tab.
void showDebugger();
};
}

7
mix/CMakeLists.txt

@ -13,7 +13,7 @@ aux_source_directory(. SRC_LIST)
include_directories(..)
find_package (Qt5WebEngine QUIET)
qt5_add_resources(UI_RESOURCES qml.qrc)
qt5_add_resources(UI_RESOURCES res.qrc)
file(GLOB HEADERS "*.h")
@ -62,6 +62,7 @@ eth_install_executable(${EXECUTABLE}
QMLDIR ${CMAKE_CURRENT_SOURCE_DIR}/qml
)
#add qml files to project tree in Qt creator
#add qml asnd stdc files to project tree in Qt creator
file(GLOB_RECURSE QMLFILES "qml/*.*")
add_custom_target(dummy SOURCES ${QMLFILES})
file(GLOB_RECURSE SOLFILES "stdc/*.*")
add_custom_target(dummy SOURCES ${QMLFILES} ${SOLFILES})

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

Loading…
Cancel
Save