Browse Source

merge from develop

cl-refactor
yann300 10 years ago
parent
commit
a3e2f52b6d
  1. 22
      alethzero/MainWin.cpp
  2. 397
      alethzero/Transact.cpp
  3. 16
      alethzero/Transact.h
  4. 48
      eth/main.cpp
  5. 46
      evmjit/libevmjit/Arith256.cpp
  6. 6
      libdevcore/CommonData.cpp
  7. 8
      libdevcore/CommonData.h
  8. 9
      libdevcrypto/FileSystem.cpp
  9. 2
      libdevcrypto/FileSystem.h
  10. 11
      libethcore/Common.h
  11. 4
      libethcore/Ethasher.cpp
  12. 1
      libethcore/Exceptions.h
  13. 1
      libethcore/Params.cpp
  14. 1
      libethcore/Params.h
  15. 40
      libethereum/Client.cpp
  16. 21
      libethereum/Client.h
  17. 1
      libethereum/EthereumPeer.cpp
  18. 22
      libethereum/Interface.h
  19. 27
      libethereum/LogFilter.cpp
  20. 11
      libethereum/LogFilter.h
  21. 2
      libethereum/State.cpp
  22. 2
      libethereum/Transaction.cpp
  23. 3
      libethereum/Transaction.h
  24. 4
      libevm/ExtVMFace.h
  25. 6
      libevm/VM.cpp
  26. 2
      libevm/VM.h
  27. 1
      libevm/VMFace.h
  28. 31
      libjsqrc/ethereumjs/dist/ethereum.js
  29. 8
      libjsqrc/ethereumjs/dist/ethereum.js.map
  30. 2
      libjsqrc/ethereumjs/dist/ethereum.min.js
  31. 2
      libjsqrc/ethereumjs/lib/web3/contract.js
  32. 27
      libjsqrc/ethereumjs/lib/web3/filter.js
  33. 2
      libp2p/Common.cpp
  34. 3
      libp2p/Common.h
  35. 32
      libp2p/Host.cpp
  36. 9
      libp2p/Host.h
  37. 31
      libp2p/NodeTable.cpp
  38. 5
      libp2p/NodeTable.h
  39. 14
      libp2p/RLPxHandshake.cpp
  40. 14
      libp2p/RLPxHandshake.h
  41. 1
      libp2p/Session.cpp
  42. 4
      libp2p/UDP.h
  43. 207
      libsolidity/ArrayUtils.cpp
  44. 6
      libsolidity/ArrayUtils.h
  45. 17
      libsolidity/Compiler.cpp
  46. 72
      libsolidity/ExpressionCompiler.cpp
  47. 46
      libsolidity/Types.cpp
  48. 19
      libsolidity/Types.h
  49. 47
      libweb3jsonrpc/WebThreeStubServerBase.cpp
  50. 122
      mix/AppContext.cpp
  51. 88
      mix/AppContext.h
  52. 2
      mix/CMakeLists.txt
  53. 26
      mix/ClientModel.cpp
  54. 10
      mix/ClientModel.h
  55. 39
      mix/Clipboard.cpp
  56. 29
      mix/Clipboard.h
  57. 88
      mix/CodeEditorExtensionManager.cpp
  58. 71
      mix/CodeEditorExtensionManager.h
  59. 13
      mix/CodeModel.cpp
  60. 4
      mix/CodeModel.h
  61. 82
      mix/Extension.cpp
  62. 75
      mix/Extension.h
  63. 42
      mix/MixApplication.cpp
  64. 4
      mix/MixApplication.h
  65. 18
      mix/MixClient.cpp
  66. 19
      mix/MixClient.h
  67. 3
      mix/QContractDefinition.cpp
  68. 65
      mix/qml.qrc
  69. 2
      mix/qml/CodeEditorView.qml
  70. 4
      mix/qml/ContractLibrary.qml
  71. 2
      mix/qml/DebugInfoList.qml
  72. 10
      mix/qml/Debugger.qml
  73. 2
      mix/qml/DeploymentDialog.qml
  74. 4
      mix/qml/LogsPane.qml
  75. 18
      mix/qml/MainContent.qml
  76. 19
      mix/qml/ProjectList.qml
  77. 6
      mix/qml/ProjectModel.qml
  78. 14
      mix/qml/Splitter.qml
  79. 38
      mix/qml/StateListModel.qml
  80. 6
      mix/qml/TransactionLog.qml
  81. 4
      mix/qml/WebCodeEditor.qml
  82. 13
      mix/qml/WebPreview.qml
  83. 2864
      mix/qml/html/cm/acorn.js
  84. 1164
      mix/qml/html/cm/acorn_loose.js
  85. 87
      mix/qml/html/cm/comment.js
  86. 547
      mix/qml/html/cm/def.js
  87. 402
      mix/qml/html/cm/doc_comment.js
  88. 969
      mix/qml/html/cm/ecma5spec.js
  89. 1615
      mix/qml/html/cm/infer.js
  90. 139
      mix/qml/html/cm/show-hint.js
  91. 26
      mix/qml/html/cm/signal.js
  92. 697
      mix/qml/html/cm/tern.js
  93. 994
      mix/qml/html/cm/ternserver.js
  94. 359
      mix/qml/html/cm/walk.js
  95. 11
      mix/qml/html/codeeditor.html
  96. 20
      mix/qml/html/codeeditor.js
  97. 149
      mix/qml/js/ProjectModel.js
  98. 37
      mix/qml/main.qml
  99. 124
      mix/res.qrc
  100. 11
      mix/web.qrc

22
alethzero/MainWin.cpp

@ -145,7 +145,7 @@ Main::Main(QWidget *parent) :
cerr << "Block Hash: " << CanonBlockChain::genesis().hash << endl; cerr << "Block Hash: " << CanonBlockChain::genesis().hash << endl;
cerr << "Block RLP: " << RLP(block) << endl; cerr << "Block RLP: " << RLP(block) << endl;
cerr << "Block Hex: " << toHex(block) << endl; cerr << "Block Hex: " << toHex(block) << endl;
cerr << "Network protocol version: " << c_protocolVersion << endl; cerr << "eth Network protocol version: " << eth::c_protocolVersion << endl;
cerr << "Client database version: " << c_databaseVersion << endl; cerr << "Client database version: " << c_databaseVersion << endl;
ui->configDock->close(); ui->configDock->close();
@ -162,7 +162,7 @@ Main::Main(QWidget *parent) :
QSettings s("ethereum", "alethzero"); QSettings s("ethereum", "alethzero");
m_networkConfig = s.value("peers").toByteArray(); m_networkConfig = s.value("peers").toByteArray();
bytesConstRef network((byte*)m_networkConfig.data(), m_networkConfig.size()); bytesConstRef network((byte*)m_networkConfig.data(), m_networkConfig.size());
m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir() + "/AlethZero", false, {"eth", "shh"}, p2p::NetworkPreferences(), network)); m_webThree.reset(new WebThreeDirect(string("AlethZero/v") + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), getDataDir(), false, {"eth", "shh"}, p2p::NetworkPreferences(), network));
m_httpConnector.reset(new jsonrpc::HttpServer(8080)); m_httpConnector.reset(new jsonrpc::HttpServer(8080));
m_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), keysAsVector(m_myKeys), this)); m_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), keysAsVector(m_myKeys), this));
@ -312,7 +312,7 @@ void Main::installBalancesWatch()
Address coinsAddr = getCurrencies(); Address coinsAddr = getCurrencies();
// TODO: Update for new currencies reg. // TODO: Update for new currencies reg.
for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, 0); ++i) for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, PendingBlock); ++i)
altCoins.push_back(right160(ethereum()->stateAt(coinsAddr, i + 1))); altCoins.push_back(right160(ethereum()->stateAt(coinsAddr, i + 1)));
for (auto i: m_myKeys) for (auto i: m_myKeys)
for (auto c: altCoins) for (auto c: altCoins)
@ -583,7 +583,7 @@ Address Main::fromString(QString const& _n) const
{ {
try try
{ {
return Address(fromHex(_n.toStdString(), ThrowType::Throw)); return Address(fromHex(_n.toStdString(), WhenError::Throw));
} }
catch (BadHexCharacter& _e) catch (BadHexCharacter& _e)
{ {
@ -616,10 +616,10 @@ QString Main::lookup(QString const& _a) const
h256 ret; h256 ret;
// TODO: fix with the new DNSreg contract // TODO: fix with the new DNSreg contract
// if (h160 dnsReg = (u160)ethereum()->stateAt(c_config, 4, 0)) // if (h160 dnsReg = (u160)ethereum()->stateAt(c_config, 4, PendingBlock))
// ret = ethereum()->stateAt(dnsReg, n); // ret = ethereum()->stateAt(dnsReg, n);
/* if (!ret) /* if (!ret)
if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0, 0)) if (h160 nameReg = (u160)ethereum()->stateAt(c_config, 0, PendingBlock))
ret = ethereum()->stateAt(nameReg, n2); ret = ethereum()->stateAt(nameReg, n2);
*/ */
if (ret && !((u256)ret >> 32)) if (ret && !((u256)ret >> 32))
@ -937,7 +937,7 @@ void Main::refreshBalances()
u256 totalBalance = 0; u256 totalBalance = 0;
/* map<Address, tuple<QString, u256, u256>> altCoins; /* map<Address, tuple<QString, u256, u256>> altCoins;
Address coinsAddr = getCurrencies(); Address coinsAddr = getCurrencies();
for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, 0); ++i) for (unsigned i = 0; i < ethereum()->stateAt(coinsAddr, PendingBlock); ++i)
{ {
auto n = ethereum()->stateAt(coinsAddr, i + 1); auto n = ethereum()->stateAt(coinsAddr, i + 1);
auto addr = right160(ethereum()->stateAt(coinsAddr, n)); auto addr = right160(ethereum()->stateAt(coinsAddr, n));
@ -1059,7 +1059,7 @@ void Main::refreshBlockCount()
{ {
cwatch << "refreshBlockCount()"; cwatch << "refreshBlockCount()";
auto d = ethereum()->blockChain().details(); auto d = ethereum()->blockChain().details();
ui->blockCount->setText(QString("%4 #%1 PV%2 D%3 H%5").arg(d.number).arg(c_protocolVersion).arg(c_databaseVersion).arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet").arg(c_ethashVersion)); ui->blockCount->setText(QString("%4 #%1 PV%2 D%3 H%5").arg(d.number).arg(eth::c_protocolVersion).arg(c_databaseVersion).arg(m_privateChain.size() ? "[" + m_privateChain + "] " : "testnet").arg(c_ethashVersion));
} }
void Main::on_turboMining_triggered() void Main::on_turboMining_triggered()
@ -1357,7 +1357,7 @@ void Main::on_transactionQueue_currentItemChanged()
auto r = receipt.rlp(); auto r = receipt.rlp();
s << "<div>Receipt: " << toString(RLP(r)) << "</div>"; s << "<div>Receipt: " << toString(RLP(r)) << "</div>";
s << "<div>Receipt-Hex: " Span(Mono) << toHex(receipt.rlp()) << "</span></div>"; s << "<div>Receipt-Hex: " Span(Mono) << toHex(receipt.rlp()) << "</span></div>";
s << renderDiff(ethereum()->diff(i, 0)); s << renderDiff(ethereum()->diff(i, PendingBlock));
// s << "Pre: " << fs.rootHash() << "<br/>"; // s << "Pre: " << fs.rootHash() << "<br/>";
// s << "Post: <b>" << ts.rootHash() << "</b>"; // s << "Post: <b>" << ts.rootHash() << "</b>";
} }
@ -1388,7 +1388,7 @@ void Main::on_inject_triggered()
QString s = QInputDialog::getText(this, "Inject Transaction", "Enter transaction dump in hex"); QString s = QInputDialog::getText(this, "Inject Transaction", "Enter transaction dump in hex");
try try
{ {
bytes b = fromHex(s.toStdString(), ThrowType::Throw); bytes b = fromHex(s.toStdString(), WhenError::Throw);
ethereum()->inject(&b); ethereum()->inject(&b);
} }
catch (BadHexCharacter& _e) catch (BadHexCharacter& _e)
@ -1550,7 +1550,7 @@ void Main::on_debugCurrent_triggered()
unsigned txi = item->data(Qt::UserRole + 1).toInt(); unsigned txi = item->data(Qt::UserRole + 1).toInt();
bytes t = ethereum()->blockChain().transaction(h, txi); bytes t = ethereum()->blockChain().transaction(h, txi);
State s(ethereum()->state(txi, h)); State s(ethereum()->state(txi, h));
Executive e(s, ethereum()->blockChain(), 0); Executive e(s, ethereum()->blockChain(), PendingBlock);
Debugger dw(this, this); Debugger dw(this, this);
dw.populate(e, Transaction(t, CheckSignature::Sender)); dw.populate(e, Transaction(t, CheckSignature::Sender));
dw.exec(); dw.exec();

397
alethzero/Transact.cpp

@ -25,6 +25,7 @@
#include "Transact.h" #include "Transact.h"
#include <fstream> #include <fstream>
#include <boost/algorithm/string.hpp>
#include <QFileDialog> #include <QFileDialog>
#include <QMessageBox> #include <QMessageBox>
#include <liblll/Compiler.h> #include <liblll/Compiler.h>
@ -135,7 +136,39 @@ void Transact::updateFee()
ui->total->setPalette(p); ui->total->setPalette(p);
} }
string Transact::getFunctionHashes(dev::solidity::CompilerStack const& _compiler, string const& _contractName) void Transact::on_destination_currentTextChanged(QString)
{
if (ui->destination->currentText().size() && ui->destination->currentText() != "(Create Contract)")
if (Address a = m_context->fromString(ui->destination->currentText()))
ui->calculatedName->setText(m_context->render(a));
else
ui->calculatedName->setText("Unknown Address");
else
ui->calculatedName->setText("Create Contract");
rejigData();
// updateFee();
}
static std::string toString(TransactionException _te)
{
switch (_te)
{
case TransactionException::Unknown: return "Unknown error";
case TransactionException::InvalidSignature: return "Permanent Abort: Invalid transaction signature";
case TransactionException::InvalidNonce: return "Transient Abort: Invalid transaction nonce";
case TransactionException::NotEnoughCash: return "Transient Abort: Not enough cash to pay for transaction";
case TransactionException::OutOfGasBase: return "Permanent Abort: Not enough gas to consider transaction";
case TransactionException::BlockGasLimitReached: return "Transient Abort: Gas limit of block reached";
case TransactionException::BadInstruction: return "VM Error: Attempt to execute invalid instruction";
case TransactionException::BadJumpDestination: return "VM Error: Attempt to jump to invalid destination";
case TransactionException::OutOfGas: return "VM Error: Out of gas";
case TransactionException::OutOfStack: return "VM Error: VM stack limit reached during execution";
case TransactionException::StackUnderflow: return "VM Error: Stack underflow";
default:; return std::string();
}
}
static string getFunctionHashes(dev::solidity::CompilerStack const& _compiler, string const& _contractName)
{ {
string ret = ""; string ret = "";
auto const& contract = _compiler.getContractDefinition(_contractName); auto const& contract = _compiler.getContractDefinition(_contractName);
@ -150,186 +183,256 @@ string Transact::getFunctionHashes(dev::solidity::CompilerStack const& _compiler
return ret; return ret;
} }
void Transact::on_destination_currentTextChanged(QString) static tuple<vector<string>, bytes, string> userInputToCode(string const& _user, bool _opt)
{
if (ui->destination->currentText().size() && ui->destination->currentText() != "(Create Contract)")
if (Address a = m_context->fromString(ui->destination->currentText()))
ui->calculatedName->setText(m_context->render(a));
else
ui->calculatedName->setText("Unknown Address");
else
ui->calculatedName->setText("Create Contract");
rejigData();
// updateFee();
}
void Transact::rejigData()
{ {
if (isCreation()) string lll;
string solidity;
bytes data;
vector<string> errors;
if (_user.find_first_not_of("1234567890abcdefABCDEF\n\t ") == string::npos && _user.size() % 2 == 0)
{ {
string src = ui->data->toPlainText().toStdString(); std::string u = _user;
vector<string> errors; boost::replace_all_copy(u, "\n", "");
QString lll; boost::replace_all_copy(u, "\t", "");
QString solidity; boost::replace_all_copy(u, " ", "");
if (src.find_first_not_of("1234567890abcdefABCDEF") == string::npos && src.size() % 2 == 0) data = fromHex(u);
m_data = fromHex(src); }
else if (sourceIsSolidity(src)) else if (sourceIsSolidity(_user))
{
dev::solidity::CompilerStack compiler(true);
try
{ {
dev::solidity::CompilerStack compiler(true);
try
{
// compiler.addSources(dev::solidity::StandardSources); // compiler.addSources(dev::solidity::StandardSources);
m_data = compiler.compile(src, ui->optimize->isChecked()); data = compiler.compile(_user, _opt);
solidity = "<h4>Solidity</h4>"; solidity = "<h4>Solidity</h4>";
solidity += "<pre>var " + QString::fromStdString(compiler.defaultContractName()) + " = web3.eth.contractFromAbi(" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped() + ");</pre>"; solidity += "<pre>var " + compiler.defaultContractName() + " = web3.eth.contract(" + QString::fromStdString(compiler.getInterface()).replace(QRegExp("\\s"), "").toHtmlEscaped().toStdString() + ");</pre>";
solidity += "<pre>" + QString::fromStdString(compiler.getSolidityInterface()).toHtmlEscaped() + "</pre>"; solidity += "<pre>" + QString::fromStdString(compiler.getSolidityInterface()).toHtmlEscaped().toStdString() + "</pre>";
solidity += "<pre>" + QString::fromStdString(getFunctionHashes(compiler)).toHtmlEscaped() + "</pre>"; solidity += "<pre>" + QString::fromStdString(getFunctionHashes(compiler, "")).toHtmlEscaped().toStdString() + "</pre>";
}
catch (dev::Exception const& exception)
{
ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler);
solidity = "<h4>Solidity</h4><pre>" + QString::fromStdString(error.str()).toHtmlEscaped() + "</pre>";
}
catch (...)
{
solidity = "<h4>Solidity</h4><pre>Uncaught exception.</pre>";
}
} }
catch (dev::Exception const& exception)
{
ostringstream error;
solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler);
errors.push_back("Solidity: " + error.str());
}
catch (...)
{
errors.push_back("Solidity: Uncaught exception");
}
}
#ifndef _MSC_VER #ifndef _MSC_VER
else if (sourceIsSerpent(src)) else if (sourceIsSerpent(_user))
{
try
{ {
try data = dev::asBytes(::compile(_user));
{
m_data = dev::asBytes(::compile(src));
for (auto& i: errors)
i = "(LLL " + i + ")";
}
catch (string const& err)
{
errors.push_back("Serpent " + err);
}
} }
catch (string const& err)
{
errors.push_back("Serpent " + err);
}
}
#endif #endif
else
{
data = compileLLL(_user, _opt, &errors);
if (errors.empty())
{
auto asmcode = compileLLLToAsm(_user, _opt);
lll = "<h4>LLL</h4><pre>" + QString::fromStdString(asmcode).toHtmlEscaped().toStdString() + "</pre>";
}
}
return make_tuple(errors, data, lll + solidity);
}
string Transact::natspecNotice(Address _to, bytes const& _data)
{
if (ethereum()->codeAt(_to, PendingBlock).size())
{
string userNotice = m_natSpecDB->getUserNotice(ethereum()->postState().codeHash(_to), _data);
if (userNotice.empty())
return "Destination contract unknown.";
else else
{ {
m_data = compileLLL(src, ui->optimize->isChecked(), &errors); NatspecExpressionEvaluator evaluator;
if (errors.empty()) return evaluator.evalExpression(QString::fromStdString(userNotice)).toStdString();
{
auto asmcode = compileLLLToAsm(src, false);
lll = "<h4>Pre</h4><pre>" + QString::fromStdString(asmcode).toHtmlEscaped() + "</pre>";
if (ui->optimize->isChecked())
{
asmcode = compileLLLToAsm(src, true);
lll = "<h4>Opt</h4><pre>" + QString::fromStdString(asmcode).toHtmlEscaped() + "</pre>" + lll;
}
}
} }
QString errs; }
else
return "Destination not a contract.";
}
void Transact::rejigData()
{
if (!ethereum())
return;
// Determine how much balance we have to play with...
auto s = findSecret(value() + ethereum()->gasLimitRemaining() * gasPrice());
auto b = ethereum()->balanceAt(KeyPair(s).address(), PendingBlock);
m_allGood = true;
QString htmlInfo;
auto bail = [&](QString he) {
m_allGood = false;
ui->send->setEnabled(false);
ui->code->setHtml(he + htmlInfo);
};
// Determine m_info.
if (isCreation())
{
string info;
vector<string> errors;
tie(errors, m_data, info) = userInputToCode(ui->data->toPlainText().toStdString(), ui->optimize->isChecked());
if (errors.size()) if (errors.size())
{ {
errs = "<h4>Errors</h4>"; // Errors determining transaction data (i.e. init code). Bail.
QString htmlErrors;
for (auto const& i: errors) for (auto const& i: errors)
errs.append("<div style=\"border-left: 6px solid #c00; margin-top: 2px\">" + QString::fromStdString(i).toHtmlEscaped() + "</div>"); htmlErrors.append("<div class=\"error\"><span class=\"icon\">ERROR</span> " + QString::fromStdString(i).toHtmlEscaped() + "</div>");
bail(htmlErrors);
return;
} }
ui->code->setHtml(errs + lll + solidity + "<h4>Code</h4>" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped() + "<h4>Hex</h4>" Div(Mono) + QString::fromStdString(toHex(m_data)) + "</div>"); htmlInfo = QString::fromStdString(info) + "<h4>Code</h4>" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped();
ui->gas->setMinimum((qint64)Interface::txGas(m_data, 0));
if (!ui->gas->isEnabled())
ui->gas->setValue(m_backupGas);
ui->gas->setEnabled(true);
if (ui->gas->value() == ui->gas->minimum() && !src.empty())
ui->gas->setValue((int)(m_ethereum->postState().gasLimitRemaining() / 10));
} }
else else
{ {
m_data = parseData(ui->data->toPlainText().toStdString()); m_data = parseData(ui->data->toPlainText().toStdString());
auto to = m_context->fromString(ui->destination->currentText()); htmlInfo = "<h4>Dump</h4>" + QString::fromStdString(dev::memDump(m_data, 8, true));
QString natspec; }
if (ethereum()->codeAt(to, 0).size())
{ htmlInfo += "<h4>Hex</h4>" + QString(Div(Mono)) + QString::fromStdString(toHex(m_data)) + "</div>";
string userNotice = m_natSpecDB->getUserNotice(ethereum()->postState().codeHash(to), m_data);
if (userNotice.empty()) // Determine the minimum amount of gas we need to play...
natspec = "Destination contract unknown."; qint64 baseGas = (qint64)Interface::txGas(m_data, 0);
else qint64 gasNeeded = 0;
{
NatspecExpressionEvaluator evaluator; if (b < value() + baseGas * gasPrice())
natspec = evaluator.evalExpression(QString::fromStdString(userNotice)); {
} // Not enough - bail.
ui->gas->setMinimum((qint64)Interface::txGas(m_data, 1)); bail("<div class=\"error\"><span class=\"icon\">ERROR</span> No single account contains enough for paying even the basic amount of gas required.</div>");
if (!ui->gas->isEnabled()) return;
ui->gas->setValue(m_backupGas); }
ui->gas->setEnabled(true); else
} gasNeeded = min<qint64>((qint64)ethereum()->gasLimitRemaining(), (qint64)((b - value()) / gasPrice()));
else
{ // Dry-run execution to determine gas requirement and any execution errors
natspec += "Destination not a contract."; Address to;
if (ui->gas->isEnabled()) ExecutionResult er;
m_backupGas = ui->gas->value(); if (isCreation())
ui->gas->setValue((qint64)Interface::txGas(m_data)); er = ethereum()->create(s, value(), m_data, gasNeeded, gasPrice());
ui->gas->setEnabled(false); else
} {
ui->code->setHtml("<h3>NatSpec</h3>" + natspec + "<h3>Dump</h3>" + QString::fromStdString(dev::memDump(m_data, 8, true)) + "<h3>Hex</h3>" + Div(Mono) + QString::fromStdString(toHex(m_data)) + "</div>"); to = m_context->fromString(ui->destination->currentText());
er = ethereum()->call(s, value(), to, m_data, ethereum()->gasLimitRemaining(), gasPrice());
}
gasNeeded = (qint64)er.gasUsed;
htmlInfo = QString("<div class=\"info\"><span class=\"icon\">INFO</span> Gas required: %1 total = %2 base, %3 exec</div>").arg(gasNeeded).arg(baseGas).arg(gasNeeded - baseGas) + htmlInfo;
if (er.excepted != TransactionException::None)
{
bail("<div class=\"error\"><span class=\"icon\">ERROR</span> " + QString::fromStdString(toString(er.excepted)) + "</div>");
return;
}
if (er.codeDeposit == CodeDeposit::Failed)
{
bail("<div class=\"error\"><span class=\"icon\">ERROR</span> Code deposit failed due to insufficient gas</div>");
return;
}
// Add Natspec information
if (!isCreation())
htmlInfo = "<div class=\"info\"><span class=\"icon\">INFO</span> " + QString::fromStdString(natspecNotice(to, m_data)).toHtmlEscaped() + "</div>" + htmlInfo;
// Update gas
if (ui->gas->value() == ui->gas->minimum())
{
ui->gas->setMinimum(gasNeeded);
ui->gas->setValue(gasNeeded);
} }
else
ui->gas->setMinimum(gasNeeded);
updateFee(); updateFee();
ui->code->setHtml(htmlInfo);
ui->send->setEnabled(m_allGood);
} }
void Transact::on_send_clicked() Secret Transact::findSecret(u256 _totalReq) const
{ {
u256 totalReq = value() + fee(); if (!ethereum())
return Secret();
Secret best;
u256 bestBalance = 0;
for (auto const& i: m_myKeys) for (auto const& i: m_myKeys)
if (ethereum()->balanceAt(i.address(), 0) >= totalReq) {
{ auto b = ethereum()->balanceAt(i.address(), PendingBlock);
Secret s = i.secret(); if (b >= _totalReq)
if (isCreation()) return i.secret();
if (b > bestBalance)
bestBalance = b, best = i.secret();
}
return best;
}
void Transact::on_send_clicked()
{
Secret s = findSecret(value() + fee());
auto b = ethereum()->balanceAt(KeyPair(s).address(), PendingBlock);
if (!s || b < value() + fee())
{
QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount.");
return;
}
if (isCreation())
{
// If execution is a contract creation, add Natspec to
// a local Natspec LEVELDB
ethereum()->submitTransaction(s, value(), m_data, ui->gas->value(), gasPrice());
string src = ui->data->toPlainText().toStdString();
if (sourceIsSolidity(src))
try
{ {
// If execution is a contract creation, add Natspec to dev::solidity::CompilerStack compiler(true);
// a local Natspec LEVELDB m_data = compiler.compile(src, ui->optimize->isChecked());
ethereum()->submitTransaction(s, value(), m_data, ui->gas->value(), gasPrice()); for (string const& s: compiler.getContractNames())
string src = ui->data->toPlainText().toStdString(); {
if (sourceIsSolidity(src)) h256 contractHash = compiler.getContractCodeHash(s);
try m_natSpecDB->add(contractHash, compiler.getMetadata(s, dev::solidity::DocumentationType::NatspecUser));
{ }
dev::solidity::CompilerStack compiler(true);
m_data = compiler.compile(src, ui->optimize->isChecked());
for (string const& s: compiler.getContractNames())
{
h256 contractHash = compiler.getContractCodeHash(s);
m_natSpecDB->add(contractHash, compiler.getMetadata(s, dev::solidity::DocumentationType::NatspecUser));
}
}
catch (...)
{
}
close();
return;
} }
else catch (...) {}
ethereum()->submitTransaction(s, value(), m_context->fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice()); }
return; else
} ethereum()->submitTransaction(s, value(), m_context->fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice());
QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount."); close();
} }
void Transact::on_debug_clicked() void Transact::on_debug_clicked()
{ {
Secret s = findSecret(value() + fee());
auto b = ethereum()->balanceAt(KeyPair(s).address(), PendingBlock);
if (!s || b < value() + fee())
{
QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount.");
return;
}
try try
{ {
u256 totalReq = value() + fee(); State st(ethereum()->postState());
for (auto i: m_myKeys) Transaction t = isCreation() ?
if (ethereum()->balanceAt(i.address()) >= totalReq) Transaction(value(), gasPrice(), ui->gas->value(), m_data, st.transactionsFrom(dev::toAddress(s)), s) :
{ Transaction(value(), gasPrice(), ui->gas->value(), m_context->fromString(ui->destination->currentText()), m_data, st.transactionsFrom(dev::toAddress(s)), s);
State st(ethereum()->postState()); Debugger dw(m_context, this);
Secret s = i.secret(); Executive e(st, ethereum()->blockChain(), 0);
Transaction t = isCreation() ? dw.populate(e, t);
Transaction(value(), gasPrice(), ui->gas->value(), m_data, st.transactionsFrom(dev::toAddress(s)), s) : dw.exec();
Transaction(value(), gasPrice(), ui->gas->value(), m_context->fromString(ui->destination->currentText()), m_data, st.transactionsFrom(dev::toAddress(s)), s);
Debugger dw(m_context, this);
Executive e(st, ethereum()->blockChain(), 0);
dw.populate(e, t);
dw.exec();
return;
}
QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount.");
} }
catch (dev::Exception const& _e) catch (dev::Exception const& _e)
{ {

16
alethzero/Transact.h

@ -57,7 +57,7 @@ private slots:
void on_cancel_clicked() { close(); } void on_cancel_clicked() { close(); }
private: private:
dev::eth::Client* ethereum() { return m_ethereum; } dev::eth::Client* ethereum() const { return m_ethereum; }
void rejigData(); void rejigData();
void updateDestination(); void updateDestination();
@ -68,15 +68,17 @@ private:
dev::u256 value() const; dev::u256 value() const;
dev::u256 gasPrice() const; dev::u256 gasPrice() const;
std::string getFunctionHashes(dev::solidity::CompilerStack const& _compiler, std::string const& _contractName = std::string()); std::string natspecNotice(dev::Address _to, dev::bytes const& _data);
dev::Secret findSecret(dev::u256 _totalReq) const;
Ui::Transact* ui; Ui::Transact* ui = nullptr;
unsigned m_backupGas; unsigned m_backupGas = 0;
dev::bytes m_data; dev::bytes m_data;
QList<dev::KeyPair> m_myKeys; QList<dev::KeyPair> m_myKeys;
dev::eth::Client* m_ethereum; dev::eth::Client* m_ethereum = nullptr;
Context* m_context; Context* m_context = nullptr;
NatSpecFace* m_natSpecDB; NatSpecFace* m_natSpecDB = nullptr;
bool m_allGood = false;
}; };

48
eth/main.cpp

@ -103,7 +103,7 @@ void interactiveHelp()
void help() void help()
{ {
cout cout
<< "Usage eth [OPTIONS] <remote-host>" << endl << "Usage eth [OPTIONS]" << endl
<< "Options:" << endl << "Options:" << endl
<< " -a,--address <addr> Set the coinbase (mining payout) address to addr (default: auto)." << endl << " -a,--address <addr> Set the coinbase (mining payout) address to addr (default: auto)." << endl
<< " -b,--bootstrap Connect to the default Ethereum peerserver." << endl << " -b,--bootstrap Connect to the default Ethereum peerserver." << endl
@ -119,6 +119,7 @@ void help()
<< " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl << " -j,--json-rpc Enable JSON-RPC server (default: off)." << endl
<< " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: 8080)." << endl << " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: 8080)." << endl
#endif #endif
<< " -K,--kill-blockchain First kill the blockchain." << endl
<< " -l,--listen <port> Listen on the given port for incoming connected (default: 30303)." << endl << " -l,--listen <port> Listen on the given port for incoming connected (default: 30303)." << endl
<< " -L,--local-networking Use peers whose addresses are local." << endl << " -L,--local-networking Use peers whose addresses are local." << endl
<< " -m,--mining <on/off/number> Enable mining, optionally for a specified number of blocks (Default: off)" << endl << " -m,--mining <on/off/number> Enable mining, optionally for a specified number of blocks (Default: off)" << endl
@ -159,7 +160,7 @@ string credits(bool _interactive = false)
void version() void version()
{ {
cout << "eth version " << dev::Version << endl; cout << "eth version " << dev::Version << endl;
cout << "Network protocol version: " << dev::eth::c_protocolVersion << endl; cout << "eth network protocol version: " << dev::eth::c_protocolVersion << endl;
cout << "Client database version: " << dev::eth::c_databaseVersion << endl; cout << "Client database version: " << dev::eth::c_databaseVersion << endl;
cout << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl; cout << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl;
exit(0); exit(0);
@ -214,6 +215,7 @@ int main(int argc, char** argv)
bool upnp = true; bool upnp = true;
bool useLocal = false; bool useLocal = false;
bool forceMining = false; bool forceMining = false;
bool killChain = false;
bool jit = false; bool jit = false;
bool structuredLogging = false; bool structuredLogging = false;
string structuredLoggingFormat = "%Y-%m-%dT%H:%M:%S"; string structuredLoggingFormat = "%Y-%m-%dT%H:%M:%S";
@ -270,13 +272,14 @@ int main(int argc, char** argv)
} }
else if (arg == "-L" || arg == "--local-networking") else if (arg == "-L" || arg == "--local-networking")
useLocal = true; useLocal = true;
else if (arg == "-K" || arg == "--kill-blockchain")
killChain = true;
else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc) else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc)
clientName = argv[++i]; clientName = argv[++i];
else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc) else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc)
{
try try
{ {
coinbase = h160(fromHex(argv[++i], ThrowType::Throw)); coinbase = h160(fromHex(argv[++i], WhenError::Throw));
} }
catch (BadHexCharacter& _e) catch (BadHexCharacter& _e)
{ {
@ -289,7 +292,6 @@ int main(int argc, char** argv)
cwarn << "coinbase rejected"; cwarn << "coinbase rejected";
break; break;
} }
}
else if ((arg == "-s" || arg == "--secret") && i + 1 < argc) else if ((arg == "-s" || arg == "--secret") && i + 1 < argc)
us = KeyPair(h256(fromHex(argv[++i]))); us = KeyPair(h256(fromHex(argv[++i])));
else if (arg == "--structured-logging-format" && i + 1 < argc) else if (arg == "--structured-logging-format" && i + 1 < argc)
@ -300,20 +302,24 @@ int main(int argc, char** argv)
dbPath = argv[++i]; dbPath = argv[++i];
else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc) else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc)
{ {
try { try
{
blockFees = stof(argv[++i]); blockFees = stof(argv[++i]);
} }
catch (...) { catch (...)
{
cerr << "Bad " << arg << " option: " << argv[++i] << endl; cerr << "Bad " << arg << " option: " << argv[++i] << endl;
return -1; return -1;
} }
} }
else if ((arg == "-e" || arg == "--ether-price") && i + 1 < argc) else if ((arg == "-e" || arg == "--ether-price") && i + 1 < argc)
{ {
try { try
{
etherPrice = stof(argv[++i]); etherPrice = stof(argv[++i]);
} }
catch (...) { catch (...)
{
cerr << "Bad " << arg << " option: " << argv[++i] << endl; cerr << "Bad " << arg << " option: " << argv[++i] << endl;
return -1; return -1;
} }
@ -401,7 +407,10 @@ int main(int argc, char** argv)
else if (arg == "-V" || arg == "--version") else if (arg == "-V" || arg == "--version")
version(); version();
else else
remoteHost = argv[i]; {
cerr << "Invalid argument: " << arg << endl;
exit(-1);
}
} }
if (!clientName.empty()) if (!clientName.empty())
@ -417,7 +426,7 @@ int main(int argc, char** argv)
dev::WebThreeDirect web3( dev::WebThreeDirect web3(
clientImplString, clientImplString,
dbPath, dbPath,
false, killChain,
mode == NodeMode::Full ? set<string>{"eth", "shh"} : set<string>(), mode == NodeMode::Full ? set<string>{"eth", "shh"} : set<string>(),
netPrefs, netPrefs,
&nodesState, &nodesState,
@ -434,7 +443,8 @@ int main(int argc, char** argv)
c->setAddress(coinbase); c->setAddress(coinbase);
} }
cout << "Address: " << endl << toHex(us.address().asArray()) << endl; cout << "Transaction Signer: " << us.address() << endl;
cout << "Mining Benefactor: " << coinbase << endl;
web3.startNetwork(); web3.startNetwork();
if (bootstrap) if (bootstrap)
@ -680,7 +690,7 @@ int main(int argc, char** argv)
auto acs =c->addresses(); auto acs =c->addresses();
string ss; string ss;
for (auto const& i: acs) for (auto const& i: acs)
if ( c->codeAt(i, 0).size()) if ( c->codeAt(i, PendingBlock).size())
{ {
ss = toString(i) + " : " + toString( c->balanceAt(i)) + " [" + toString((unsigned) c->countAt(i)) + "]"; ss = toString(i) + " : " + toString( c->balanceAt(i)) + " [" + toString((unsigned) c->countAt(i)) + "]";
cout << ss << endl; cout << ss << endl;
@ -691,7 +701,7 @@ int main(int argc, char** argv)
auto acs =c->addresses(); auto acs =c->addresses();
string ss; string ss;
for (auto const& i: acs) for (auto const& i: acs)
if ( c->codeAt(i, 0).empty()) if ( c->codeAt(i, PendingBlock).empty())
{ {
ss = toString(i) + " : " + toString( c->balanceAt(i)) + " [" + toString((unsigned) c->countAt(i)) + "]"; ss = toString(i) + " : " + toString( c->balanceAt(i)) + " [" + toString((unsigned) c->countAt(i)) + "]";
cout << ss << endl; cout << ss << endl;
@ -720,7 +730,7 @@ int main(int argc, char** argv)
u256 minGas = (u256)Client::txGas(bytes(), 0); u256 minGas = (u256)Client::txGas(bytes(), 0);
try try
{ {
Address dest = h160(fromHex(hexAddr, ThrowType::Throw)); Address dest = h160(fromHex(hexAddr, WhenError::Throw));
c->submitTransaction(us.secret(), amount, dest, bytes(), minGas); c->submitTransaction(us.secret(), amount, dest, bytes(), minGas);
} }
catch (BadHexCharacter& _e) catch (BadHexCharacter& _e)
@ -764,7 +774,7 @@ int main(int argc, char** argv)
stringstream ssc; stringstream ssc;
try try
{ {
init = fromHex(sinit, ThrowType::Throw); init = fromHex(sinit, WhenError::Throw);
} }
catch (BadHexCharacter& _e) catch (BadHexCharacter& _e)
{ {
@ -883,10 +893,10 @@ int main(int argc, char** argv)
try try
{ {
auto storage =c->storageAt(h, 0); auto storage =c->storageAt(h, PendingBlock);
for (auto const& i: storage) for (auto const& i: storage)
s << "@" << showbase << hex << i.first << " " << showbase << hex << i.second << endl; s << "@" << showbase << hex << i.first << " " << showbase << hex << i.second << endl;
s << endl << disassemble( c->codeAt(h, 0)) << endl; s << endl << disassemble( c->codeAt(h, PendingBlock)) << endl;
string outFile = getDataDir() + "/" + rechex + ".evm"; string outFile = getDataDir() + "/" + rechex + ".evm";
ofstream ofs; ofstream ofs;
@ -925,7 +935,7 @@ int main(int argc, char** argv)
{ {
try try
{ {
coinbase = h160(fromHex(hexAddr, ThrowType::Throw)); coinbase = h160(fromHex(hexAddr, WhenError::Throw));
} }
catch (BadHexCharacter& _e) catch (BadHexCharacter& _e)
{ {

46
evmjit/libevmjit/Arith256.cpp

@ -371,17 +371,18 @@ llvm::Value* Arith256::mul(llvm::Value* _arg1, llvm::Value* _arg2)
std::pair<llvm::Value*, llvm::Value*> Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2) std::pair<llvm::Value*, llvm::Value*> Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2)
{ {
if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_arg1)) // FIXME: Disabled because of llvm::APInt::urem bug
{ // if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_arg1))
if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2)) // {
{ // if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2))
if (!c2->getValue()) // {
return std::make_pair(Constant::get(0), Constant::get(0)); // if (!c2->getValue())
auto div = Constant::get(c1->getValue().udiv(c2->getValue())); // return std::make_pair(Constant::get(0), Constant::get(0));
auto mod = Constant::get(c1->getValue().urem(c2->getValue())); // auto div = Constant::get(c1->getValue().udiv(c2->getValue()));
return std::make_pair(div, mod); // auto mod = Constant::get(c1->getValue().urem(c2->getValue()));
} // return std::make_pair(div, mod);
} // }
// }
auto r = createCall(getDivFunc(Type::Word), {_arg1, _arg2}); auto r = createCall(getDivFunc(Type::Word), {_arg1, _arg2});
auto div = m_builder.CreateExtractValue(r, 0, "div"); auto div = m_builder.CreateExtractValue(r, 0, "div");
@ -391,17 +392,18 @@ std::pair<llvm::Value*, llvm::Value*> Arith256::div(llvm::Value* _arg1, llvm::Va
std::pair<llvm::Value*, llvm::Value*> Arith256::sdiv(llvm::Value* _x, llvm::Value* _y) std::pair<llvm::Value*, llvm::Value*> Arith256::sdiv(llvm::Value* _x, llvm::Value* _y)
{ {
if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_x)) // FIXME: Disabled because of llvm::APInt::urem bug
{ // if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_x))
if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_y)) // {
{ // if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_y))
if (!c2->getValue()) // {
return std::make_pair(Constant::get(0), Constant::get(0)); // if (!c2->getValue())
auto div = Constant::get(c1->getValue().sdiv(c2->getValue())); // return std::make_pair(Constant::get(0), Constant::get(0));
auto mod = Constant::get(c1->getValue().srem(c2->getValue())); // auto div = Constant::get(c1->getValue().sdiv(c2->getValue()));
return std::make_pair(div, mod); // auto mod = Constant::get(c1->getValue().srem(c2->getValue()));
} // return std::make_pair(div, mod);
} // }
// }
auto xIsNeg = m_builder.CreateICmpSLT(_x, Constant::get(0)); auto xIsNeg = m_builder.CreateICmpSLT(_x, Constant::get(0));
auto xNeg = m_builder.CreateSub(Constant::get(0), _x); auto xNeg = m_builder.CreateSub(Constant::get(0), _x);

6
libdevcore/CommonData.cpp

@ -78,7 +78,7 @@ int dev::fromHex(char _i)
BOOST_THROW_EXCEPTION(BadHexCharacter() << errinfo_invalidSymbol(_i)); BOOST_THROW_EXCEPTION(BadHexCharacter() << errinfo_invalidSymbol(_i));
} }
bytes dev::fromHex(std::string const& _s, ThrowType _throw) bytes dev::fromHex(std::string const& _s, WhenError _throw)
{ {
unsigned s = (_s[0] == '0' && _s[1] == 'x') ? 2 : 0; unsigned s = (_s[0] == '0' && _s[1] == 'x') ? 2 : 0;
std::vector<uint8_t> ret; std::vector<uint8_t> ret;
@ -96,7 +96,7 @@ bytes dev::fromHex(std::string const& _s, ThrowType _throw)
#ifndef BOOST_NO_EXCEPTIONS #ifndef BOOST_NO_EXCEPTIONS
cwarn << boost::current_exception_diagnostic_information(); cwarn << boost::current_exception_diagnostic_information();
#endif #endif
if (_throw == ThrowType::Throw) if (_throw == WhenError::Throw)
throw; throw;
} }
for (unsigned i = s; i < _s.size(); i += 2) for (unsigned i = s; i < _s.size(); i += 2)
@ -109,7 +109,7 @@ bytes dev::fromHex(std::string const& _s, ThrowType _throw)
#ifndef BOOST_NO_EXCEPTIONS #ifndef BOOST_NO_EXCEPTIONS
cwarn << boost::current_exception_diagnostic_information(); cwarn << boost::current_exception_diagnostic_information();
#endif #endif
if (_throw == ThrowType::Throw) if (_throw == WhenError::Throw)
throw; throw;
} }
return ret; return ret;

8
libdevcore/CommonData.h

@ -35,9 +35,9 @@ namespace dev
// String conversion functions, mainly to/from hex/nibble/byte representations. // String conversion functions, mainly to/from hex/nibble/byte representations.
enum class ThrowType enum class WhenError
{ {
NoThrow = 0, DontThrow = 0,
Throw = 1, Throw = 1,
}; };
@ -59,8 +59,8 @@ int fromHex(char _i);
/// Converts a (printable) ASCII hex string into the corresponding byte stream. /// Converts a (printable) ASCII hex string into the corresponding byte stream.
/// @example fromHex("41626261") == asBytes("Abba") /// @example fromHex("41626261") == asBytes("Abba")
/// If _throw = ThrowType::NoThrow, it replaces bad hex characters with 0's, otherwise it will throw an exception. /// If _throw = ThrowType::DontThrow, it replaces bad hex characters with 0's, otherwise it will throw an exception.
bytes fromHex(std::string const& _s, ThrowType _throw = ThrowType::NoThrow); bytes fromHex(std::string const& _s, WhenError _throw = WhenError::DontThrow);
#if 0 #if 0
std::string toBase58(bytesConstRef _data); std::string toBase58(bytesConstRef _data);

9
libdevcrypto/FileSystem.cpp

@ -32,12 +32,15 @@
using namespace std; using namespace std;
using namespace dev; using namespace dev;
std::string dev::getDataDir() std::string dev::getDataDir(std::string _prefix)
{ {
if (_prefix.empty())
_prefix = "ethereum";
#ifdef _WIN32 #ifdef _WIN32
_prefix[0] = toupper(_prefix[0]);
char path[1024] = ""; char path[1024] = "";
if (SHGetSpecialFolderPathA(NULL, path, CSIDL_APPDATA, true)) if (SHGetSpecialFolderPathA(NULL, path, CSIDL_APPDATA, true))
return (boost::filesystem::path(path) / "Ethereum").string(); return (boost::filesystem::path(path) / _prefix).string();
else else
{ {
#ifndef _MSC_VER // todo? #ifndef _MSC_VER // todo?
@ -57,7 +60,7 @@ std::string dev::getDataDir()
// This eventually needs to be put in proper wrapper (to support sandboxing) // This eventually needs to be put in proper wrapper (to support sandboxing)
return (dataDirPath / "Library/Application Support/Ethereum").string(); return (dataDirPath / "Library/Application Support/Ethereum").string();
#else #else
return (dataDirPath / ".ethereum").string(); return (dataDirPath / ("." + _prefix)).string();
#endif #endif
#endif #endif
} }

2
libdevcrypto/FileSystem.h

@ -30,6 +30,6 @@ namespace dev
{ {
/// @returns the path for user data. /// @returns the path for user data.
std::string getDataDir(); std::string getDataDir(std::string _prefix = "ethereum");
} }

11
libethcore/Common.h

@ -71,5 +71,16 @@ static const u256 wei = exp10<0>();
using Nonce = h64; using Nonce = h64;
using BlockNumber = unsigned;
static const BlockNumber LatestBlock = (BlockNumber)-2;
static const BlockNumber PendingBlock = (BlockNumber)-1;
enum class RelativeBlock: BlockNumber
{
Latest = LatestBlock,
Pending = PendingBlock
};
} }
} }

4
libethcore/Ethasher.cpp

@ -71,10 +71,10 @@ bytesConstRef Ethasher::full(BlockInfo const& _header)
m_fulls.erase(m_fulls.begin()); m_fulls.erase(m_fulls.begin());
} }
try { try {
boost::filesystem::create_directories(getDataDir() + "/ethashcache"); boost::filesystem::create_directories(getDataDir("ethash"));
} catch (...) {} } catch (...) {}
std::string memoFile = getDataDir() + "/ethashcache/full"; std::string memoFile = getDataDir("ethash") + "/full";
auto info = rlpList(c_ethashRevision, _header.seedHash()); auto info = rlpList(c_ethashRevision, _header.seedHash());
if (boost::filesystem::exists(memoFile) && contents(memoFile + ".info") != info) if (boost::filesystem::exists(memoFile) && contents(memoFile + ".info") != info)
boost::filesystem::remove(memoFile); boost::filesystem::remove(memoFile);

1
libethcore/Exceptions.h

@ -59,7 +59,6 @@ struct InvalidGasUsed: virtual dev::Exception {};
class InvalidTransactionsHash: virtual public dev::Exception {}; class InvalidTransactionsHash: virtual public dev::Exception {};
struct InvalidTransaction: virtual dev::Exception {}; struct InvalidTransaction: virtual dev::Exception {};
struct InvalidDifficulty: virtual dev::Exception {}; struct InvalidDifficulty: virtual dev::Exception {};
struct InvalidSeedHash: virtual dev::Exception {};
class InvalidGasLimit: virtual public dev::Exception {}; class InvalidGasLimit: virtual public dev::Exception {};
struct InvalidTransactionGasUsed: virtual dev::Exception {}; struct InvalidTransactionGasUsed: virtual dev::Exception {};
struct InvalidTransactionsStateRoot: virtual dev::Exception {}; struct InvalidTransactionsStateRoot: virtual dev::Exception {};

1
libethcore/Params.cpp

@ -37,6 +37,7 @@ u256 const c_gasLimitBoundDivisor = 1024;
u256 const c_minimumDifficulty = 131072; u256 const c_minimumDifficulty = 131072;
u256 const c_difficultyBoundDivisor = 2048; u256 const c_difficultyBoundDivisor = 2048;
u256 const c_durationLimit = 8; u256 const c_durationLimit = 8;
u256 const c_stackLimit = 1024;
u256 const c_tierStepGas[] = {0, 2, 3, 5, 8, 10, 20, 0}; u256 const c_tierStepGas[] = {0, 2, 3, 5, 8, 10, 20, 0};
u256 const c_expGas = 10; u256 const c_expGas = 10;
u256 const c_expByteGas = 10; u256 const c_expByteGas = 10;

1
libethcore/Params.h

@ -38,6 +38,7 @@ extern u256 const c_gasLimitBoundDivisor;
extern u256 const c_minimumDifficulty; extern u256 const c_minimumDifficulty;
extern u256 const c_difficultyBoundDivisor; extern u256 const c_difficultyBoundDivisor;
extern u256 const c_durationLimit; extern u256 const c_durationLimit;
extern u256 const c_stackLimit;
extern u256 const c_tierStepGas[8]; ///< Once per operation, for a selection of them. extern u256 const c_tierStepGas[8]; ///< Once per operation, for a selection of them.
extern u256 const c_expGas; ///< Once per EXP instuction. extern u256 const c_expGas; ///< Once per EXP instuction.

40
libethereum/Client.cpp

@ -40,7 +40,7 @@ VersionChecker::VersionChecker(string const& _dbPath):
{ {
auto protocolContents = contents(m_path + "/protocol"); auto protocolContents = contents(m_path + "/protocol");
auto databaseContents = contents(m_path + "/database"); auto databaseContents = contents(m_path + "/database");
m_ok = RLP(protocolContents).toInt<unsigned>(RLP::LaisezFaire) == c_protocolVersion && RLP(databaseContents).toInt<unsigned>(RLP::LaisezFaire) == c_databaseVersion; m_ok = RLP(protocolContents).toInt<unsigned>(RLP::LaisezFaire) == eth::c_protocolVersion && RLP(databaseContents).toInt<unsigned>(RLP::LaisezFaire) == c_databaseVersion;
} }
void VersionChecker::setOk() void VersionChecker::setOk()
@ -55,7 +55,7 @@ void VersionChecker::setOk()
{ {
cwarn << "Unhandled exception! Failed to create directory: " << m_path << "\n" << boost::current_exception_diagnostic_information(); cwarn << "Unhandled exception! Failed to create directory: " << m_path << "\n" << boost::current_exception_diagnostic_information();
} }
writeFile(m_path + "/protocol", rlp(c_protocolVersion)); writeFile(m_path + "/protocol", rlp(eth::c_protocolVersion));
writeFile(m_path + "/database", rlp(c_databaseVersion)); writeFile(m_path + "/database", rlp(c_databaseVersion));
} }
} }
@ -357,11 +357,11 @@ LocalisedLogEntries Client::checkWatch(unsigned _watchId)
return ret; return ret;
} }
void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Set& io_changed, h256 _sha3) void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Set& io_changed, h256 _transactionHash)
{ {
Guard l(m_filterLock); Guard l(m_filterLock);
for (pair<h256 const, InstalledFilter>& i: m_filters) for (pair<h256 const, InstalledFilter>& i: m_filters)
if ((unsigned)i.second.filter.latest() > m_bc.number()) if (i.second.filter.envelops(RelativeBlock::Pending, m_bc.number() + 1))
{ {
// acceptable number. // acceptable number.
auto m = i.second.filter.matches(_receipt); auto m = i.second.filter.matches(_receipt);
@ -369,7 +369,7 @@ void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Set& i
{ {
// filter catches them // filter catches them
for (LogEntry const& l: m) for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, m_bc.number() + 1, _sha3)); i.second.changes.push_back(LocalisedLogEntry(l, m_bc.number() + 1, _transactionHash));
io_changed.insert(i.first); io_changed.insert(i.first);
} }
} }
@ -383,7 +383,7 @@ void Client::appendFromNewBlock(h256 const& _block, h256Set& io_changed)
Guard l(m_filterLock); Guard l(m_filterLock);
for (pair<h256 const, InstalledFilter>& i: m_filters) for (pair<h256 const, InstalledFilter>& i: m_filters)
if ((unsigned)i.second.filter.latest() >= d.number && (unsigned)i.second.filter.earliest() <= d.number && i.second.filter.matches(d.logBloom)) if (i.second.filter.envelops(RelativeBlock::Latest, d.number) && i.second.filter.matches(d.logBloom))
// acceptable number & looks like block may contain a matching log entry. // acceptable number & looks like block may contain a matching log entry.
for (size_t j = 0; j < br.receipts.size(); j++) for (size_t j = 0; j < br.receipts.size(); j++)
{ {
@ -391,10 +391,10 @@ void Client::appendFromNewBlock(h256 const& _block, h256Set& io_changed)
auto m = i.second.filter.matches(tr); auto m = i.second.filter.matches(tr);
if (m.size()) if (m.size())
{ {
auto sha3 = transaction(d.hash, j).sha3(); auto transactionHash = transaction(d.hash, j).sha3();
// filter catches them // filter catches them
for (LogEntry const& l: m) for (LogEntry const& l: m)
i.second.changes.push_back(LocalisedLogEntry(l, (unsigned)d.number, sha3)); i.second.changes.push_back(LocalisedLogEntry(l, (unsigned)d.number, transactionHash));
io_changed.insert(i.first); io_changed.insert(i.first);
} }
} }
@ -491,7 +491,7 @@ void Client::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes
m_tq.attemptImport(t.rlp()); m_tq.attemptImport(t.rlp());
} }
ExecutionResult Client::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, int _blockNumber) ExecutionResult Client::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber)
{ {
ExecutionResult ret; ExecutionResult ret;
try try
@ -514,7 +514,7 @@ ExecutionResult Client::call(Secret _secret, u256 _value, Address _dest, bytes c
return ret; return ret;
} }
ExecutionResult Client::create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, int _blockNumber) ExecutionResult Client::create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber)
{ {
ExecutionResult ret; ExecutionResult ret;
try try
@ -739,12 +739,12 @@ unsigned Client::numberOf(int _n) const
return m_bc.details().number + max(-(int)m_bc.details().number, 1 + _n); return m_bc.details().number + max(-(int)m_bc.details().number, 1 + _n);
} }
State Client::asOf(int _h) const State Client::asOf(unsigned _h) const
{ {
ReadGuard l(x_stateDB); ReadGuard l(x_stateDB);
if (_h == 0) if (_h == PendingBlock)
return m_postMine; return m_postMine;
else if (_h == -1) else if (_h == LatestBlock)
return m_preMine; return m_preMine;
else else
return State(m_stateDB, m_bc, m_bc.numberHash(numberOf(_h))); return State(m_stateDB, m_bc, m_bc.numberHash(numberOf(_h)));
@ -768,7 +768,7 @@ eth::State Client::state(unsigned _txi) const
return m_postMine.fromPending(_txi); return m_postMine.fromPending(_txi);
} }
StateDiff Client::diff(unsigned _txi, int _block) const StateDiff Client::diff(unsigned _txi, BlockNumber _block) const
{ {
State st = asOf(_block); State st = asOf(_block);
return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); return st.fromPending(_txi).diff(st.fromPending(_txi + 1));
@ -780,7 +780,7 @@ StateDiff Client::diff(unsigned _txi, h256 _block) const
return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); return st.fromPending(_txi).diff(st.fromPending(_txi + 1));
} }
std::vector<Address> Client::addresses(int _block) const std::vector<Address> Client::addresses(BlockNumber _block) const
{ {
vector<Address> ret; vector<Address> ret;
for (auto const& i: asOf(_block).addresses()) for (auto const& i: asOf(_block).addresses())
@ -788,27 +788,27 @@ std::vector<Address> Client::addresses(int _block) const
return ret; return ret;
} }
u256 Client::balanceAt(Address _a, int _block) const u256 Client::balanceAt(Address _a, BlockNumber _block) const
{ {
return asOf(_block).balance(_a); return asOf(_block).balance(_a);
} }
std::map<u256, u256> Client::storageAt(Address _a, int _block) const std::map<u256, u256> Client::storageAt(Address _a, BlockNumber _block) const
{ {
return asOf(_block).storage(_a); return asOf(_block).storage(_a);
} }
u256 Client::countAt(Address _a, int _block) const u256 Client::countAt(Address _a, BlockNumber _block) const
{ {
return asOf(_block).transactionsFrom(_a); return asOf(_block).transactionsFrom(_a);
} }
u256 Client::stateAt(Address _a, u256 _l, int _block) const u256 Client::stateAt(Address _a, u256 _l, BlockNumber _block) const
{ {
return asOf(_block).storage(_a, _l); return asOf(_block).storage(_a, _l);
} }
bytes Client::codeAt(Address _a, int _block) const bytes Client::codeAt(Address _a, BlockNumber _block) const
{ {
return asOf(_block).code(_a); return asOf(_block).code(_a);
} }

21
libethereum/Client.h

@ -225,11 +225,11 @@ public:
virtual void flushTransactions() override; virtual void flushTransactions() override;
/// Makes the given call. Nothing is recorded into the state. /// Makes the given call. Nothing is recorded into the state.
virtual ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, int _blockNumber = 0) override; virtual ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock) override;
/// Does the given creation. Nothing is recorded into the state. /// Does the given creation. Nothing is recorded into the state.
/// @returns the pair of the Address of the created contract together with its code. /// @returns the pair of the Address of the created contract together with its code.
virtual ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, int _blockNumber = 0) override; virtual ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = PendingBlock) override;
/// Makes the given call. Nothing is recorded into the state. This cheats by creating a null address and endowing it with a lot of ETH. /// Makes the given call. Nothing is recorded into the state. This cheats by creating a null address and endowing it with a lot of ETH.
ExecutionResult call(Address _dest, bytes const& _data = bytes(), u256 _gas = 125000, u256 _value = 0, u256 _gasPrice = 1 * ether); ExecutionResult call(Address _dest, bytes const& _data = bytes(), u256 _gas = 125000, u256 _value = 0, u256 _gasPrice = 1 * ether);
@ -244,11 +244,11 @@ public:
using Interface::codeAt; using Interface::codeAt;
using Interface::storageAt; using Interface::storageAt;
virtual u256 balanceAt(Address _a, int _block) const; virtual u256 balanceAt(Address _a, BlockNumber _block) const;
virtual u256 countAt(Address _a, int _block) const; virtual u256 countAt(Address _a, BlockNumber _block) const;
virtual u256 stateAt(Address _a, u256 _l, int _block) const; virtual u256 stateAt(Address _a, u256 _l, BlockNumber _block) const;
virtual bytes codeAt(Address _a, int _block) const; virtual bytes codeAt(Address _a, BlockNumber _block) const;
virtual std::map<u256, u256> storageAt(Address _a, int _block) const; virtual std::map<u256, u256> storageAt(Address _a, BlockNumber _block) const;
virtual unsigned installWatch(LogFilter const& _filter, Reaping _r = Reaping::Automatic) override; virtual unsigned installWatch(LogFilter const& _filter, Reaping _r = Reaping::Automatic) override;
virtual unsigned installWatch(h256 _filterId, Reaping _r = Reaping::Automatic) override; virtual unsigned installWatch(h256 _filterId, Reaping _r = Reaping::Automatic) override;
@ -282,11 +282,11 @@ public:
/// Differences between transactions. /// Differences between transactions.
using Interface::diff; using Interface::diff;
virtual StateDiff diff(unsigned _txi, h256 _block) const; virtual StateDiff diff(unsigned _txi, h256 _block) const;
virtual StateDiff diff(unsigned _txi, int _block) const; virtual StateDiff diff(unsigned _txi, BlockNumber _block) const;
/// Get a list of all active addresses. /// Get a list of all active addresses.
using Interface::addresses; using Interface::addresses;
virtual std::vector<Address> addresses(int _block) const; virtual std::vector<Address> addresses(BlockNumber _block) const;
/// Get the remaining gas limit in this block. /// Get the remaining gas limit in this block.
virtual u256 gasLimitRemaining() const { return m_postMine.gasLimitRemaining(); } virtual u256 gasLimitRemaining() const { return m_postMine.gasLimitRemaining(); }
@ -383,7 +383,8 @@ private:
/// 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. /// 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; unsigned numberOf(int _b) const;
State asOf(int _h) const; /// Returns the state object for the full block (i.e. the terminal state) for index _h.
/// Works properly with LatestBlock and PendingBlock.
State asOf(unsigned _h) const; State asOf(unsigned _h) const;
VersionChecker m_vc; ///< Dummy object to check & update the protocol version. VersionChecker m_vc; ///< Dummy object to check & update the protocol version.

1
libethereum/EthereumPeer.cpp

@ -48,6 +48,7 @@ EthereumPeer::EthereumPeer(Session* _s, HostCapabilityFace* _h, unsigned _i):
EthereumPeer::~EthereumPeer() EthereumPeer::~EthereumPeer()
{ {
clogS(NetMessageSummary) << "Aborting Sync :-(";
abortSync(); abortSync();
} }

22
libethereum/Interface.h

@ -73,16 +73,16 @@ public:
virtual void flushTransactions() = 0; virtual void flushTransactions() = 0;
/// Makes the given call. Nothing is recorded into the state. /// Makes the given call. Nothing is recorded into the state.
virtual ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, int _blockNumber = 0) = 0; virtual ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = 0) = 0;
/// Does the given creation. Nothing is recorded into the state. /// Does the given creation. Nothing is recorded into the state.
/// @returns the pair of the Address of the created contract together with its code. /// @returns the pair of the Address of the created contract together with its code.
virtual ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, int _blockNumber = 0) = 0; virtual ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, BlockNumber _blockNumber = 0) = 0;
// [STATE-QUERY API] // [STATE-QUERY API]
int getDefault() const { return m_default; } int getDefault() const { return m_default; }
void setDefault(int _block) { m_default = _block; } void setDefault(BlockNumber _block) { m_default = _block; }
u256 balanceAt(Address _a) const { return balanceAt(_a, m_default); } u256 balanceAt(Address _a) const { return balanceAt(_a, m_default); }
u256 countAt(Address _a) const { return countAt(_a, m_default); } u256 countAt(Address _a) const { return countAt(_a, m_default); }
@ -90,11 +90,11 @@ public:
bytes codeAt(Address _a) const { return codeAt(_a, m_default); } bytes codeAt(Address _a) const { return codeAt(_a, m_default); }
std::map<u256, u256> storageAt(Address _a) const { return storageAt(_a, m_default); } std::map<u256, u256> storageAt(Address _a) const { return storageAt(_a, m_default); }
virtual u256 balanceAt(Address _a, int _block) const = 0; virtual u256 balanceAt(Address _a, BlockNumber _block) const = 0;
virtual u256 countAt(Address _a, int _block) const = 0; virtual u256 countAt(Address _a, BlockNumber _block) const = 0;
virtual u256 stateAt(Address _a, u256 _l, int _block) const = 0; virtual u256 stateAt(Address _a, u256 _l, BlockNumber _block) const = 0;
virtual bytes codeAt(Address _a, int _block) const = 0; virtual bytes codeAt(Address _a, BlockNumber _block) const = 0;
virtual std::map<u256, u256> storageAt(Address _a, int _block) const = 0; virtual std::map<u256, u256> storageAt(Address _a, BlockNumber _block) const = 0;
// [LOGS API] // [LOGS API]
@ -135,11 +135,11 @@ public:
/// Differences between transactions. /// Differences between transactions.
StateDiff diff(unsigned _txi) const { return diff(_txi, m_default); } StateDiff diff(unsigned _txi) const { return diff(_txi, m_default); }
virtual StateDiff diff(unsigned _txi, h256 _block) const = 0; virtual StateDiff diff(unsigned _txi, h256 _block) const = 0;
virtual StateDiff diff(unsigned _txi, int _block) const = 0; virtual StateDiff diff(unsigned _txi, BlockNumber _block) const = 0;
/// Get a list of all active addresses. /// Get a list of all active addresses.
virtual Addresses addresses() const { return addresses(m_default); } virtual Addresses addresses() const { return addresses(m_default); }
virtual Addresses addresses(int _block) const = 0; virtual Addresses addresses(BlockNumber _block) const = 0;
/// Get the fee associated for a transaction with the given data. /// Get the fee associated for a transaction with the given data.
template <class T> static bigint txGas(T const& _data, u256 _gas = 0) { bigint ret = c_txGas + _gas; for (auto i: _data) ret += i ? c_txDataNonZeroGas : c_txDataZeroGas; return ret; } template <class T> static bigint txGas(T const& _data, u256 _gas = 0) { bigint ret = c_txGas + _gas; for (auto i: _data) ret += i ? c_txDataNonZeroGas : c_txDataZeroGas; return ret; }
@ -177,7 +177,7 @@ public:
virtual MineProgress miningProgress() const = 0; virtual MineProgress miningProgress() const = 0;
protected: protected:
int m_default = -1; int m_default = PendingBlock;
}; };
class Watch; class Watch;

27
libethereum/LogFilter.cpp

@ -46,6 +46,33 @@ h256 LogFilter::sha3() const
return dev::sha3(s.out()); return dev::sha3(s.out());
} }
static bool isNoLater(RelativeBlock _logBlockRelation, u256 _logBlockNumber, unsigned _latest)
{
if (_latest == PendingBlock)
return true;
else if (_latest == LatestBlock)
return _logBlockRelation == RelativeBlock::Latest;
else
return _logBlockNumber <= _latest;
}
static bool isNoEarlier(RelativeBlock _logBlockRelation, u256 _logBlockNumber, unsigned _earliest)
{
if (_earliest == PendingBlock)
return _logBlockRelation == RelativeBlock::Pending;
else if (_earliest == LatestBlock)
return true;
else
return _logBlockNumber >= _earliest;
}
bool LogFilter::envelops(RelativeBlock _logBlockRelation, u256 _logBlockNumber) const
{
return
isNoLater(_logBlockRelation, _logBlockNumber, m_latest) &&
isNoEarlier(_logBlockRelation, _logBlockNumber, m_earliest);
}
bool LogFilter::matches(LogBloom _bloom) const bool LogFilter::matches(LogBloom _bloom) const
{ {
if (m_addresses.size()) if (m_addresses.size())

11
libethereum/LogFilter.h

@ -45,14 +45,15 @@ class State;
class LogFilter class LogFilter
{ {
public: public:
LogFilter(int _earliest = 0, int _latest = -1): m_earliest(_earliest), m_latest(_latest) {} LogFilter(unsigned _earliest = 0, unsigned _latest = PendingBlock): m_earliest(_earliest), m_latest(_latest) {}
void streamRLP(RLPStream& _s) const; void streamRLP(RLPStream& _s) const;
h256 sha3() const; h256 sha3() const;
int earliest() const { return m_earliest; } unsigned earliest() const { return m_earliest; }
int latest() const { return m_latest; } unsigned latest() const { return m_latest; }
bool envelops(RelativeBlock _logBlockRelation, u256 _logBlockNumber) const;
std::vector<LogBloom> bloomPossibilities() const; std::vector<LogBloom> bloomPossibilities() const;
bool matches(LogBloom _bloom) const; bool matches(LogBloom _bloom) const;
bool matches(State const& _s, unsigned _i) const; bool matches(State const& _s, unsigned _i) const;
@ -68,8 +69,8 @@ public:
private: private:
AddressSet m_addresses; AddressSet m_addresses;
std::array<h256Set, 4> m_topics; std::array<h256Set, 4> m_topics;
int m_earliest = 0; unsigned m_earliest = 0;
int m_latest = -1; unsigned m_latest = LatestBlock;
}; };
} }

2
libethereum/State.cpp

@ -1040,7 +1040,7 @@ LastHashes State::getLastHashes(BlockChain const& _bc, unsigned _n) const
{ {
LastHashes ret; LastHashes ret;
ret.resize(256); ret.resize(256);
if (c_protocolVersion > 49) if (eth::c_protocolVersion > 49)
{ {
ret[0] = _bc.numberHash(_n); ret[0] = _bc.numberHash(_n);
for (unsigned i = 1; i < 256; ++i) for (unsigned i = 1; i < 256; ++i)

2
libethereum/Transaction.cpp

@ -46,6 +46,8 @@ TransactionException dev::eth::toTransactionException(VMException const& _e)
return TransactionException::BadJumpDestination; return TransactionException::BadJumpDestination;
if (!!dynamic_cast<OutOfGas const*>(&_e)) if (!!dynamic_cast<OutOfGas const*>(&_e))
return TransactionException::OutOfGas; return TransactionException::OutOfGas;
if (!!dynamic_cast<OutOfStack const*>(&_e))
return TransactionException::OutOfStack;
if (!!dynamic_cast<StackUnderflow const*>(&_e)) if (!!dynamic_cast<StackUnderflow const*>(&_e))
return TransactionException::StackUnderflow; return TransactionException::StackUnderflow;
return TransactionException::Unknown; return TransactionException::Unknown;

3
libethereum/Transaction.h

@ -56,6 +56,7 @@ enum class TransactionException
BadInstruction, BadInstruction,
BadJumpDestination, BadJumpDestination,
OutOfGas, ///< Ran out of gas executing code of the transaction. OutOfGas, ///< Ran out of gas executing code of the transaction.
OutOfStack, ///< Ran out of stack executing code of the transaction.
StackUnderflow StackUnderflow
}; };
@ -66,7 +67,7 @@ enum class CodeDeposit
Success Success
}; };
class VMException; struct VMException;
TransactionException toTransactionException(VMException const& _e); TransactionException toTransactionException(VMException const& _e);

4
libevm/ExtVMFace.h

@ -63,10 +63,10 @@ using LogEntries = std::vector<LogEntry>;
struct LocalisedLogEntry: public LogEntry struct LocalisedLogEntry: public LogEntry
{ {
LocalisedLogEntry() {} LocalisedLogEntry() {}
LocalisedLogEntry(LogEntry const& _le, unsigned _number, h256 _sha3 = {}): LogEntry(_le), number(_number), sha3(_sha3) {} LocalisedLogEntry(LogEntry const& _le, unsigned _number, h256 _transactionHash = h256()): LogEntry(_le), number(_number), transactionHash(_transactionHash) {}
unsigned number = 0; unsigned number = 0;
h256 sha3; h256 transactionHash;
}; };
using LocalisedLogEntries = std::vector<LocalisedLogEntry>; using LocalisedLogEntries = std::vector<LocalisedLogEntry>;

6
libevm/VM.cpp

@ -36,6 +36,7 @@ struct InstructionMetric
{ {
int gasPriceTier; int gasPriceTier;
int args; int args;
int ret;
}; };
static array<InstructionMetric, 256> metrics() static array<InstructionMetric, 256> metrics()
@ -46,12 +47,15 @@ static array<InstructionMetric, 256> metrics()
InstructionInfo inst = instructionInfo((Instruction)i); InstructionInfo inst = instructionInfo((Instruction)i);
s_ret[i].gasPriceTier = inst.gasPriceTier; s_ret[i].gasPriceTier = inst.gasPriceTier;
s_ret[i].args = inst.args; s_ret[i].args = inst.args;
s_ret[i].ret = inst.ret;
} }
return s_ret; return s_ret;
} }
bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
{ {
m_stack.reserve((unsigned)c_stackLimit);
static const array<InstructionMetric, 256> c_metrics = metrics(); static const array<InstructionMetric, 256> c_metrics = metrics();
auto memNeed = [](u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; }; auto memNeed = [](u256 _offset, dev::u256 _size) { return _size ? (bigint)_offset + _size : (bigint)0; };
@ -89,7 +93,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
// should work, but just seems to result in immediate errorless exit on initial execution. yeah. weird. // should work, but just seems to result in immediate errorless exit on initial execution. yeah. weird.
//m_onFail = std::function<void()>(onOperation); //m_onFail = std::function<void()>(onOperation);
require(metric.args); require(metric.args, metric.ret);
auto onOperation = [&]() auto onOperation = [&]()
{ {

2
libevm/VM.h

@ -56,7 +56,7 @@ public:
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
void require(u256 _n) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_n, (bigint)m_stack.size())); } } void require(u256 _n, u256 _d) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_n, (bigint)m_stack.size())); } if (m_stack.size() - _n + _d >= c_stackLimit) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(OutOfStack() << RequirementError((bigint)(_d - _n), (bigint)m_stack.size())); } }
void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } } void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } }
u256 curPC() const { return m_curPC; } u256 curPC() const { return m_curPC; }

1
libevm/VMFace.h

@ -31,6 +31,7 @@ struct BreakPointHit: virtual VMException {};
struct BadInstruction: virtual VMException {}; struct BadInstruction: virtual VMException {};
struct BadJumpDestination: virtual VMException {}; struct BadJumpDestination: virtual VMException {};
struct OutOfGas: virtual VMException {}; struct OutOfGas: virtual VMException {};
struct OutOfStack: virtual VMException {};
struct StackUnderflow: virtual VMException {}; struct StackUnderflow: virtual VMException {};
/// EVM Virtual Machine interface /// EVM Virtual Machine interface

31
libjsqrc/ethereumjs/dist/ethereum.js

@ -1527,7 +1527,7 @@ var addEventsToContract = function (contract, desc, address) {
var parser = eventImpl.outputParser(e); var parser = eventImpl.outputParser(e);
return parser(data); return parser(data);
}; };
return web3.eth.filter(o, undefined, undefined, outputFormatter); return web3.eth.filter(o, undefined, undefined, outputFormatter);
}; };
// this property should be used by eth.filter to check if object is an event // this property should be used by eth.filter to check if object is an event
@ -2005,16 +2005,6 @@ var getOptions = function (options) {
options.toBlock = options.latest; options.toBlock = options.latest;
} }
if (options.skip) {
console.warn('"skip" is deprecated, is "offset" instead');
options.offset = options.skip;
}
if (options.max) {
console.warn('"max" is deprecated, is "limit" instead');
options.limit = options.max;
}
// make sure topics, get converted to hex // make sure topics, get converted to hex
if(options.topics instanceof Array) { if(options.topics instanceof Array) {
options.topics = options.topics.map(function(topic){ options.topics = options.topics.map(function(topic){
@ -2022,13 +2012,18 @@ var getOptions = function (options) {
}); });
} }
var asBlockNumber = function(n) {
if (n == null)
return null;
if (n == 'latest' || n == 'pending')
return n;
return utils.toHex(n);
};
// evaluate lazy properties // evaluate lazy properties
return { return {
fromBlock: utils.toHex(options.fromBlock), fromBlock: asBlockNumber(options.fromBlock),
toBlock: utils.toHex(options.toBlock), toBlock: asBlockNumber(options.toBlock),
limit: utils.toHex(options.limit),
offset: utils.toHex(options.offset),
to: options.to, to: options.to,
address: options.address, address: options.address,
topics: options.topics topics: options.topics
@ -2053,9 +2048,9 @@ var filter = function(options, implementation, formatter) {
// call the callbacks // call the callbacks
var onMessages = function (messages) { var onMessages = function (messages) {
messages.forEach(function (message) { messages.forEach(function (message) {
message = formatter ? formatter(message) : message; message = formatter ? formatter(message) : message;
callbacks.forEach(function (callback) { callbacks.forEach(function (callback) {
callback(message); callback(message);
}); });
}); });
}; };
@ -2855,4 +2850,4 @@ module.exports = web3;
},{"./lib/solidity/abi":1,"./lib/web3":6,"./lib/web3/contract":7,"./lib/web3/httpprovider":13,"./lib/web3/qtsync":16}]},{},["web3"]) },{"./lib/solidity/abi":1,"./lib/web3":6,"./lib/web3/contract":7,"./lib/web3/httpprovider":13,"./lib/web3/qtsync":16}]},{},["web3"])
//# sourceMappingURL=ethereum.js.map //# sourceMappingURL=ethereum.js.map

8
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

2
libjsqrc/ethereumjs/lib/web3/contract.js

@ -159,7 +159,7 @@ var addEventsToContract = function (contract, desc, address) {
var parser = eventImpl.outputParser(e); var parser = eventImpl.outputParser(e);
return parser(data); return parser(data);
}; };
return web3.eth.filter(o, undefined, undefined, outputFormatter); return web3.eth.filter(o, undefined, undefined, outputFormatter);
}; };
// this property should be used by eth.filter to check if object is an event // this property should be used by eth.filter to check if object is an event

27
libjsqrc/ethereumjs/lib/web3/filter.js

@ -64,16 +64,6 @@ var getOptions = function (options) {
options.toBlock = options.latest; options.toBlock = options.latest;
} }
if (options.skip) {
console.warn('"skip" is deprecated, is "offset" instead');
options.offset = options.skip;
}
if (options.max) {
console.warn('"max" is deprecated, is "limit" instead');
options.limit = options.max;
}
// make sure topics, get converted to hex // make sure topics, get converted to hex
if(options.topics instanceof Array) { if(options.topics instanceof Array) {
options.topics = options.topics.map(function(topic){ options.topics = options.topics.map(function(topic){
@ -81,13 +71,18 @@ var getOptions = function (options) {
}); });
} }
var asBlockNumber = function(n) {
if (n == null)
return null;
if (n == 'latest' || n == 'pending')
return n;
return utils.toHex(n);
};
// evaluate lazy properties // evaluate lazy properties
return { return {
fromBlock: utils.toHex(options.fromBlock), fromBlock: asBlockNumber(options.fromBlock),
toBlock: utils.toHex(options.toBlock), toBlock: asBlockNumber(options.toBlock),
limit: utils.toHex(options.limit),
offset: utils.toHex(options.offset),
to: options.to, to: options.to,
address: options.address, address: options.address,
topics: options.topics topics: options.topics
@ -112,9 +107,9 @@ var filter = function(options, implementation, formatter) {
// call the callbacks // call the callbacks
var onMessages = function (messages) { var onMessages = function (messages) {
messages.forEach(function (message) { messages.forEach(function (message) {
message = formatter ? formatter(message) : message; message = formatter ? formatter(message) : message;
callbacks.forEach(function (callback) { callbacks.forEach(function (callback) {
callback(message); callback(message);
}); });
}); });
}; };

2
libp2p/Common.cpp

@ -24,6 +24,8 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
const unsigned dev::p2p::c_protocolVersion = 3;
// Helper function to determine if an address falls within one of the reserved ranges // Helper function to determine if an address falls within one of the reserved ranges
// For V4: // For V4:
// Class A "10.*", Class B "172.[16->31].*", Class C "192.168.*" // Class A "10.*", Class B "172.[16->31].*", Class C "192.168.*"

3
libp2p/Common.h

@ -48,6 +48,9 @@ class RLPStream;
namespace p2p namespace p2p
{ {
/// Peer network protocol version.
extern const unsigned c_protocolVersion;
using NodeId = h512; using NodeId = h512;
bool isPrivateAddress(bi::address const& _addressToCheck); bool isPrivateAddress(bi::address const& _addressToCheck);

32
libp2p/Host.cpp

@ -42,6 +42,12 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace dev::p2p; using namespace dev::p2p;
/// Interval at which Host::run will call keepAlivePeers to ping peers.
std::chrono::seconds const c_keepAliveInterval = std::chrono::seconds(30);
/// Disconnect timeout after failure to respond to keepAlivePeers ping.
std::chrono::milliseconds const c_keepAliveTimeOut = std::chrono::milliseconds(1000);
HostNodeTableHandler::HostNodeTableHandler(Host& _host): m_host(_host) {} HostNodeTableHandler::HostNodeTableHandler(Host& _host): m_host(_host) {}
void HostNodeTableHandler::processEvent(NodeId const& _n, NodeTableEventType const& _e) void HostNodeTableHandler::processEvent(NodeId const& _n, NodeTableEventType const& _e)
@ -170,11 +176,6 @@ void Host::doneWorking()
m_sessions.clear(); m_sessions.clear();
} }
unsigned Host::protocolVersion() const
{
return 3;
}
void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint) void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint)
{ {
shared_ptr<Peer> p; shared_ptr<Peer> p;
@ -205,7 +206,7 @@ void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io
// create session so disconnects are managed // create session so disconnects are managed
auto ps = make_shared<Session>(this, _io, p, PeerSessionInfo({_id, clientVersion, _endpoint.address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[2].toSet<CapDesc>(), 0, map<string, string>()})); auto ps = make_shared<Session>(this, _io, p, PeerSessionInfo({_id, clientVersion, _endpoint.address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[2].toSet<CapDesc>(), 0, map<string, string>()}));
if (protocolVersion != this->protocolVersion()) if (protocolVersion != dev::p2p::c_protocolVersion)
{ {
ps->disconnect(IncompatibleProtocol); ps->disconnect(IncompatibleProtocol);
return; return;
@ -260,7 +261,7 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e)
p->required = n.required; p->required = n.required;
m_peers[_n] = p; m_peers[_n] = p;
clog(NetNote) << "p2p.host.peers.events.peersAdded " << _n << p->endpoint.tcp.address() << p->endpoint.udp.address(); clog(NetNote) << "p2p.host.peers.events.peersAdded " << _n << "udp:" << p->endpoint.udp.address() << "tcp:" << p->endpoint.tcp.address();
} }
p->endpoint.tcp = n.endpoint.tcp; p->endpoint.tcp = n.endpoint.tcp;
} }
@ -474,6 +475,7 @@ void Host::connect(std::shared_ptr<Peer> const& _p)
clog(NetConnect) << "Connection refused to node" << _p->id.abridged() << "@" << _p->peerEndpoint() << "(" << ec.message() << ")"; clog(NetConnect) << "Connection refused to node" << _p->id.abridged() << "@" << _p->peerEndpoint() << "(" << ec.message() << ")";
_p->m_lastDisconnect = TCPError; _p->m_lastDisconnect = TCPError;
_p->m_lastAttempted = std::chrono::system_clock::now(); _p->m_lastAttempted = std::chrono::system_clock::now();
_p->m_failedAttempts++;
} }
else else
{ {
@ -609,8 +611,7 @@ void Host::startedWorking()
else else
clog(NetNote) << "p2p.start.notice id:" << id().abridged() << "Listen port is invalid or unavailable. Node Table using default port (30303)."; clog(NetNote) << "p2p.start.notice id:" << id().abridged() << "Listen port is invalid or unavailable. Node Table using default port (30303).";
// TODO: add m_tcpPublic endpoint; sort out endpoint stuff for nodetable m_nodeTable.reset(new NodeTable(m_ioService, m_alias, bi::address::from_string(listenAddress()), listenPort() > 0 ? listenPort() : 30303));
m_nodeTable.reset(new NodeTable(m_ioService, m_alias, m_listenPort > 0 ? m_listenPort : 30303));
m_nodeTable->setEventHandler(new HostNodeTableHandler(*this)); m_nodeTable->setEventHandler(new HostNodeTableHandler(*this));
restoreNetwork(&m_restoreNetwork); restoreNetwork(&m_restoreNetwork);
@ -674,7 +675,7 @@ bytes Host::saveNetwork() const
// TODO: alpha: Figure out why it ever shares these ports.//p.address.port() >= 30300 && p.address.port() <= 30305 && // TODO: alpha: Figure out why it ever shares these ports.//p.address.port() >= 30300 && p.address.port() <= 30305 &&
// TODO: alpha: if/how to save private addresses // TODO: alpha: if/how to save private addresses
// Only save peers which have connected within 2 days, with properly-advertised port and public IP address // Only save peers which have connected within 2 days, with properly-advertised port and public IP address
if (chrono::system_clock::now() - p.m_lastConnected < chrono::seconds(3600 * 48) && p.peerEndpoint().port() > 0 && p.peerEndpoint().port() < /*49152*/32768 && p.id != id() && !isPrivateAddress(p.peerEndpoint().address())) if (chrono::system_clock::now() - p.m_lastConnected < chrono::seconds(3600 * 48) && p.peerEndpoint().port() > 0 && p.peerEndpoint().port() < /*49152*/32768 && p.id != id() && !isPrivateAddress(p.endpoint.udp.address()) && !isPrivateAddress(p.endpoint.tcp.address()))
{ {
network.appendList(10); network.appendList(10);
if (p.peerEndpoint().address().is_v4()) if (p.peerEndpoint().address().is_v4())
@ -708,7 +709,7 @@ bytes Host::saveNetwork() const
} }
RLPStream ret(3); RLPStream ret(3);
ret << 1 << m_alias.secret(); ret << dev::p2p::c_protocolVersion << m_alias.secret();
ret.appendList(count).appendRaw(network.out(), count); ret.appendList(count).appendRaw(network.out(), count);
return ret.out(); return ret.out();
} }
@ -721,7 +722,7 @@ void Host::restoreNetwork(bytesConstRef _b)
RecursiveGuard l(x_sessions); RecursiveGuard l(x_sessions);
RLP r(_b); RLP r(_b);
if (r.itemCount() > 0 && r[0].isInt() && r[0].toInt<int>() == 1) if (r.itemCount() > 0 && r[0].isInt() && r[0].toInt<unsigned>() == dev::p2p::c_protocolVersion)
{ {
// r[0] = version // r[0] = version
// r[1] = key // r[1] = key
@ -741,6 +742,13 @@ void Host::restoreNetwork(bytesConstRef _b)
tcp = bi::tcp::endpoint(bi::address_v6(i[0].toArray<byte, 16>()), i[1].toInt<short>()); tcp = bi::tcp::endpoint(bi::address_v6(i[0].toArray<byte, 16>()), i[1].toInt<short>());
udp = bi::udp::endpoint(bi::address_v6(i[0].toArray<byte, 16>()), i[1].toInt<short>()); udp = bi::udp::endpoint(bi::address_v6(i[0].toArray<byte, 16>()), i[1].toInt<short>());
} }
// skip private addresses
// todo: to support private addresseses entries must be stored
// and managed externally by host rather than nodetable.
if (isPrivateAddress(tcp.address()) || isPrivateAddress(udp.address()))
continue;
auto id = (NodeId)i[2]; auto id = (NodeId)i[2];
if (i.itemCount() == 3) if (i.itemCount() == 3)
m_nodeTable->addNode(id, udp, tcp); m_nodeTable->addNode(id, udp, tcp);

9
libp2p/Host.h

@ -94,18 +94,9 @@ public:
/// Will block on network process events. /// Will block on network process events.
virtual ~Host(); virtual ~Host();
/// Interval at which Host::run will call keepAlivePeers to ping peers.
std::chrono::seconds const c_keepAliveInterval = std::chrono::seconds(30);
/// Disconnect timeout after failure to respond to keepAlivePeers ping.
std::chrono::milliseconds const c_keepAliveTimeOut = std::chrono::milliseconds(1000);
/// Default host for current version of client. /// Default host for current version of client.
static std::string pocHost(); static std::string pocHost();
/// Basic peer network protocol version.
unsigned protocolVersion() const;
/// Register a peer-capability; all new peer connections will have this capability. /// Register a peer-capability; all new peer connections will have this capability.
template <class T> std::shared_ptr<T> registerCapability(T* _t) { _t->m_host = this; auto ret = std::shared_ptr<T>(_t); m_capabilities[std::make_pair(T::staticName(), T::staticVersion())] = ret; return ret; } template <class T> std::shared_ptr<T> registerCapability(T* _t) { _t->m_host = this; auto ret = std::shared_ptr<T>(_t); m_capabilities[std::make_pair(T::staticName(), T::staticVersion())] = ret; return ret; }

31
libp2p/NodeTable.cpp

@ -27,11 +27,11 @@ using namespace dev::p2p;
NodeEntry::NodeEntry(Node _src, Public _pubk, NodeIPEndpoint _gw): Node(_pubk, _gw), distance(NodeTable::distance(_src.id,_pubk)) {} NodeEntry::NodeEntry(Node _src, Public _pubk, NodeIPEndpoint _gw): Node(_pubk, _gw), distance(NodeTable::distance(_src.id,_pubk)) {}
NodeEntry::NodeEntry(Node _src, Public _pubk, bi::udp::endpoint _udp): Node(_pubk, NodeIPEndpoint(_udp)), distance(NodeTable::distance(_src.id,_pubk)) {} NodeEntry::NodeEntry(Node _src, Public _pubk, bi::udp::endpoint _udp): Node(_pubk, NodeIPEndpoint(_udp)), distance(NodeTable::distance(_src.id,_pubk)) {}
NodeTable::NodeTable(ba::io_service& _io, KeyPair _alias, uint16_t _udp): NodeTable::NodeTable(ba::io_service& _io, KeyPair _alias, bi::address const& _udpAddress, uint16_t _udp):
m_node(Node(_alias.pub(), bi::udp::endpoint())), m_node(Node(_alias.pub(), bi::udp::endpoint(_udpAddress, _udp))),
m_secret(_alias.sec()), m_secret(_alias.sec()),
m_io(_io), m_io(_io),
m_socket(new NodeSocket(m_io, *this, _udp)), m_socket(new NodeSocket(m_io, *this, m_node.endpoint.udp)),
m_socketPointer(m_socket.get()), m_socketPointer(m_socket.get()),
m_bucketRefreshTimer(m_io), m_bucketRefreshTimer(m_io),
m_evictionCheckTimer(m_io) m_evictionCheckTimer(m_io)
@ -70,6 +70,20 @@ shared_ptr<NodeEntry> NodeTable::addNode(Public const& _pubk, bi::udp::endpoint
shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node) shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node)
{ {
if (_node.endpoint.udp.address().to_string() == "0.0.0.0" || _node.endpoint.tcp.address().to_string() == "0.0.0.0")
{
string ptype;
if (_node.endpoint.udp.address().to_string() != "0.0.0.0")
ptype = "TCP";
else if (_node.endpoint.tcp.address().to_string() != "0.0.0.0")
ptype = "UDP";
else
ptype = "TCP,UDP";
clog(NodeTableWarn) << "addNode Failed. Invalid" << ptype << "address 0.0.0.0 for" << _node.id.abridged();
return move(shared_ptr<NodeEntry>());
}
// ping address if nodeid is empty // ping address if nodeid is empty
if (!_node.id) if (!_node.id)
{ {
@ -326,7 +340,7 @@ void NodeTable::noteActiveNode(Public const& _pubk, bi::udp::endpoint const& _en
s.nodes.push_back(node); s.nodes.push_back(node);
s.touch(); s.touch();
if (!removed) if (!removed && m_nodeEventHandler)
m_nodeEventHandler->appendEvent(node->id, NodeEntryAdded); m_nodeEventHandler->appendEvent(node->id, NodeEntryAdded);
} }
} }
@ -335,7 +349,7 @@ void NodeTable::noteActiveNode(Public const& _pubk, bi::udp::endpoint const& _en
s.nodes.push_back(node); s.nodes.push_back(node);
s.touch(); s.touch();
if (!removed) if (!removed && m_nodeEventHandler)
m_nodeEventHandler->appendEvent(node->id, NodeEntryAdded); m_nodeEventHandler->appendEvent(node->id, NodeEntryAdded);
} }
} }
@ -463,6 +477,13 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
case PingNode::type: case PingNode::type:
{ {
PingNode in = PingNode::fromBytesConstRef(_from, rlpBytes); PingNode in = PingNode::fromBytesConstRef(_from, rlpBytes);
if (in.version != dev::p2p::c_protocolVersion)
{
if (auto n = nodeEntry(nodeid))
dropNode(n);
return;
}
addNode(nodeid, _from, bi::tcp::endpoint(bi::address::from_string(in.ipAddress), in.port)); addNode(nodeid, _from, bi::tcp::endpoint(bi::address::from_string(in.ipAddress), in.port));
Pong p(_from); Pong p(_from);

5
libp2p/NodeTable.h

@ -135,7 +135,8 @@ class NodeTable: UDPSocketEvents, public std::enable_shared_from_this<NodeTable>
using EvictionTimeout = std::pair<std::pair<NodeId, TimePoint>, NodeId>; ///< First NodeId may be evicted and replaced with second NodeId. using EvictionTimeout = std::pair<std::pair<NodeId, TimePoint>, NodeId>; ///< First NodeId may be evicted and replaced with second NodeId.
public: public:
NodeTable(ba::io_service& _io, KeyPair _alias, uint16_t _udpPort = 30303); /// Constructor requiring host for I/O, credentials, and IP Address and port to listen on.
NodeTable(ba::io_service& _io, KeyPair _alias, bi::address const& _udpAddress, uint16_t _udpPort = 30303);
~NodeTable(); ~NodeTable();
/// Returns distance based on xor metric two node ids. Used by NodeEntry and NodeTable. /// Returns distance based on xor metric two node ids. Used by NodeEntry and NodeTable.
@ -315,7 +316,7 @@ struct PingNode: RLPXDatagram<PingNode>
static const uint8_t type = 1; static const uint8_t type = 1;
unsigned version = 1; unsigned version = dev::p2p::c_protocolVersion;
std::string ipAddress; std::string ipAddress;
unsigned port; unsigned port;
unsigned expiration; unsigned expiration;

14
libp2p/RLPxHandshake.cpp

@ -139,7 +139,7 @@ void RLPXHandshake::error()
void RLPXHandshake::transition(boost::system::error_code _ech) void RLPXHandshake::transition(boost::system::error_code _ech)
{ {
if (_ech || m_nextState == Error) if (_ech || m_nextState == Error || m_cancel)
return error(); return error();
auto self(shared_from_this()); auto self(shared_from_this());
@ -172,7 +172,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
// 5 arguments, HelloPacket // 5 arguments, HelloPacket
RLPStream s; RLPStream s;
s.append((unsigned)0).appendList(5) s.append((unsigned)0).appendList(5)
<< m_host->protocolVersion() << dev::p2p::c_protocolVersion
<< m_host->m_clientVersion << m_host->m_clientVersion
<< m_host->caps() << m_host->caps()
<< m_host->listenPort() << m_host->listenPort()
@ -259,4 +259,14 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
} }
}); });
} }
m_idleTimer.expires_from_now(c_timeout);
m_idleTimer.async_wait([this, self](boost::system::error_code const& _ec)
{
if (!_ec)
{
clog(NetWarn) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Timeout)";
cancel();
}
});
} }

14
libp2p/RLPxHandshake.h

@ -62,17 +62,18 @@ class RLPXHandshake: public std::enable_shared_from_this<RLPXHandshake>
public: public:
/// Setup incoming connection. /// Setup incoming connection.
RLPXHandshake(Host* _host, std::shared_ptr<RLPXSocket> const& _socket): m_host(_host), m_originated(false), m_socket(_socket) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); } RLPXHandshake(Host* _host, std::shared_ptr<RLPXSocket> const& _socket): m_host(_host), m_originated(false), m_socket(_socket), m_idleTimer(m_socket->ref().get_io_service()) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); }
/// Setup outbound connection. /// Setup outbound connection.
RLPXHandshake(Host* _host, std::shared_ptr<RLPXSocket> const& _socket, NodeId _remote): m_host(_host), m_remote(_remote), m_originated(true), m_socket(_socket) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); } RLPXHandshake(Host* _host, std::shared_ptr<RLPXSocket> const& _socket, NodeId _remote): m_host(_host), m_remote(_remote), m_originated(true), m_socket(_socket), m_idleTimer(m_socket->ref().get_io_service()) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); }
~RLPXHandshake() {} ~RLPXHandshake() {}
/// Start handshake. /// Start handshake.
void start() { transition(); } void start() { transition(); }
void cancel() { m_nextState = Error; } /// Cancels handshake preventing
void cancel() { m_cancel = true; }
protected: protected:
/// Write Auth message to socket and transitions to AckAuth. /// Write Auth message to socket and transitions to AckAuth.
@ -93,7 +94,11 @@ protected:
/// Performs transition for m_nextState. /// Performs transition for m_nextState.
void transition(boost::system::error_code _ech = boost::system::error_code()); void transition(boost::system::error_code _ech = boost::system::error_code());
/// Timeout for remote to respond to transition events. Enforced by m_idleTimer and refreshed by transition().
boost::posix_time::milliseconds const c_timeout = boost::posix_time::milliseconds(1000);
State m_nextState = New; ///< Current or expected state of transition. State m_nextState = New; ///< Current or expected state of transition.
bool m_cancel = false; ///< Will be set to true if connection was canceled.
Host* m_host; ///< Host which provides m_alias, protocolVersion(), m_clientVersion, caps(), and TCP listenPort(). Host* m_host; ///< Host which provides m_alias, protocolVersion(), m_clientVersion, caps(), and TCP listenPort().
@ -119,7 +124,8 @@ protected:
/// Passed onto Host which will take ownership. /// Passed onto Host which will take ownership.
RLPXFrameIO* m_io = nullptr; RLPXFrameIO* m_io = nullptr;
std::shared_ptr<RLPXSocket> m_socket; ///< Socket. std::shared_ptr<RLPXSocket> m_socket; ///< Socket.
boost::asio::deadline_timer m_idleTimer; ///< Timer which enforces c_timeout.
}; };
} }

1
libp2p/Session.cpp

@ -51,6 +51,7 @@ Session::Session(Host* _s, RLPXFrameIO* _io, std::shared_ptr<Peer> const& _n, Pe
Session::~Session() Session::~Session()
{ {
clogS(NetMessageSummary) << "Closing Peer Session :-(";
m_peer->m_lastConnected = m_peer->m_lastAttempted - chrono::seconds(1); m_peer->m_lastConnected = m_peer->m_lastAttempted - chrono::seconds(1);
// Read-chain finished for one reason or another. // Read-chain finished for one reason or another.

4
libp2p/UDP.h

@ -113,6 +113,10 @@ public:
enum { maxDatagramSize = MaxDatagramSize }; enum { maxDatagramSize = MaxDatagramSize };
static_assert(maxDatagramSize < 65507, "UDP datagrams cannot be larger than 65507 bytes"); static_assert(maxDatagramSize < 65507, "UDP datagrams cannot be larger than 65507 bytes");
/// Create socket for specific endpoint.
UDPSocket(ba::io_service& _io, UDPSocketEvents& _host, bi::udp::endpoint _endpoint): m_host(_host), m_endpoint(_endpoint), m_socket(_io) { m_started.store(false); m_closed.store(true); };
/// Create socket which listens to all ports.
UDPSocket(ba::io_service& _io, UDPSocketEvents& _host, unsigned _port): m_host(_host), m_endpoint(bi::udp::v4(), _port), m_socket(_io) { m_started.store(false); m_closed.store(true); }; UDPSocket(ba::io_service& _io, UDPSocketEvents& _host, unsigned _port): m_host(_host), m_endpoint(bi::udp::v4(), _port), m_socket(_io) { m_started.store(false); m_closed.store(true); };
virtual ~UDPSocket() { disconnect(); } virtual ~UDPSocket() { disconnect(); }

207
libsolidity/ArrayUtils.cpp

@ -52,15 +52,21 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// TODO unroll loop for small sizes // TODO unroll loop for small sizes
bool sourceIsStorage = _sourceType.getLocation() == ArrayType::Location::Storage; bool sourceIsStorage = _sourceType.getLocation() == ArrayType::Location::Storage;
bool directCopy = sourceIsStorage && sourceBaseType->isValueType() && *sourceBaseType == *targetBaseType;
bool haveByteOffsetSource = !directCopy && sourceIsStorage && sourceBaseType->getStorageBytes() <= 16;
bool haveByteOffsetTarget = !directCopy && targetBaseType->getStorageBytes() <= 16;
unsigned byteOffsetSize = (haveByteOffsetSource ? 1 : 0) + (haveByteOffsetTarget ? 1 : 0);
// stack: source_ref [source_byte_off] [source_length] target_ref target_byte_off // stack: source_ref [source_byte_off] [source_length] target_ref target_byte_off
// store target_ref // store target_ref
m_context << eth::Instruction::POP; //@todo // arrays always start at zero byte offset, pop offset
m_context << eth::Instruction::POP;
for (unsigned i = _sourceType.getSizeOnStack(); i > 0; --i) for (unsigned i = _sourceType.getSizeOnStack(); i > 0; --i)
m_context << eth::swapInstruction(i); m_context << eth::swapInstruction(i);
// stack: target_ref source_ref [source_byte_off] [source_length] // stack: target_ref source_ref [source_byte_off] [source_length]
if (sourceIsStorage) if (sourceIsStorage)
m_context << eth::Instruction::POP; //@todo // arrays always start at zero byte offset, pop offset
m_context << eth::Instruction::POP;
// stack: target_ref source_ref [source_length] // stack: target_ref source_ref [source_length]
// retrieve source length // retrieve source length
if (_sourceType.getLocation() != ArrayType::Location::CallData || !_sourceType.isDynamicallySized()) if (_sourceType.getLocation() != ArrayType::Location::CallData || !_sourceType.isDynamicallySized())
@ -81,7 +87,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
m_context m_context
<< eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP
<< eth::Instruction::POP << eth::Instruction::POP; << eth::Instruction::POP << eth::Instruction::POP;
m_context << u256(0); //@todo m_context << u256(0);
return; return;
} }
// compute hashes (data positions) // compute hashes (data positions)
@ -97,8 +103,8 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// stack: target_ref target_data_end source_length target_data_pos source_ref // stack: target_ref target_data_end source_length target_data_pos source_ref
// skip copying if source length is zero // skip copying if source length is zero
m_context << eth::Instruction::DUP3 << eth::Instruction::ISZERO; m_context << eth::Instruction::DUP3 << eth::Instruction::ISZERO;
eth::AssemblyItem copyLoopEnd = m_context.newTag(); eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag();
m_context.appendConditionalJumpTo(copyLoopEnd); m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset);
if (_sourceType.getLocation() == ArrayType::Location::Storage && _sourceType.isDynamicallySized()) if (_sourceType.getLocation() == ArrayType::Location::Storage && _sourceType.isDynamicallySized())
CompilerUtils(m_context).computeHashStatic(); CompilerUtils(m_context).computeHashStatic();
@ -107,18 +113,24 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
convertLengthToSize(_sourceType); convertLengthToSize(_sourceType);
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD; m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end
if (haveByteOffsetTarget)
m_context << u256(0);
if (haveByteOffsetSource)
m_context << u256(0);
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
eth::AssemblyItem copyLoopStart = m_context.newTag(); eth::AssemblyItem copyLoopStart = m_context.newTag();
m_context << copyLoopStart; m_context << copyLoopStart;
// check for loop condition // check for loop condition
m_context m_context
<< eth::Instruction::DUP3 << eth::Instruction::DUP2 << eth::dupInstruction(3 + byteOffsetSize) << eth::dupInstruction(2 + byteOffsetSize)
<< eth::Instruction::GT << eth::Instruction::ISZERO; << eth::Instruction::GT << eth::Instruction::ISZERO;
eth::AssemblyItem copyLoopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(copyLoopEnd); m_context.appendConditionalJumpTo(copyLoopEnd);
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
// copy // copy
if (sourceBaseType->getCategory() == Type::Category::Array) if (sourceBaseType->getCategory() == Type::Category::Array)
{ {
//@todo solAssert(byteOffsetSize == 0, "Byte offset for array as base type.");
m_context << eth::Instruction::DUP3; m_context << eth::Instruction::DUP3;
if (sourceIsStorage) if (sourceIsStorage)
m_context << u256(0); m_context << u256(0);
@ -129,36 +141,80 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
); );
m_context << eth::Instruction::POP << eth::Instruction::POP; m_context << eth::Instruction::POP << eth::Instruction::POP;
} }
else if (directCopy)
{
solAssert(byteOffsetSize == 0, "Byte offset for direct copy.");
m_context
<< eth::Instruction::DUP3 << eth::Instruction::SLOAD
<< eth::Instruction::DUP3 << eth::Instruction::SSTORE;
}
else else
{ {
m_context << eth::Instruction::DUP3; // Note that we have to copy each element on its own in case conversion is involved.
// We might copy too much if there is padding at the last element, but this way end
// checking is easier.
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
m_context << eth::dupInstruction(3 + byteOffsetSize);
if (_sourceType.getLocation() == ArrayType::Location::Storage) if (_sourceType.getLocation() == ArrayType::Location::Storage)
{ {
m_context << u256(0); if (haveByteOffsetSource)
m_context << eth::Instruction::DUP2;
else
m_context << u256(0);
StorageItem(m_context, *sourceBaseType).retrieveValue(SourceLocation(), true); StorageItem(m_context, *sourceBaseType).retrieveValue(SourceLocation(), true);
} }
else if (sourceBaseType->isValueType()) else if (sourceBaseType->isValueType())
CompilerUtils(m_context).loadFromMemoryDynamic(*sourceBaseType, true, true, false); CompilerUtils(m_context).loadFromMemoryDynamic(*sourceBaseType, true, true, false);
else else
solAssert(false, "Copying of unknown type requested: " + sourceBaseType->toString()); solAssert(false, "Copying of unknown type requested: " + sourceBaseType->toString());
solAssert(2 + sourceBaseType->getSizeOnStack() <= 16, "Stack too deep."); // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] <source_value>...
m_context << eth::dupInstruction(2 + sourceBaseType->getSizeOnStack()) << u256(0); solAssert(2 + byteOffsetSize + sourceBaseType->getSizeOnStack() <= 16, "Stack too deep.");
// fetch target storage reference
m_context << eth::dupInstruction(2 + byteOffsetSize + sourceBaseType->getSizeOnStack());
if (haveByteOffsetTarget)
m_context << eth::dupInstruction(1 + byteOffsetSize + sourceBaseType->getSizeOnStack());
else
m_context << u256(0);
StorageItem(m_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true); StorageItem(m_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true);
} }
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
// increment source // increment source
m_context if (haveByteOffsetSource)
<< eth::Instruction::SWAP2 incrementByteOffset(sourceBaseType->getStorageBytes(), 1, haveByteOffsetTarget ? 5 : 4);
<< (sourceIsStorage ? sourceBaseType->getStorageSize() : sourceBaseType->getCalldataEncodedSize()) else
<< eth::Instruction::ADD m_context
<< eth::Instruction::SWAP2; << eth::swapInstruction(2 + byteOffsetSize)
<< (sourceIsStorage ? sourceBaseType->getStorageSize() : sourceBaseType->getCalldataEncodedSize())
<< eth::Instruction::ADD
<< eth::swapInstruction(2 + byteOffsetSize);
// increment target // increment target
m_context if (haveByteOffsetTarget)
<< eth::Instruction::SWAP1 incrementByteOffset(targetBaseType->getStorageBytes(), byteOffsetSize, byteOffsetSize + 2);
<< targetBaseType->getStorageSize() else
<< eth::Instruction::ADD m_context
<< eth::Instruction::SWAP1; << eth::swapInstruction(1 + byteOffsetSize)
<< targetBaseType->getStorageSize()
<< eth::Instruction::ADD
<< eth::swapInstruction(1 + byteOffsetSize);
m_context.appendJumpTo(copyLoopStart); m_context.appendJumpTo(copyLoopStart);
m_context << copyLoopEnd; m_context << copyLoopEnd;
if (haveByteOffsetTarget)
{
// clear elements that might be left over in the current slot in target
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end target_byte_offset [source_byte_offset]
m_context << eth::dupInstruction(byteOffsetSize) << eth::Instruction::ISZERO;
eth::AssemblyItem copyCleanupLoopEnd = m_context.appendConditionalJump();
m_context << eth::dupInstruction(2 + byteOffsetSize) << eth::dupInstruction(1 + byteOffsetSize);
StorageItem(m_context, *targetBaseType).setToZero(SourceLocation(), true);
incrementByteOffset(targetBaseType->getStorageBytes(), byteOffsetSize, byteOffsetSize + 2);
m_context.appendJumpTo(copyLoopEnd);
m_context << copyCleanupLoopEnd;
m_context << eth::Instruction::POP; // might pop the source, but then target is popped next
}
if (haveByteOffsetSource)
m_context << eth::Instruction::POP;
m_context << copyLoopEndWithoutByteOffset;
// zero-out leftovers in target // zero-out leftovers in target
// stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end // stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end
@ -166,41 +222,61 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// stack: target_ref target_data_end target_data_pos_updated // stack: target_ref target_data_end target_data_pos_updated
clearStorageLoop(*targetBaseType); clearStorageLoop(*targetBaseType);
m_context << eth::Instruction::POP; m_context << eth::Instruction::POP;
m_context << u256(0); //@todo m_context << u256(0);
} }
void ArrayUtils::clearArray(ArrayType const& _type) const void ArrayUtils::clearArray(ArrayType const& _type) const
{ {
unsigned stackHeightStart = m_context.getStackHeight(); unsigned stackHeightStart = m_context.getStackHeight();
solAssert(_type.getLocation() == ArrayType::Location::Storage, ""); solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
if (_type.isDynamicallySized()) if (_type.getBaseType()->getStorageBytes() < 32)
{ {
m_context << eth::Instruction::POP; // remove byte offset solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type.");
clearDynamicArray(_type); solAssert(_type.getBaseType()->getStorageSize() <= 1, "Invalid storage size for type.");
} }
if (_type.getBaseType()->isValueType())
solAssert(_type.getBaseType()->getStorageSize() <= 1, "Invalid size for value type.");
m_context << eth::Instruction::POP; // remove byte offset
if (_type.isDynamicallySized())
clearDynamicArray(_type);
else if (_type.getLength() == 0 || _type.getBaseType()->getCategory() == Type::Category::Mapping) else if (_type.getLength() == 0 || _type.getBaseType()->getCategory() == Type::Category::Mapping)
m_context << eth::Instruction::POP << eth::Instruction::POP; m_context << eth::Instruction::POP;
else if (_type.getLength() < 5) // unroll loop for small arrays @todo choose a good value else if (_type.getBaseType()->isValueType() && _type.getStorageSize() <= 5)
{
// unroll loop for small arrays @todo choose a good value
// Note that we loop over storage slots here, not elements.
for (unsigned i = 1; i < _type.getStorageSize(); ++i)
m_context
<< u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE
<< u256(1) << eth::Instruction::ADD;
m_context << u256(0) << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
}
else if (!_type.getBaseType()->isValueType() && _type.getLength() <= 4)
{ {
solAssert(!_type.isByteArray(), ""); // unroll loop for small arrays @todo choose a good value
solAssert(_type.getBaseType()->getStorageBytes() >= 32, "Invalid storage size.");
for (unsigned i = 1; i < _type.getLength(); ++i) for (unsigned i = 1; i < _type.getLength(); ++i)
{ {
m_context << u256(0);
StorageItem(m_context, *_type.getBaseType()).setToZero(SourceLocation(), false); StorageItem(m_context, *_type.getBaseType()).setToZero(SourceLocation(), false);
m_context << eth::Instruction::SWAP1; m_context
m_context << u256(_type.getBaseType()->getStorageSize()) << eth::Instruction::ADD; << eth::Instruction::POP
m_context << eth::Instruction::SWAP1; << u256(_type.getBaseType()->getStorageSize()) << eth::Instruction::ADD;
} }
m_context << u256(0);
StorageItem(m_context, *_type.getBaseType()).setToZero(SourceLocation(), true); StorageItem(m_context, *_type.getBaseType()).setToZero(SourceLocation(), true);
} }
else else
{ {
solAssert(!_type.isByteArray(), "");
m_context << eth::Instruction::SWAP1;
m_context << eth::Instruction::DUP1 << _type.getLength(); m_context << eth::Instruction::DUP1 << _type.getLength();
convertLengthToSize(_type); convertLengthToSize(_type);
m_context << eth::Instruction::ADD << eth::Instruction::SWAP1; m_context << eth::Instruction::ADD << eth::Instruction::SWAP1;
clearStorageLoop(*_type.getBaseType()); if (_type.getBaseType()->getStorageBytes() < 32)
m_context << eth::Instruction::POP << eth::Instruction::POP; clearStorageLoop(IntegerType(256));
else
clearStorageLoop(*_type.getBaseType());
m_context << eth::Instruction::POP;
} }
solAssert(m_context.getStackHeight() == stackHeightStart - 2, ""); solAssert(m_context.getStackHeight() == stackHeightStart - 2, "");
} }
@ -224,7 +300,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD
<< eth::Instruction::SWAP1; << eth::Instruction::SWAP1;
// stack: data_pos_end data_pos // stack: data_pos_end data_pos
if (_type.isByteArray()) if (_type.isByteArray() || _type.getBaseType()->getStorageBytes() < 32)
clearStorageLoop(IntegerType(256)); clearStorageLoop(IntegerType(256));
else else
clearStorageLoop(*_type.getBaseType()); clearStorageLoop(*_type.getBaseType());
@ -237,6 +313,8 @@ void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const
{ {
solAssert(_type.getLocation() == ArrayType::Location::Storage, ""); solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
solAssert(_type.isDynamicallySized(), ""); solAssert(_type.isDynamicallySized(), "");
if (!_type.isByteArray() && _type.getBaseType()->getStorageBytes() < 32)
solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type.");
unsigned stackHeightStart = m_context.getStackHeight(); unsigned stackHeightStart = m_context.getStackHeight();
eth::AssemblyItem resizeEnd = m_context.newTag(); eth::AssemblyItem resizeEnd = m_context.newTag();
@ -266,7 +344,7 @@ void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const
// stack: ref new_length data_pos new_size delete_end // stack: ref new_length data_pos new_size delete_end
m_context << eth::Instruction::SWAP2 << eth::Instruction::ADD; m_context << eth::Instruction::SWAP2 << eth::Instruction::ADD;
// stack: ref new_length delete_end delete_start // stack: ref new_length delete_end delete_start
if (_type.isByteArray()) if (_type.isByteArray() || _type.getBaseType()->getStorageBytes() < 32)
clearStorageLoop(IntegerType(256)); clearStorageLoop(IntegerType(256));
else else
clearStorageLoop(*_type.getBaseType()); clearStorageLoop(*_type.getBaseType());
@ -294,7 +372,7 @@ void ArrayUtils::clearStorageLoop(Type const& _type) const
eth::AssemblyItem zeroLoopEnd = m_context.newTag(); eth::AssemblyItem zeroLoopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(zeroLoopEnd); m_context.appendConditionalJumpTo(zeroLoopEnd);
// delete // delete
m_context << u256(0); //@todo m_context << u256(0);
StorageItem(m_context, _type).setToZero(SourceLocation(), false); StorageItem(m_context, _type).setToZero(SourceLocation(), false);
m_context << eth::Instruction::POP; m_context << eth::Instruction::POP;
// increment // increment
@ -313,7 +391,20 @@ void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) con
if (_arrayType.isByteArray()) if (_arrayType.isByteArray())
m_context << u256(31) << eth::Instruction::ADD m_context << u256(31) << eth::Instruction::ADD
<< u256(32) << eth::Instruction::SWAP1 << eth::Instruction::DIV; << u256(32) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
else if (_arrayType.getBaseType()->getStorageSize() > 1) else if (_arrayType.getBaseType()->getStorageSize() <= 1)
{
unsigned baseBytes = _arrayType.getBaseType()->getStorageBytes();
if (baseBytes == 0)
m_context << eth::Instruction::POP << u256(1);
else if (baseBytes <= 16)
{
unsigned itemsPerSlot = 32 / baseBytes;
m_context
<< u256(itemsPerSlot - 1) << eth::Instruction::ADD
<< u256(itemsPerSlot) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
}
}
else
m_context << _arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL; m_context << _arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL;
} }
else else
@ -349,3 +440,39 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const
} }
} }
void ArrayUtils::incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPosition, unsigned _storageOffsetPosition) const
{
solAssert(_byteSize < 32, "");
solAssert(_byteSize != 0, "");
// We do the following, but avoiding jumps:
// byteOffset += byteSize
// if (byteOffset + byteSize > 32)
// {
// storageOffset++;
// byteOffset = 0;
// }
if (_byteOffsetPosition > 1)
m_context << eth::swapInstruction(_byteOffsetPosition - 1);
m_context << u256(_byteSize) << eth::Instruction::ADD;
if (_byteOffsetPosition > 1)
m_context << eth::swapInstruction(_byteOffsetPosition - 1);
// compute, X := (byteOffset + byteSize - 1) / 32, should be 1 iff byteOffset + bytesize > 32
m_context
<< u256(32) << eth::dupInstruction(1 + _byteOffsetPosition) << u256(_byteSize - 1)
<< eth::Instruction::ADD << eth::Instruction::DIV;
// increment storage offset if X == 1 (just add X to it)
// stack: X
m_context
<< eth::swapInstruction(_storageOffsetPosition) << eth::dupInstruction(_storageOffsetPosition + 1)
<< eth::Instruction::ADD << eth::swapInstruction(_storageOffsetPosition);
// stack: X
// set source_byte_offset to zero if X == 1 (using source_byte_offset *= 1 - X)
m_context << u256(1) << eth::Instruction::SUB;
// stack: 1 - X
if (_byteOffsetPosition == 1)
m_context << eth::Instruction::MUL;
else
m_context
<< eth::dupInstruction(_byteOffsetPosition + 1) << eth::Instruction::MUL
<< eth::swapInstruction(_byteOffsetPosition) << eth::Instruction::POP;
}

6
libsolidity/ArrayUtils.h

@ -72,6 +72,12 @@ public:
void retrieveLength(ArrayType const& _arrayType) const; void retrieveLength(ArrayType const& _arrayType) const;
private: private:
/// Adds the given number of bytes to a storage byte offset counter and also increments
/// the storage offset if adding this number again would increase the counter over 32.
/// @param byteOffsetPosition the stack offset of the storage byte offset
/// @param storageOffsetPosition the stack offset of the storage slot offset
void incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPosition, unsigned _storageOffsetPosition) const;
CompilerContext& m_context; CompilerContext& m_context;
}; };

17
libsolidity/Compiler.cpp

@ -20,12 +20,12 @@
* Solidity compiler. * Solidity compiler.
*/ */
#include <libsolidity/Compiler.h>
#include <algorithm> #include <algorithm>
#include <boost/range/adaptor/reversed.hpp> #include <boost/range/adaptor/reversed.hpp>
#include <libevmcore/Instruction.h> #include <libevmcore/Instruction.h>
#include <libevmcore/Assembly.h> #include <libevmcore/Assembly.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/Compiler.h>
#include <libsolidity/ExpressionCompiler.h> #include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerUtils.h> #include <libsolidity/CompilerUtils.h>
@ -274,19 +274,8 @@ void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
void Compiler::registerStateVariables(ContractDefinition const& _contract) void Compiler::registerStateVariables(ContractDefinition const& _contract)
{ {
vector<VariableDeclaration const*> variables; for (auto const& var: ContractType(_contract).getStateVariables())
for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.getLinearizedBaseContracts())) m_context.addStateVariable(*get<0>(var), get<1>(var), get<2>(var));
for (ASTPointer<VariableDeclaration> const& variable: contract->getStateVariables())
if (!variable->isConstant())
variables.push_back(variable.get());
TypePointers types;
for (auto variable: variables)
types.push_back(variable->getType());
StorageOffsets offsets;
offsets.computeOffsets(types);
for (size_t index = 0; index < variables.size(); ++index)
if (auto const* offset = offsets.getOffset(index))
m_context.addStateVariable(*variables[index], offset->first, offset->second);
} }
void Compiler::initializeStateVariables(ContractDefinition const& _contract) void Compiler::initializeStateVariables(ContractDefinition const& _contract)

72
libsolidity/ExpressionCompiler.cpp

@ -753,7 +753,6 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
} }
else if (baseType.getCategory() == Type::Category::Array) else if (baseType.getCategory() == Type::Category::Array)
{ {
// stack layout: <base_ref> [storage_byte_offset] [<length>] <index>
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(baseType); ArrayType const& arrayType = dynamic_cast<ArrayType const&>(baseType);
solAssert(_indexAccess.getIndexExpression(), "Index expression expected."); solAssert(_indexAccess.getIndexExpression(), "Index expression expected.");
ArrayType::Location location = arrayType.getLocation(); ArrayType::Location location = arrayType.getLocation();
@ -762,6 +761,11 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
location == ArrayType::Location::Memory ? eth::Instruction::MLOAD : location == ArrayType::Location::Memory ? eth::Instruction::MLOAD :
eth::Instruction::CALLDATALOAD; eth::Instruction::CALLDATALOAD;
// remove storage byte offset
if (location == ArrayType::Location::Storage)
m_context << eth::Instruction::POP;
// stack layout: <base_ref> [<length>] <index>
_indexAccess.getIndexExpression()->accept(*this); _indexAccess.getIndexExpression()->accept(*this);
// retrieve length // retrieve length
if (!arrayType.isDynamicallySized()) if (!arrayType.isDynamicallySized())
@ -769,11 +773,9 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
else if (location == ArrayType::Location::CallData) else if (location == ArrayType::Location::CallData)
// length is stored on the stack // length is stored on the stack
m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::SWAP1;
else if (location == ArrayType::Location::Storage)
m_context << eth::Instruction::DUP3 << load;
else else
m_context << eth::Instruction::DUP2 << load; m_context << eth::Instruction::DUP2 << load;
// stack: <base_ref> [storage_byte_offset] <index> <length> // stack: <base_ref> <index> <length>
// check out-of-bounds access // check out-of-bounds access
m_context << eth::Instruction::DUP2 << eth::Instruction::LT; m_context << eth::Instruction::DUP2 << eth::Instruction::LT;
eth::AssemblyItem legalAccess = m_context.appendConditionalJump(); eth::AssemblyItem legalAccess = m_context.appendConditionalJump();
@ -781,23 +783,22 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
m_context << eth::Instruction::STOP; m_context << eth::Instruction::STOP;
m_context << legalAccess; m_context << legalAccess;
// stack: <base_ref> [storage_byte_offset] <index> // stack: <base_ref> <index>
if (arrayType.isByteArray()) if (arrayType.isByteArray())
// byte array is packed differently, especially in storage
switch (location) switch (location)
{ {
case ArrayType::Location::Storage: case ArrayType::Location::Storage:
// byte array index storage lvalue on stack (goal): // byte array index storage lvalue on stack (goal):
// <ref> <byte_number> = <base_ref + index / 32> <index % 32> // <ref> <byte_number> = <base_ref + index / 32> <index % 32>
m_context << u256(32) << eth::Instruction::SWAP3; m_context << u256(32) << eth::Instruction::SWAP2;
CompilerUtils(m_context).computeHashStatic(); CompilerUtils(m_context).computeHashStatic();
// stack: 32 storage_byte_offset index data_ref // stack: 32 index data_ref
m_context m_context
<< eth::Instruction::DUP4 << eth::Instruction::DUP3 << eth::Instruction::DUP3 << eth::Instruction::DUP3
<< eth::Instruction::DIV << eth::Instruction::ADD << eth::Instruction::DIV << eth::Instruction::ADD
// stack: 32 storage_byte_offset index (data_ref + index / 32) // stack: 32 index (data_ref + index / 32)
<< eth::Instruction::SWAP3 << eth::Instruction::SWAP2 << eth::Instruction::SWAP2 << eth::Instruction::SWAP1
<< eth::Instruction::POP << eth::Instruction::MOD; << eth::Instruction::MOD;
setLValue<StorageByteArrayElement>(_indexAccess); setLValue<StorageByteArrayElement>(_indexAccess);
break; break;
case ArrayType::Location::CallData: case ArrayType::Location::CallData:
@ -811,36 +812,51 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
} }
else else
{ {
// stack: <base_ref> [storage_byte_offset] <index> // stack: <base_ref> <index>
if (location == ArrayType::Location::Storage) m_context << eth::Instruction::SWAP1;
//@todo use byte offset, remove it for now
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
u256 elementSize =
location == ArrayType::Location::Storage ?
arrayType.getBaseType()->getStorageSize() :
arrayType.getBaseType()->getCalldataEncodedSize();
solAssert(elementSize != 0, "Invalid element size.");
if (elementSize > 1)
m_context << elementSize << eth::Instruction::MUL;
if (arrayType.isDynamicallySized()) if (arrayType.isDynamicallySized())
{ {
if (location == ArrayType::Location::Storage) if (location == ArrayType::Location::Storage)
{
m_context << eth::Instruction::SWAP1;
CompilerUtils(m_context).computeHashStatic(); CompilerUtils(m_context).computeHashStatic();
}
else if (location == ArrayType::Location::Memory) else if (location == ArrayType::Location::Memory)
m_context << u256(32) << eth::Instruction::ADD; m_context << u256(32) << eth::Instruction::ADD;
} }
m_context << eth::Instruction::ADD; // stack: <index> <data_ref>
switch (location) switch (location)
{ {
case ArrayType::Location::CallData: case ArrayType::Location::CallData:
m_context
<< eth::Instruction::SWAP1 << arrayType.getBaseType()->getCalldataEncodedSize()
<< eth::Instruction::MUL << eth::Instruction::ADD;
if (arrayType.getBaseType()->isValueType()) if (arrayType.getBaseType()->isValueType())
CompilerUtils(m_context).loadFromMemoryDynamic(*arrayType.getBaseType(), true, true, false); CompilerUtils(m_context).loadFromMemoryDynamic(*arrayType.getBaseType(), true, true, false);
break; break;
case ArrayType::Location::Storage: case ArrayType::Location::Storage:
m_context << u256(0); // @todo m_context << eth::Instruction::SWAP1;
if (arrayType.getBaseType()->getStorageBytes() <= 16)
{
// stack: <data_ref> <index>
// goal:
// <ref> <byte_number> = <base_ref + index / itemsPerSlot> <(index % itemsPerSlot) * byteSize>
unsigned byteSize = arrayType.getBaseType()->getStorageBytes();
solAssert(byteSize != 0, "");
unsigned itemsPerSlot = 32 / byteSize;
m_context << u256(itemsPerSlot) << eth::Instruction::SWAP2;
// stack: itemsPerSlot index data_ref
m_context
<< eth::Instruction::DUP3 << eth::Instruction::DUP3
<< eth::Instruction::DIV << eth::Instruction::ADD
// stack: itemsPerSlot index (data_ref + index / itemsPerSlot)
<< eth::Instruction::SWAP2 << eth::Instruction::SWAP1
<< eth::Instruction::MOD
<< u256(byteSize) << eth::Instruction::MUL;
}
else
{
if (arrayType.getBaseType()->getStorageSize() != 1)
m_context << arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL;
m_context << eth::Instruction::ADD << u256(0);
}
setLValueToStorageItem(_indexAccess); setLValueToStorageItem(_indexAccess);
break; break;
case ArrayType::Location::Memory: case ArrayType::Location::Memory:

46
libsolidity/Types.cpp

@ -20,14 +20,14 @@
* Solidity data types * Solidity data types
*/ */
#include <libsolidity/Types.h>
#include <limits>
#include <boost/range/adaptor/reversed.hpp>
#include <libdevcore/CommonIO.h> #include <libdevcore/CommonIO.h>
#include <libdevcore/CommonData.h> #include <libdevcore/CommonData.h>
#include <libsolidity/Utils.h> #include <libsolidity/Utils.h>
#include <libsolidity/Types.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <limits>
using namespace std; using namespace std;
namespace dev namespace dev
@ -184,6 +184,8 @@ TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length
TypePointer baseType = _baseTypeName.toType(); TypePointer baseType = _baseTypeName.toType();
if (!baseType) if (!baseType)
BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Invalid type name.")); BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Invalid type name."));
if (baseType->getStorageBytes() == 0)
BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Illegal base type of storage size zero for array."));
if (_length) if (_length)
{ {
if (!_length->getType()) if (!_length->getType())
@ -700,13 +702,21 @@ u256 ArrayType::getStorageSize() const
{ {
if (isDynamicallySized()) if (isDynamicallySized())
return 1; return 1;
else
bigint size;
unsigned baseBytes = getBaseType()->getStorageBytes();
if (baseBytes == 0)
size = 1;
else if (baseBytes < 32)
{ {
bigint size = bigint(getLength()) * getBaseType()->getStorageSize(); unsigned itemsPerSlot = 32 / baseBytes;
if (size >= bigint(1) << 256) size = (bigint(getLength()) + (itemsPerSlot - 1)) / itemsPerSlot;
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Array too large for storage."));
return max<u256>(1, u256(size));
} }
else
size = bigint(getLength()) * getBaseType()->getStorageSize();
if (size >= bigint(1) << 256)
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Array too large for storage."));
return max<u256>(1, u256(size));
} }
unsigned ArrayType::getSizeOnStack() const unsigned ArrayType::getSizeOnStack() const
@ -806,6 +816,26 @@ u256 ContractType::getFunctionIdentifier(string const& _functionName) const
return Invalid256; return Invalid256;
} }
vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::getStateVariables() const
{
vector<VariableDeclaration const*> variables;
for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.getLinearizedBaseContracts()))
for (ASTPointer<VariableDeclaration> const& variable: contract->getStateVariables())
if (!variable->isConstant())
variables.push_back(variable.get());
TypePointers types;
for (auto variable: variables)
types.push_back(variable->getType());
StorageOffsets offsets;
offsets.computeOffsets(types);
vector<tuple<VariableDeclaration const*, u256, unsigned>> variablesAndOffsets;
for (size_t index = 0; index < variables.size(); ++index)
if (auto const* offset = offsets.getOffset(index))
variablesAndOffsets.push_back(make_tuple(variables[index], offset->first, offset->second));
return variablesAndOffsets;
}
TypePointer StructType::unaryOperatorResult(Token::Value _operator) const TypePointer StructType::unaryOperatorResult(Token::Value _operator) const
{ {
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer(); return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();

19
libsolidity/Types.h

@ -335,13 +335,22 @@ public:
/// Constructor for a byte array ("bytes") /// Constructor for a byte array ("bytes")
explicit ArrayType(Location _location): explicit ArrayType(Location _location):
m_location(_location), m_isByteArray(true), m_baseType(std::make_shared<IntegerType>(8)) {} m_location(_location),
m_isByteArray(true),
m_baseType(std::make_shared<FixedBytesType>(8))
{}
/// Constructor for a dynamically sized array type ("type[]") /// Constructor for a dynamically sized array type ("type[]")
ArrayType(Location _location, const TypePointer &_baseType): ArrayType(Location _location, const TypePointer &_baseType):
m_location(_location), m_baseType(_baseType) {} m_location(_location),
m_baseType(_baseType)
{}
/// Constructor for a fixed-size array type ("type[20]") /// Constructor for a fixed-size array type ("type[20]")
ArrayType(Location _location, const TypePointer &_baseType, u256 const& _length): ArrayType(Location _location, const TypePointer &_baseType, u256 const& _length):
m_location(_location), m_baseType(_baseType), m_hasDynamicLength(false), m_length(_length) {} m_location(_location),
m_baseType(_baseType),
m_hasDynamicLength(false),
m_length(_length)
{}
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
@ -403,6 +412,10 @@ public:
/// not exist. /// not exist.
u256 getFunctionIdentifier(std::string const& _functionName) const; u256 getFunctionIdentifier(std::string const& _functionName) const;
/// @returns a list of all state variables (including inherited) of the contract and their
/// offsets in storage.
std::vector<std::tuple<VariableDeclaration const*, u256, unsigned>> getStateVariables() const;
private: private:
ContractDefinition const& m_contract; ContractDefinition const& m_contract;
/// If true, it is the "super" type of the current contract, i.e. it contains only inherited /// If true, it is the "super" type of the current contract, i.e. it contains only inherited

47
libweb3jsonrpc/WebThreeStubServerBase.cpp

@ -112,18 +112,20 @@ static Json::Value toJson(dev::eth::TransactionSkeleton const& _t)
static Json::Value toJson(dev::eth::LocalisedLogEntry const& _e) static Json::Value toJson(dev::eth::LocalisedLogEntry const& _e)
{ {
Json::Value res; Json::Value res;
if (_e.transactionHash)
res["data"] = toJS(_e.data); {
res["address"] = toJS(_e.address); res["data"] = toJS(_e.data);
res["topics"] = Json::Value(Json::arrayValue); res["address"] = toJS(_e.address);
for (auto const& t: _e.topics) res["topics"] = Json::Value(Json::arrayValue);
res["topics"].append(toJS(t)); for (auto const& t: _e.topics)
res["number"] = _e.number; res["topics"].append(toJS(t));
res["hash"] = toJS(_e.sha3); res["number"] = _e.number;
res["hash"] = toJS(_e.transactionHash);
}
return res; return res;
} }
static Json::Value toJson(dev::eth::LocalisedLogEntries const& _es) // commented to avoid warning. Uncomment once in use @ poC-7. static Json::Value toJson(dev::eth::LocalisedLogEntries const& _es)
{ {
Json::Value res(Json::arrayValue); Json::Value res(Json::arrayValue);
for (dev::eth::LocalisedLogEntry const& e: _es) for (dev::eth::LocalisedLogEntry const& e: _es)
@ -139,6 +141,18 @@ static Json::Value toJson(map<u256, u256> const& _storage)
return res; return res;
} }
static unsigned toBlockNumber(std::string const& _js)
{
if (_js == "latest")
return LatestBlock;
else if (_js == "earliest")
return 0;
else if (_js == "pending")
return PendingBlock;
else
return (unsigned)jsToInt(_js);
}
static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to avoid warning. Uncomment once in use @ PoC-7. static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to avoid warning. Uncomment once in use @ PoC-7.
{ {
dev::eth::LogFilter filter; dev::eth::LogFilter filter;
@ -147,9 +161,9 @@ static dev::eth::LogFilter toLogFilter(Json::Value const& _json) // commented to
// check only !empty. it should throw exceptions if input params are incorrect // check only !empty. it should throw exceptions if input params are incorrect
if (!_json["fromBlock"].empty()) if (!_json["fromBlock"].empty())
filter.withEarliest(jsToInt(_json["fromBlock"].asString())); filter.withEarliest(toBlockNumber(_json["fromBlock"].asString()));
if (!_json["toBlock"].empty()) if (!_json["toBlock"].empty())
filter.withLatest(jsToInt(_json["toBlock"].asString())); filter.withLatest(toBlockNumber(_json["toBlock"].asString()));
if (!_json["address"].empty()) if (!_json["address"].empty())
{ {
if (_json["address"].isArray()) if (_json["address"].isArray())
@ -227,15 +241,6 @@ static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message
return res; return res;
} }
static int toBlockNumber(string const& _string)
{
if (_string.compare("latest") == 0)
return -1;
if (_string.compare("pending") == 0)
return 0;
return jsToInt(_string);
}
WebThreeStubServerBase::WebThreeStubServerBase(AbstractServerConnector& _conn, vector<dev::KeyPair> const& _accounts): WebThreeStubServerBase::WebThreeStubServerBase(AbstractServerConnector& _conn, vector<dev::KeyPair> const& _accounts):
AbstractWebThreeStubServer(_conn), m_accounts(make_shared<AccountHolder>(bind(&WebThreeStubServerBase::client, this))) AbstractWebThreeStubServer(_conn), m_accounts(make_shared<AccountHolder>(bind(&WebThreeStubServerBase::client, this)))
{ {
@ -476,7 +481,7 @@ string WebThreeStubServerBase::eth_call(Json::Value const& _json, string const&
t.gasPrice = 10 * dev::eth::szabo; t.gasPrice = 10 * dev::eth::szabo;
if (!t.gas) if (!t.gas)
t.gas = min<u256>(client()->gasLimitRemaining(), client()->balanceAt(t.from) / t.gasPrice); t.gas = min<u256>(client()->gasLimitRemaining(), client()->balanceAt(t.from) / t.gasPrice);
ret = toJS(client()->call(m_accounts->secretKey(t.from), t.value, t.to, t.data, t.gas, t.gasPrice, number)); ret = toJS(client()->call(m_accounts->secretKey(t.from), t.value, t.to, t.data, t.gas, t.gasPrice, number).output);
return ret; return ret;
} }

122
mix/AppContext.cpp

@ -1,122 +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 AppContext.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Provides access to the current QQmlApplicationEngine which is used to add QML file on the fly.
* In the future this class can be extended to add more variable related to the context of the application.
* For now AppContext provides reference to:
* - QQmlApplicationEngine
* - dev::WebThreeDirect (and dev::eth::Client)
* - KeyEventManager
*/
#include <QMessageBox>
#include <QClipboard>
#include <QQmlComponent>
#include <QQmlContext>
#include <QQmlApplicationEngine>
#include <QWindow>
#include "CodeModel.h"
#include "FileIo.h"
#include "ClientModel.h"
#include "CodeEditorExtensionManager.h"
#include "Exceptions.h"
#include "QEther.h"
#include "QVariableDefinition.h"
#include "HttpServer.h"
#include "AppContext.h"
#include "SortFilterProxyModel.h"
using namespace dev;
using namespace dev::eth;
using namespace dev::mix;
const QString c_projectFileName = "project.mix";
AppContext::AppContext(QQmlApplicationEngine* _engine)
{
m_applicationEngine = _engine;
m_codeModel.reset(new CodeModel(this));
m_clientModel.reset(new ClientModel(this));
m_fileIo.reset(new FileIo());
connect(QApplication::clipboard(), &QClipboard::dataChanged, [this] { emit clipboardChanged();});
}
AppContext::~AppContext()
{
}
void AppContext::load()
{
m_applicationEngine->rootContext()->setContextProperty("appContext", this);
QFont f;
m_applicationEngine->rootContext()->setContextProperty("systemPointSize", f.pointSize());
qmlRegisterType<FileIo>("org.ethereum.qml", 1, 0, "FileIo");
m_applicationEngine->rootContext()->setContextProperty("codeModel", m_codeModel.get());
m_applicationEngine->rootContext()->setContextProperty("fileIo", m_fileIo.get());
qmlRegisterType<QEther>("org.ethereum.qml.QEther", 1, 0, "QEther");
qmlRegisterType<QBigInt>("org.ethereum.qml.QBigInt", 1, 0, "QBigInt");
qmlRegisterType<QVariableDeclaration>("org.ethereum.qml.QVariableDeclaration", 1, 0, "QVariableDeclaration");
qmlRegisterType<RecordLogEntry>("org.ethereum.qml.RecordLogEntry", 1, 0, "RecordLogEntry");
qmlRegisterType<SortFilterProxyModel>("org.ethereum.qml.SortFilterProxyModel", 1, 0, "SortFilterProxyModel");
qmlRegisterType<QSolidityType>("org.ethereum.qml.QSolidityType", 1, 0, "QSolidityType");
QQmlComponent projectModelComponent(m_applicationEngine, QUrl("qrc:/qml/ProjectModel.qml"));
QObject* projectModel = projectModelComponent.create();
if (projectModelComponent.isError())
{
QmlLoadException exception;
for (auto const& e : projectModelComponent.errors())
exception << QmlErrorInfo(e);
BOOST_THROW_EXCEPTION(exception);
}
m_applicationEngine->rootContext()->setContextProperty("projectModel", projectModel);
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
qmlRegisterType<HttpServer>("HttpServer", 1, 0, "HttpServer");
m_applicationEngine->load(QUrl("qrc:/qml/main.qml"));
QWindow *window = qobject_cast<QWindow*>(m_applicationEngine->rootObjects().at(0));
window->setIcon(QIcon(":/res/mix_256x256x32.png"));
appLoaded();
}
QQmlApplicationEngine* AppContext::appEngine()
{
return m_applicationEngine;
}
void AppContext::displayMessageDialog(QString _title, QString _message)
{
// TODO : move to a UI dedicated layer.
QObject* dialogWin = m_applicationEngine->rootObjects().at(0)->findChild<QObject*>("alertMessageDialog", Qt::FindChildrenRecursively);
QObject* dialogWinComponent = m_applicationEngine->rootObjects().at(0)->findChild<QObject*>("alertMessageDialogContent", Qt::FindChildrenRecursively);
dialogWinComponent->setProperty("source", QString("qrc:/qml/BasicMessage.qml"));
dialogWin->setProperty("title", _title);
dialogWin->setProperty("width", "250");
dialogWin->setProperty("height", "100");
dialogWin->findChild<QObject*>("messageContent", Qt::FindChildrenRecursively)->setProperty("text", _message);
QMetaObject::invokeMethod(dialogWin, "open");
}
QString AppContext::clipboard() const
{
QClipboard *clipboard = QApplication::clipboard();
return clipboard->text();
}
void AppContext::toClipboard(QString _text)
{
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(_text);
}

88
mix/AppContext.h

@ -1,88 +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 AppContext.h
* @author Yann yann@ethdev.com
* @date 2014
* Provides access to the current QQmlApplicationEngine which is used to add QML file on the fly.
* In the future this class can be extended to add more variable related to the context of the application.
* For now AppContext provides reference to:
* - QQmlApplicationEngine
* - dev::WebThreeDirect (and dev::eth::Client)
* - KeyEventManager
*/
#pragma once
#include <memory>
#include <QUrl>
#include <QObject>
class QQmlApplicationEngine;
namespace dev
{
namespace mix
{
class CodeModel;
class ClientModel;
class FileIo;
/**
* @brief Provides access to application scope variable.
*/
class AppContext: public QObject
{
Q_OBJECT
Q_PROPERTY(QString clipboard READ clipboard WRITE toClipboard NOTIFY clipboardChanged)
public:
AppContext(QQmlApplicationEngine* _engine);
virtual ~AppContext();
/// Load the UI from qml files
void load();
/// Get the current QQMLApplicationEngine instance.
QQmlApplicationEngine* appEngine();
/// Get code model
CodeModel* codeModel() { return m_codeModel.get(); }
/// Get client model
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);
/// Get text from clipboard
QString clipboard() const;
signals:
/// Triggered once components have been loaded
void appLoaded();
void clipboardChanged();
private:
QQmlApplicationEngine* m_applicationEngine; //owned by app
std::unique_ptr<CodeModel> m_codeModel;
std::unique_ptr<ClientModel> m_clientModel;
std::unique_ptr<FileIo> m_fileIo;
public slots:
/// Delete the current instance when application quit.
void quitApplication() {}
};
}
}

2
mix/CMakeLists.txt

@ -16,7 +16,7 @@ include_directories(${Boost_INCLUDE_DIRS})
include_directories(BEFORE ..) include_directories(BEFORE ..)
find_package (Qt5WebEngine QUIET) find_package (Qt5WebEngine QUIET)
qt5_add_resources(UI_RESOURCES res.qrc) qt5_add_resources(UI_RESOURCES res.qrc qml.qrc)
file(GLOB HEADERS "*.h") file(GLOB HEADERS "*.h")

26
mix/ClientModel.cpp

@ -21,6 +21,7 @@
// Make sure boost/asio.hpp is included before windows.h. // Make sure boost/asio.hpp is included before windows.h.
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "ClientModel.h"
#include <QtConcurrent/QtConcurrent> #include <QtConcurrent/QtConcurrent>
#include <QDebug> #include <QDebug>
#include <QQmlContext> #include <QQmlContext>
@ -29,7 +30,6 @@
#include <jsonrpccpp/server.h> #include <jsonrpccpp/server.h>
#include <libethcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include <libethereum/Transaction.h> #include <libethereum/Transaction.h>
#include "AppContext.h"
#include "DebuggingStateWrapper.h" #include "DebuggingStateWrapper.h"
#include "Exceptions.h" #include "Exceptions.h"
#include "QContractDefinition.h" #include "QContractDefinition.h"
@ -39,7 +39,6 @@
#include "CodeModel.h" #include "CodeModel.h"
#include "QEther.h" #include "QEther.h"
#include "Web3Server.h" #include "Web3Server.h"
#include "ClientModel.h"
#include "MixClient.h" #include "MixClient.h"
using namespace dev; using namespace dev;
@ -67,8 +66,8 @@ private:
}; };
ClientModel::ClientModel(AppContext* _context): ClientModel::ClientModel():
m_context(_context), m_running(false), m_rpcConnector(new RpcConnector()) m_running(false), m_rpcConnector(new RpcConnector())
{ {
qRegisterMetaType<QBigInt*>("QBigInt*"); qRegisterMetaType<QBigInt*>("QBigInt*");
qRegisterMetaType<QVariableDefinition*>("QVariableDefinition*"); qRegisterMetaType<QVariableDefinition*>("QVariableDefinition*");
@ -87,7 +86,6 @@ ClientModel::ClientModel(AppContext* _context):
m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), m_client->userAccounts(), m_client.get())); m_web3Server.reset(new Web3Server(*m_rpcConnector.get(), m_client->userAccounts(), m_client.get()));
connect(m_web3Server.get(), &Web3Server::newTransaction, this, &ClientModel::onNewTransaction, Qt::DirectConnection); connect(m_web3Server.get(), &Web3Server::newTransaction, this, &ClientModel::onNewTransaction, Qt::DirectConnection);
_context->appEngine()->rootContext()->setContextProperty("clientModel", this);
} }
ClientModel::~ClientModel() ClientModel::~ClientModel()
@ -184,8 +182,8 @@ void ClientModel::setupState(QVariantMap _state)
} }
else else
{ {
if (contractId.isEmpty() && m_context->codeModel()->hasContract()) //TODO: This is to support old project files, remove later if (contractId.isEmpty() && m_codeModel->hasContract()) //TODO: This is to support old project files, remove later
contractId = m_context->codeModel()->contracts().keys()[0]; contractId = m_codeModel->contracts().keys()[0];
TransactionSettings transactionSettings(contractId, functionId, value, gas, gasPrice, Secret(sender.toStdString())); TransactionSettings transactionSettings(contractId, functionId, value, gas, gasPrice, Secret(sender.toStdString()));
transactionSettings.parameterValues = transaction.value("parameters").toMap(); transactionSettings.parameterValues = transaction.value("parameters").toMap();
@ -220,7 +218,7 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
if (!transaction.stdContractUrl.isEmpty()) if (!transaction.stdContractUrl.isEmpty())
{ {
//std contract //std contract
dev::bytes const& stdContractCode = m_context->codeModel()->getStdContractCode(transaction.contractId, transaction.stdContractUrl); dev::bytes const& stdContractCode = m_codeModel->getStdContractCode(transaction.contractId, transaction.stdContractUrl);
TransactionSettings stdTransaction = transaction; TransactionSettings stdTransaction = transaction;
stdTransaction.gas = 500000;// TODO: get this from std contracts library stdTransaction.gas = 500000;// TODO: get this from std contracts library
Address address = deployContract(stdContractCode, stdTransaction); Address address = deployContract(stdContractCode, stdTransaction);
@ -230,7 +228,7 @@ void ClientModel::executeSequence(std::vector<TransactionSettings> const& _seque
else else
{ {
//encode data //encode data
CompiledContract const& compilerRes = m_context->codeModel()->contract(transaction.contractId); CompiledContract const& compilerRes = m_codeModel->contract(transaction.contractId);
QFunctionDefinition const* f = nullptr; QFunctionDefinition const* f = nullptr;
bytes contractCode = compilerRes.bytes(); bytes contractCode = compilerRes.bytes();
std::shared_ptr<QContractDefinition> contractDef = compilerRes.sharedContract(); std::shared_ptr<QContractDefinition> contractDef = compilerRes.sharedContract();
@ -320,7 +318,7 @@ void ClientModel::showDebuggerForTransaction(ExecutionResult const& _t)
auto nameIter = m_contractNames.find(code.address); auto nameIter = m_contractNames.find(code.address);
if (nameIter != m_contractNames.end()) if (nameIter != m_contractNames.end())
{ {
CompiledContract const& compilerRes = m_context->codeModel()->contract(nameIter->second); CompiledContract const& compilerRes = m_codeModel->contract(nameIter->second);
eth::AssemblyItems assemblyItems = !_t.isConstructor() ? compilerRes.assemblyItems() : compilerRes.constructorAssemblyItems(); eth::AssemblyItems assemblyItems = !_t.isConstructor() ? compilerRes.assemblyItems() : compilerRes.constructorAssemblyItems();
codes.back()->setDocument(compilerRes.documentId()); codes.back()->setDocument(compilerRes.documentId());
codeItems.push_back(std::move(assemblyItems)); codeItems.push_back(std::move(assemblyItems));
@ -439,12 +437,6 @@ void ClientModel::debugRecord(unsigned _index)
showDebuggerForTransaction(e); showDebuggerForTransaction(e);
} }
void ClientModel::showDebugError(QString const& _error)
{
//TODO: change that to a signal
m_context->displayMessageDialog(tr("Debugger"), _error);
}
Address ClientModel::deployContract(bytes const& _code, TransactionSettings const& _ctrTransaction) Address ClientModel::deployContract(bytes const& _code, TransactionSettings const& _ctrTransaction)
{ {
Address newAddress = m_client->submitTransaction(_ctrTransaction.sender, _ctrTransaction.value, _code, _ctrTransaction.gas, _ctrTransaction.gasPrice); Address newAddress = m_client->submitTransaction(_ctrTransaction.sender, _ctrTransaction.value, _code, _ctrTransaction.gas, _ctrTransaction.gasPrice);
@ -527,7 +519,7 @@ void ClientModel::onNewTransaction()
auto contractAddressIter = m_contractNames.find(contractAddress); auto contractAddressIter = m_contractNames.find(contractAddress);
if (contractAddressIter != m_contractNames.end()) if (contractAddressIter != m_contractNames.end())
{ {
CompiledContract const& compilerRes = m_context->codeModel()->contract(contractAddressIter->second); CompiledContract const& compilerRes = m_codeModel->contract(contractAddressIter->second);
const QContractDefinition* def = compilerRes.contract(); const QContractDefinition* def = compilerRes.contract();
contract = def->name(); contract = def->name();
if (abi) if (abi)

10
mix/ClientModel.h

@ -34,13 +34,13 @@ namespace dev
namespace mix namespace mix
{ {
class AppContext;
class Web3Server; class Web3Server;
class RpcConnector; class RpcConnector;
class QEther; class QEther;
class QDebugData; class QDebugData;
class MixClient; class MixClient;
class QVariableDefinition; class QVariableDefinition;
class CodeModel;
struct SolidityType; struct SolidityType;
/// Backend transaction config class /// Backend transaction config class
@ -127,7 +127,7 @@ class ClientModel: public QObject
Q_OBJECT Q_OBJECT
public: public:
ClientModel(AppContext* _context); ClientModel();
~ClientModel(); ~ClientModel();
/// @returns true if currently executing contract code /// @returns true if currently executing contract code
Q_PROPERTY(bool running MEMBER m_running NOTIFY runStateChanged) Q_PROPERTY(bool running MEMBER m_running NOTIFY runStateChanged)
@ -143,6 +143,8 @@ public:
Q_INVOKABLE QString apiCall(QString const& _message); Q_INVOKABLE QString apiCall(QString const& _message);
/// Simulate mining. Creates a new block /// Simulate mining. Creates a new block
Q_INVOKABLE void mine(); Q_INVOKABLE void mine();
/// Get/set code model. Should be set from qml
Q_PROPERTY(CodeModel* codeModel MEMBER m_codeModel)
public slots: public slots:
/// Setup state, run transaction sequence, show debugger for the last transaction /// Setup state, run transaction sequence, show debugger for the last transaction
@ -157,8 +159,6 @@ public slots:
private slots: private slots:
/// Update UI with machine states result. Display a modal dialog. /// Update UI with machine states result. Display a modal dialog.
void showDebugger(); void showDebugger();
/// Update UI with transaction run error.
void showDebugError(QString const& _error);
signals: signals:
/// Transaction execution started /// Transaction execution started
@ -201,7 +201,6 @@ private:
void showDebuggerForTransaction(ExecutionResult const& _t); void showDebuggerForTransaction(ExecutionResult const& _t);
QVariant formatValue(SolidityType const& _type, dev::u256 const& _value); QVariant formatValue(SolidityType const& _type, dev::u256 const& _value);
AppContext* m_context;
std::atomic<bool> m_running; std::atomic<bool> m_running;
std::atomic<bool> m_mining; std::atomic<bool> m_mining;
std::unique_ptr<MixClient> m_client; std::unique_ptr<MixClient> m_client;
@ -211,6 +210,7 @@ private:
std::map<Address, QString> m_contractNames; std::map<Address, QString> m_contractNames;
std::map<QString, Address> m_stdContractAddresses; std::map<QString, Address> m_stdContractAddresses;
std::map<Address, QString> m_stdContractNames; std::map<Address, QString> m_stdContractNames;
CodeModel* m_codeModel = nullptr;
}; };
} }

39
mix/StatusPane.cpp → mix/Clipboard.cpp

@ -1,55 +1,40 @@
/* /*
This file is part of cpp-ethereum. This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file ConstantCompilationControl.cpp /** @file Clipboard.cpp
* @author Yann yann@ethdev.com * @author Yann yann@ethdev.com
* @date 2014 * @date 2015
* Ethereum IDE client.
*/ */
#include <QQmlContext> #include "Clipboard.h"
#include <QQuickItem>
#include <QtCore/QFileInfo>
#include <QApplication> #include <QApplication>
#include <QQmlApplicationEngine> #include <QClipboard>
#include <QtCore/QtCore>
#include <QDebug>
#include "StatusPane.h"
#include "QContractDefinition.h"
#include "AppContext.h"
#include "CodeModel.h"
using namespace dev::mix; using namespace dev::mix;
StatusPane::StatusPane(AppContext* _context): Extension(_context, ExtensionDisplayBehavior::HeaderView) Clipboard::Clipboard()
{ {
_context->appEngine()->rootContext()->setContextProperty("statusPane", this); connect(QApplication::clipboard(), &QClipboard::dataChanged, [this] { emit clipboardChanged();});
} }
QString StatusPane::contentUrl() const QString Clipboard::text() const
{ {
return QStringLiteral("qrc:/qml/StatusPane.qml"); QClipboard *clipboard = QApplication::clipboard();
return clipboard->text();
} }
QString StatusPane::title() const void Clipboard::setText(QString _text)
{ {
return QApplication::tr("compiler"); QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(_text);
} }
void StatusPane::start() const
{
}

29
mix/StatusPane.h → mix/Clipboard.h

@ -1,25 +1,27 @@
/* /*
This file is part of cpp-ethereum. This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file ConstantCompilationControl.h /** @file Clipboard.h
* @author Yann yann@ethdev.com * @author Yann yann@ethdev.com
* @date 2014 * @date 2015
* Ethereum IDE client.
*/ */
#pragma once #pragma once
#include "Extension.h" #include <QObject>
namespace dev namespace dev
{ {
@ -27,20 +29,23 @@ namespace mix
{ {
/** /**
* @brief Extension which display assembly code of the contract being edited. * @brief Provides access to system clipboard
*/ */
class StatusPane: public Extension
class Clipboard: public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText NOTIFY clipboardChanged)
public: public:
StatusPane(AppContext* _appContext); Clipboard();
~StatusPane() {} /// Copy text to clipboard
void start() const override; void setText(QString _text);
QString title() const override; /// Get text from clipboard
QString contentUrl() const override; QString text() const;
public slots: signals:
void clipboardChanged();
}; };
} }

88
mix/CodeEditorExtensionManager.cpp

@ -1,88 +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 CodeEditorExtensionManager.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <QQuickItem>
#include <QGraphicsObject>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQuickTextDocument>
#include "StatusPane.h"
#include "AppContext.h"
#include "MixApplication.h"
#include "CodeModel.h"
#include "ClientModel.h"
#include "CodeHighlighter.h"
#include "CodeEditorExtensionManager.h"
using namespace dev::mix;
CodeEditorExtensionManager::CodeEditorExtensionManager():
m_appContext(static_cast<MixApplication*>(QApplication::instance())->context())
{
}
CodeEditorExtensionManager::~CodeEditorExtensionManager()
{
m_features.clear();
}
void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor)
{
if (!_editor)
return;
}
void CodeEditorExtensionManager::initExtension(std::shared_ptr<Extension> _ext)
{
if (!_ext->contentUrl().isEmpty())
{
try
{
if (_ext->getDisplayBehavior() == ExtensionDisplayBehavior::RightView)
_ext->addTabOn(m_rightView);
if (_ext->getDisplayBehavior() == ExtensionDisplayBehavior::HeaderView)
_ext->addTabOn(m_headerView);
}
catch (...)
{
qDebug() << "Exception when adding tab into view.";
return;
}
}
_ext->start();
m_features.append(_ext);
}
void CodeEditorExtensionManager::applyCodeHighlight()
{
//TODO: reimplement
}
void CodeEditorExtensionManager::setRightView(QQuickItem* _rightView)
{
m_rightView = _rightView;
}
void CodeEditorExtensionManager::setHeaderView(QQuickItem* _headerView)
{
m_headerView = _headerView;
}

71
mix/CodeEditorExtensionManager.h

@ -1,71 +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 CodeEditorExtensionManager.h
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#pragma once
#include <memory>
#include <QQuickItem>
#include <QTextDocument>
#include <QVector>
#include "StatusPane.h"
namespace dev
{
namespace mix
{
class AppContext;
/**
* @brief Init and provides connection between extensions.
*/
class CodeEditorExtensionManager: public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickItem* headerView MEMBER m_headerView WRITE setHeaderView)
Q_PROPERTY(QQuickItem* rightView MEMBER m_rightView WRITE setRightView)
public:
CodeEditorExtensionManager();
~CodeEditorExtensionManager();
/// Initialize extension.
void initExtension(std::shared_ptr<Extension>);
/// Set current tab view
void setHeaderView(QQuickItem*);
/// Set current right tab view.
void setRightView(QQuickItem*);
private slots:
void applyCodeHighlight();
private:
QVector<std::shared_ptr<Extension>> m_features;
QQuickItem* m_headerView;
QQuickItem* m_rightView;
AppContext* m_appContext;
void loadEditor(QQuickItem* _editor);
};
}
}

13
mix/CodeModel.cpp

@ -129,8 +129,7 @@ QString CompiledContract::codeHex() const
return QString::fromStdString(toJS(m_bytes)); return QString::fromStdString(toJS(m_bytes));
} }
CodeModel::CodeModel(QObject* _parent): CodeModel::CodeModel():
QObject(_parent),
m_compiling(false), m_compiling(false),
m_codeHighlighterSettings(new CodeHighlighterSettings()), m_codeHighlighterSettings(new CodeHighlighterSettings()),
m_backgroundWorker(this), m_backgroundWorker(this),
@ -181,7 +180,10 @@ void CodeModel::registerCodeChange(QString const& _documentId, QString const& _c
{ {
CompiledContract* contract = contractByDocumentId(_documentId); CompiledContract* contract = contractByDocumentId(_documentId);
if (contract != nullptr && contract->m_sourceHash == qHash(_code)) if (contract != nullptr && contract->m_sourceHash == qHash(_code))
{
emit compilationComplete();
return; return;
}
{ {
Guard pl(x_pendingContracts); Guard pl(x_pendingContracts);
@ -259,11 +261,16 @@ void CodeModel::runCompilationJob(int _jobId)
CompiledContract* contract = new CompiledContract(cs, name, source); CompiledContract* contract = new CompiledContract(cs, name, source);
QQmlEngine::setObjectOwnership(contract, QQmlEngine::CppOwnership); QQmlEngine::setObjectOwnership(contract, QQmlEngine::CppOwnership);
result[name] = contract; result[name] = contract;
CompiledContract* prevContract = m_contractMap.value(name); CompiledContract* prevContract = nullptr;
for (ContractMap::const_iterator c = m_contractMap.cbegin(); c != m_contractMap.cend(); ++c)
if (c.value()->documentId() == contract->documentId())
prevContract = c.value();
if (prevContract != nullptr && prevContract->contractInterface() != result[name]->contractInterface()) if (prevContract != nullptr && prevContract->contractInterface() != result[name]->contractInterface())
emit contractInterfaceChanged(name); emit contractInterfaceChanged(name);
if (prevContract == nullptr) if (prevContract == nullptr)
emit newContractCompiled(name); emit newContractCompiled(name);
else if (prevContract->contract()->name() != name)
emit contractRenamed(contract->documentId(), prevContract->contract()->name(), name);
} }
releaseContracts(); releaseContracts();
m_contractMap.swap(result); m_contractMap.swap(result);

4
mix/CodeModel.h

@ -125,7 +125,7 @@ class CodeModel: public QObject
Q_OBJECT Q_OBJECT
public: public:
CodeModel(QObject* _parent); CodeModel();
~CodeModel(); ~CodeModel();
Q_PROPERTY(QVariantMap contracts READ contracts NOTIFY codeChanged) Q_PROPERTY(QVariantMap contracts READ contracts NOTIFY codeChanged)
@ -165,6 +165,8 @@ signals:
void contractInterfaceChanged(QString _documentId); void contractInterfaceChanged(QString _documentId);
/// Emitted if there is a new contract compiled for the first time /// Emitted if there is a new contract compiled for the first time
void newContractCompiled(QString _documentId); void newContractCompiled(QString _documentId);
/// Emitted if a contract name has been changed
void contractRenamed(QString _documentId, QString _oldName, QString _newName);
public slots: public slots:
/// Update code model on source code change /// Update code model on source code change

82
mix/Extension.cpp

@ -1,82 +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 Extension.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#include <QMessageBox>
#include <QDebug>
#include <QQmlApplicationEngine>
#include <libevm/VM.h>
#include <libwebthree/WebThree.h>
#include "Extension.h"
#include "AppContext.h"
using namespace dev;
using namespace dev::mix;
Extension::Extension(AppContext* _context)
{
init(_context);
}
Extension::Extension(AppContext* _context, ExtensionDisplayBehavior _displayBehavior)
{
init(_context);
m_displayBehavior = _displayBehavior;
}
void Extension::init(AppContext* _context)
{
m_ctx = _context;
m_appEngine = m_ctx->appEngine();
}
void Extension::addTabOn(QObject* _view)
{
if (contentUrl() == "")
return;
QVariant returnValue;
QQmlComponent* component = new QQmlComponent(
m_appEngine,
QUrl(contentUrl()), _view);
QMetaObject::invokeMethod(_view, "addTab",
Q_RETURN_ARG(QVariant, returnValue),
Q_ARG(QVariant, this->title()),
Q_ARG(QVariant, QVariant::fromValue(component)));
m_view = qvariant_cast<QObject*>(returnValue);
}
void Extension::addContentOn(QObject* _view)
{
Q_UNUSED(_view);
if (m_displayBehavior == ExtensionDisplayBehavior::ModalDialog)
{
QQmlComponent* component = new QQmlComponent(m_appEngine, QUrl(contentUrl()), _view);
QObject* dialogWin = m_appEngine->rootObjects().at(0)->findChild<QObject*>("dialog", Qt::FindChildrenRecursively);
QObject* dialogWinComponent = m_appEngine->rootObjects().at(0)->findChild<QObject*>("modalDialogContent", Qt::FindChildrenRecursively);
dialogWinComponent->setProperty("sourceComponent", QVariant::fromValue(component));
dialogWin->setProperty("title", title());
QMetaObject::invokeMethod(dialogWin, "open");
}
//TODO add more view type.
}

75
mix/Extension.h

@ -1,75 +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 Extension.h
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
*/
#pragma once
#include <QApplication>
#include <QQmlComponent>
class QQmlApplicationEngine;
namespace dev
{
namespace mix
{
class AppContext;
enum ExtensionDisplayBehavior
{
HeaderView,
RightView,
ModalDialog
};
class Extension: public QObject
{
Q_OBJECT
public:
Extension(AppContext* _context);
Extension(AppContext* _context, ExtensionDisplayBehavior _displayBehavior);
/// Return the QML url of the view to display.
virtual QString contentUrl() const { return ""; }
/// Return the title of this extension.
virtual QString title() const { return ""; }
/// Initialize extension.
virtual void start() const {}
/// Add the view define in contentUrl() in the _view QObject.
void addContentOn(QObject* _view);
/// Add the view define in contentUrl() in the _view QObject (_view has to be a tab).
void addTabOn(QObject* _view);
/// Modify the display behavior of this extension.
void setDisplayBehavior(ExtensionDisplayBehavior _displayBehavior) { m_displayBehavior = _displayBehavior; }
/// Get the display behavior of thi extension.
ExtensionDisplayBehavior getDisplayBehavior() { return m_displayBehavior; }
protected:
QObject* m_view;
ExtensionDisplayBehavior m_displayBehavior;
AppContext* m_ctx;
QQmlApplicationEngine* m_appEngine;
private:
void init(AppContext* _context);
};
}
}

42
mix/MixApplication.cpp

@ -19,22 +19,26 @@
* @date 2014 * @date 2014
*/ */
#include <QDebug> #include "MixApplication.h"
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QUrl>
#include <QIcon>
#ifdef ETH_HAVE_WEBENGINE #ifdef ETH_HAVE_WEBENGINE
#include <QtWebEngine/QtWebEngine> #include <QtWebEngine/QtWebEngine>
#endif #endif
#include "CodeModel.h"
#include "MixApplication.h" #include "ClientModel.h"
#include "AppContext.h" #include "FileIo.h"
#include "QEther.h"
#include <QMenuBar> #include "QVariableDeclaration.h"
#include "SortFilterProxyModel.h"
#include "Clipboard.h"
#include "HttpServer.h"
using namespace dev::mix; using namespace dev::mix;
MixApplication::MixApplication(int& _argc, char* _argv[]): MixApplication::MixApplication(int& _argc, char* _argv[]):
QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine()), m_appContext(new AppContext(m_engine.get())) QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine())
{ {
setOrganizationName(tr("Ethereum")); setOrganizationName(tr("Ethereum"));
setOrganizationDomain(tr("ethereum.org")); setOrganizationDomain(tr("ethereum.org"));
@ -43,8 +47,26 @@ MixApplication::MixApplication(int& _argc, char* _argv[]):
#ifdef ETH_HAVE_WEBENGINE #ifdef ETH_HAVE_WEBENGINE
QtWebEngine::initialize(); QtWebEngine::initialize();
#endif #endif
QObject::connect(this, SIGNAL(lastWindowClosed()), context(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff
m_appContext->load(); QFont f;
m_engine->rootContext()->setContextProperty("systemPointSize", f.pointSize());
qmlRegisterType<CodeModel>("org.ethereum.qml.CodeModel", 1, 0, "CodeModel");
qmlRegisterType<ClientModel>("org.ethereum.qml.ClientModel", 1, 0, "ClientModel");
qmlRegisterType<FileIo>("org.ethereum.qml.FileIo", 1, 0, "FileIo");
qmlRegisterType<QEther>("org.ethereum.qml.QEther", 1, 0, "QEther");
qmlRegisterType<QBigInt>("org.ethereum.qml.QBigInt", 1, 0, "QBigInt");
qmlRegisterType<QVariableDeclaration>("org.ethereum.qml.QVariableDeclaration", 1, 0, "QVariableDeclaration");
qmlRegisterType<RecordLogEntry>("org.ethereum.qml.RecordLogEntry", 1, 0, "RecordLogEntry");
qmlRegisterType<SortFilterProxyModel>("org.ethereum.qml.SortFilterProxyModel", 1, 0, "SortFilterProxyModel");
qmlRegisterType<QSolidityType>("org.ethereum.qml.QSolidityType", 1, 0, "QSolidityType");
qmlRegisterType<Clipboard>("org.ethereum.qml.Clipboard", 1, 0, "Clipboard");
qmlRegisterType<HttpServer>("HttpServer", 1, 0, "HttpServer");
qRegisterMetaType<CodeModel*>("CodeModel*");
qRegisterMetaType<ClientModel*>("ClientModel*");
m_engine->load(QUrl("qrc:/qml/main.qml"));
QWindow *window = qobject_cast<QWindow*>(m_engine->rootObjects().at(0));
window->setIcon(QIcon(":/res/mix_256x256x32.png"));
} }
MixApplication::~MixApplication() MixApplication::~MixApplication()

4
mix/MixApplication.h

@ -33,8 +33,6 @@ namespace dev
namespace mix namespace mix
{ {
class AppContext;
class MixApplication: public QApplication class MixApplication: public QApplication
{ {
Q_OBJECT Q_OBJECT
@ -42,12 +40,10 @@ class MixApplication: public QApplication
public: public:
MixApplication(int& _argc, char* _argv[]); MixApplication(int& _argc, char* _argv[]);
virtual ~MixApplication(); virtual ~MixApplication();
AppContext* context() { return m_appContext.get(); }
QQmlApplicationEngine* engine() { return m_engine.get(); } QQmlApplicationEngine* engine() { return m_engine.get(); }
private: private:
std::unique_ptr<QQmlApplicationEngine> m_engine; std::unique_ptr<QQmlApplicationEngine> m_engine;
std::unique_ptr<AppContext> m_appContext;
}; };
} }

18
mix/MixClient.cpp

@ -283,7 +283,7 @@ void MixClient::flushTransactions()
{ {
} }
dev::eth::ExecutionResult MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, int _blockNumber) dev::eth::ExecutionResult MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber)
{ {
u256 n; u256 n;
State temp; State temp;
@ -299,7 +299,7 @@ dev::eth::ExecutionResult MixClient::call(Secret _secret, u256 _value, Address _
return lastExecution().result; return lastExecution().result;
} }
dev::eth::ExecutionResult MixClient::create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, int _blockNumber) dev::eth::ExecutionResult MixClient::create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, BlockNumber _blockNumber)
{ {
u256 n; u256 n;
State temp; State temp;
@ -315,27 +315,27 @@ dev::eth::ExecutionResult MixClient::create(Secret _secret, u256 _value, bytes c
return lastExecution().result; return lastExecution().result;
} }
u256 MixClient::balanceAt(Address _a, int _block) const u256 MixClient::balanceAt(Address _a, BlockNumber _block) const
{ {
return asOf(_block).balance(_a); return asOf(_block).balance(_a);
} }
u256 MixClient::countAt(Address _a, int _block) const u256 MixClient::countAt(Address _a, BlockNumber _block) const
{ {
return asOf(_block).transactionsFrom(_a); return asOf(_block).transactionsFrom(_a);
} }
u256 MixClient::stateAt(Address _a, u256 _l, int _block) const u256 MixClient::stateAt(Address _a, u256 _l, BlockNumber _block) const
{ {
return asOf(_block).storage(_a, _l); return asOf(_block).storage(_a, _l);
} }
bytes MixClient::codeAt(Address _a, int _block) const bytes MixClient::codeAt(Address _a, BlockNumber _block) const
{ {
return asOf(_block).code(_a); return asOf(_block).code(_a);
} }
std::map<u256, u256> MixClient::storageAt(Address _a, int _block) const std::map<u256, u256> MixClient::storageAt(Address _a, BlockNumber _block) const
{ {
return asOf(_block).storage(_a); return asOf(_block).storage(_a);
} }
@ -567,13 +567,13 @@ eth::StateDiff MixClient::diff(unsigned _txi, h256 _block) const
return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); return st.fromPending(_txi).diff(st.fromPending(_txi + 1));
} }
eth::StateDiff MixClient::diff(unsigned _txi, int _block) const eth::StateDiff MixClient::diff(unsigned _txi, BlockNumber _block) const
{ {
State st = asOf(_block); State st = asOf(_block);
return st.fromPending(_txi).diff(st.fromPending(_txi + 1)); return st.fromPending(_txi).diff(st.fromPending(_txi + 1));
} }
Addresses MixClient::addresses(int _block) const Addresses MixClient::addresses(BlockNumber _block) const
{ {
Addresses ret; Addresses ret;
for (auto const& i: asOf(_block).addresses()) for (auto const& i: asOf(_block).addresses())

19
mix/MixClient.h

@ -25,6 +25,7 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <libethcore/Common.h>
#include <libethereum/Interface.h> #include <libethereum/Interface.h>
#include <libethereum/Client.h> #include <libethereum/Client.h>
#include "MachineStates.h" #include "MachineStates.h"
@ -52,13 +53,13 @@ public:
Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) override; Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) override;
void inject(bytesConstRef _rlp) override; void inject(bytesConstRef _rlp) override;
void flushTransactions() override; void flushTransactions() override;
dev::eth::ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, int _blockNumber) override; dev::eth::ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber) override;
dev::eth::ExecutionResult create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, int _blockNumber) override; dev::eth::ExecutionResult create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, eth::BlockNumber _blockNumber) override;
u256 balanceAt(Address _a, int _block) const override; u256 balanceAt(Address _a, eth::BlockNumber _block) const override;
u256 countAt(Address _a, int _block) const override; u256 countAt(Address _a, eth::BlockNumber _block) const override;
u256 stateAt(Address _a, u256 _l, int _block) const override; u256 stateAt(Address _a, u256 _l, eth::BlockNumber _block) const override;
bytes codeAt(Address _a, int _block) const override; bytes codeAt(Address _a, eth::BlockNumber _block) const override;
std::map<u256, u256> storageAt(Address _a, int _block) const override; std::map<u256, u256> storageAt(Address _a, eth::BlockNumber _block) const override;
eth::LocalisedLogEntries logs(unsigned _watchId) const override; eth::LocalisedLogEntries logs(unsigned _watchId) const override;
eth::LocalisedLogEntries logs(eth::LogFilter const& _filter) const override; eth::LocalisedLogEntries logs(eth::LogFilter const& _filter) const override;
unsigned installWatch(eth::LogFilter const& _filter, eth::Reaping _r = eth::Reaping::Automatic) override; unsigned installWatch(eth::LogFilter const& _filter, eth::Reaping _r = eth::Reaping::Automatic) override;
@ -79,8 +80,8 @@ public:
unsigned number() const override; unsigned number() const override;
eth::Transactions pending() const override; eth::Transactions pending() const override;
eth::StateDiff diff(unsigned _txi, h256 _block) const override; eth::StateDiff diff(unsigned _txi, h256 _block) const override;
eth::StateDiff diff(unsigned _txi, int _block) const override; eth::StateDiff diff(unsigned _txi, eth::BlockNumber _block) const override;
Addresses addresses(int _block) const override; Addresses addresses(eth::BlockNumber _block) const override;
u256 gasLimitRemaining() const override; u256 gasLimitRemaining() const override;
void setAddress(Address _us) override; void setAddress(Address _us) override;
Address address() const override; Address address() const override;

3
mix/QContractDefinition.cpp

@ -21,14 +21,13 @@
#include <QObject> #include <QObject>
#include "QContractDefinition.h"
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h> #include <libsolidity/Parser.h>
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
#include <libsolidity/NameAndTypeResolver.h> #include <libsolidity/NameAndTypeResolver.h>
#include "AppContext.h"
#include "QContractDefinition.h"
using namespace dev::solidity; using namespace dev::solidity;
using namespace dev::mix; using namespace dev::mix;

65
mix/qml.qrc

@ -0,0 +1,65 @@
<RCC>
<qresource prefix="/">
<file>qml/AlertMessageDialog.qml</file>
<file>qml/BasicMessage.qml</file>
<file>qml/BigIntValue.qml</file>
<file>qml/CallStack.qml</file>
<file>qml/CodeEditorStyle.qml</file>
<file>qml/CodeEditorView.qml</file>
<file>qml/CommonSeparator.qml</file>
<file>qml/ContractLibrary.qml</file>
<file>qml/DebugBasicInfo.qml</file>
<file>qml/DebugInfoList.qml</file>
<file>qml/Debugger.qml</file>
<file>qml/DebuggerPaneStyle.qml</file>
<file>qml/DefaultLabel.qml</file>
<file>qml/DefaultTextField.qml</file>
<file>qml/DeploymentDialog.qml</file>
<file>qml/Ether.qml</file>
<file>qml/EtherValue.qml</file>
<file>qml/FilesSection.qml</file>
<file>qml/ItemDelegateDataDump.qml</file>
<file>qml/LogsPane.qml</file>
<file>qml/LogsPaneStyle.qml</file>
<file>qml/MainContent.qml</file>
<file>qml/ModalDialog.qml</file>
<file>qml/NewProjectDialog.qml</file>
<file>qml/ProjectFilesStyle.qml</file>
<file>qml/ProjectList.qml</file>
<file>qml/ProjectModel.qml</file>
<file>qml/QBoolTypeView.qml</file>
<file>qml/QHashTypeView.qml</file>
<file>qml/QIntTypeView.qml</file>
<file>qml/QRealTypeView.qml</file>
<file>qml/QStringTypeView.qml</file>
<file>qml/QVariableDeclaration.qml</file>
<file>qml/QVariableDefinition.qml</file>
<file>qml/SourceSansProBold.qml</file>
<file>qml/SourceSansProLight.qml</file>
<file>qml/SourceSansProRegular.qml</file>
<file>qml/Splitter.qml</file>
<file>qml/StateDialog.qml</file>
<file>qml/StateDialogStyle.qml</file>
<file>qml/StateList.qml</file>
<file>qml/StateListModel.qml</file>
<file>qml/StateStyle.qml</file>
<file>qml/StatusPane.qml</file>
<file>qml/StatusPaneStyle.qml</file>
<file>qml/StepActionImage.qml</file>
<file>qml/StorageView.qml</file>
<file>qml/StructView.qml</file>
<file>qml/Style.qml</file>
<file>qml/TabStyle.qml</file>
<file>qml/TransactionDialog.qml</file>
<file>qml/TransactionLog.qml</file>
<file>qml/VariablesView.qml</file>
<file>qml/WebPreviewStyle.qml</file>
<file>qml/js/Debugger.js</file>
<file>qml/js/ErrorLocationFormater.js</file>
<file>qml/js/ProjectModel.js</file>
<file>qml/js/QEtherHelper.js</file>
<file>qml/js/TransactionHelper.js</file>
<file>qml/main.qml</file>
<file>qml/qmldir</file>
</qresource>
</RCC>

2
mix/qml/CodeEditorView.qml

@ -118,7 +118,7 @@ Item {
var doc = editorListModel.get(i); var doc = editorListModel.get(i);
var editor = editors.itemAt(i).item; var editor = editors.itemAt(i).item;
if (editor) if (editor)
fileIo.writeFile(doc.path, item.getText()); fileIo.writeFile(doc.path, editor.getText());
} }
} }

4
mix/qml/ContractLibrary.qml

@ -5,8 +5,8 @@ Item {
property alias model: contractListModel; property alias model: contractListModel;
Connections { Connections {
target: appContext target: mainApplication
Component.onCompleted: { onLoaded: {
//TODO: load a list, dependencies, ets, from external files //TODO: load a list, dependencies, ets, from external files
contractListModel.append({ contractListModel.append({

2
mix/qml/DebugInfoList.qml

@ -136,7 +136,7 @@ ColumnLayout {
var str = ""; var str = "";
for (var i = 0; i < listModel.length; i++) for (var i = 0; i < listModel.length; i++)
str += listModel[i] + "\n"; str += listModel[i] + "\n";
appContext.toClipboard(str); clipboard.text = str;
} }
} }

10
mix/qml/Debugger.qml

@ -68,7 +68,7 @@ Rectangle {
Connections { Connections {
target: clientModel target: clientModel
onDebugDataReady: { onDebugDataReady: {
update(_debugData, true); update(_debugData, false);
} }
} }
@ -158,19 +158,15 @@ Rectangle {
} }
} }
SplitView { Splitter {
id: debugScrollArea id: debugScrollArea
anchors.fill: parent anchors.fill: parent
orientation: Qt.Vertical orientation: Qt.Vertical
handleDelegate: Rectangle {
height: machineStates.sideMargin
color: "transparent"
}
TransactionLog { TransactionLog {
id: transactionLog id: transactionLog
Layout.fillWidth: true Layout.fillWidth: true
Layout.minimumHeight: 120 Layout.minimumHeight: 130
height: 250 height: 250
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left

2
mix/qml/DeploymentDialog.qml

@ -223,7 +223,7 @@ Window {
enabled: deploymentDialog.packageBase64 !== "" enabled: deploymentDialog.packageBase64 !== ""
tooltip: qsTr("Copy Base64 conversion to ClipBoard") tooltip: qsTr("Copy Base64 conversion to ClipBoard")
onTriggered: { onTriggered: {
appContext.toClipboard(deploymentDialog.packageBase64); clipboard.text = deploymentDialog.packageBase64;
} }
} }

4
mix/qml/LogsPane.qml

@ -67,7 +67,7 @@ Rectangle
var log = logsModel.get(k); var log = logsModel.get(k);
content += log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content + "\n"; content += log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content + "\n";
} }
appContext.toClipboard(content); clipboard.text = content;
} }
} }
@ -207,7 +207,7 @@ Rectangle
{ {
var log = logsModel.get(logsTable.currentRow); var log = logsModel.get(logsTable.currentRow);
if (log) if (log)
appContext.toClipboard(log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content); clipboard.text = (log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content);
} }
model: SortFilterProxyModel { model: SortFilterProxyModel {

18
mix/qml/MainContent.qml

@ -2,7 +2,6 @@ import QtQuick 2.2
import QtQuick.Controls 1.1 import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0 import QtQuick.Layouts 1.0
import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Styles 1.1
import CodeEditorExtensionManager 1.0
import Qt.labs.settings 1.0 import Qt.labs.settings 1.0
import org.ethereum.qml.QEther 1.0 import org.ethereum.qml.QEther 1.0
import "js/QEtherHelper.js" as QEtherHelper import "js/QEtherHelper.js" as QEtherHelper
@ -101,9 +100,6 @@ Rectangle {
rightView.displayCompilationErrorIfAny(); rightView.displayCompilationErrorIfAny();
} }
CodeEditorExtensionManager {
}
Settings { Settings {
id: mainSettings id: mainSettings
property alias codeWebOrientation: codeWebSplitter.orientation property alias codeWebOrientation: codeWebSplitter.orientation
@ -158,14 +154,9 @@ Rectangle {
property alias rightViewWidth: rightView.width property alias rightViewWidth: rightView.width
} }
SplitView Splitter
{ {
anchors.fill: parent anchors.fill: parent
handleDelegate: Rectangle {
width: 1
height: 1
color: "#8c8c8c"
}
orientation: Qt.Horizontal orientation: Qt.Horizontal
ProjectList { ProjectList {
@ -183,12 +174,7 @@ Rectangle {
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
SplitView { Splitter {
handleDelegate: Rectangle {
width: 1
height: 1
color: "#8c8c8c"
}
id: codeWebSplitter id: codeWebSplitter
anchors.fill: parent anchors.fill: parent
orientation: Qt.Vertical orientation: Qt.Vertical

19
mix/qml/ProjectList.qml

@ -111,6 +111,25 @@ Item {
Connections { Connections {
target: codeModel target: codeModel
onContractRenamed: {
if (modelData === "Contracts")
{
var ci = 0;
for (var si = 0; si < projectModel.listModel.count; si++) {
var document = projectModel.listModel.get(si);
if (document.isContract) {
var compiledDoc = codeModel.contractByDocumentId(document.documentId);
if (_documentId === document.documentId && _newName !== document.name) {
document.name = _newName;
projectModel.listModel.set(si, document);
sectionModel.set(ci, document);
}
ci++;
}
}
}
}
onCompilationComplete: { onCompilationComplete: {
if (modelData === "Contracts") { if (modelData === "Contracts") {
var ci = 0; var ci = 0;

6
mix/qml/ProjectModel.qml

@ -51,6 +51,7 @@ Item {
function createProject() { ProjectModelCode.createProject(); } function createProject() { ProjectModelCode.createProject(); }
function closeProject(callBack) { ProjectModelCode.closeProject(callBack); } function closeProject(callBack) { ProjectModelCode.closeProject(callBack); }
function saveProject() { ProjectModelCode.saveProject(); } function saveProject() { ProjectModelCode.saveProject(); }
function saveProjectFile() { ProjectModelCode.saveProjectFile(); }
function loadProject(path) { ProjectModelCode.loadProject(path); } function loadProject(path) { ProjectModelCode.loadProject(path); }
function newHtmlFile() { ProjectModelCode.newHtmlFile(); } function newHtmlFile() { ProjectModelCode.newHtmlFile(); }
function newJsFile() { ProjectModelCode.newJsFile(); } function newJsFile() { ProjectModelCode.newJsFile(); }
@ -69,8 +70,8 @@ Item {
function formatAppUrl() { ProjectModelCode.formatAppUrl(url); } function formatAppUrl() { ProjectModelCode.formatAppUrl(url); }
Connections { Connections {
target: appContext target: mainApplication
onAppLoaded: { onLoaded: {
if (projectSettings.lastProjectPath && projectSettings.lastProjectPath !== "") if (projectSettings.lastProjectPath && projectSettings.lastProjectPath !== "")
projectModel.loadProject(projectSettings.lastProjectPath) projectModel.loadProject(projectSettings.lastProjectPath)
} }
@ -173,7 +174,6 @@ Item {
{ {
target: projectModel target: projectModel
onProjectClosed: { onProjectClosed: {
projectSettings.lastProjectPath = "";
projectPath = ""; projectPath = "";
} }
} }

14
mix/qml/Splitter.qml

@ -0,0 +1,14 @@
import QtQuick 2.0
import QtQuick.Controls 1.3
SplitView
{
handleDelegate: Rectangle {
width: 4
height: 4
color: "#cccccc"
}
}

38
mix/qml/StateListModel.qml

@ -128,6 +128,9 @@ Item {
onNewContractCompiled: { onNewContractCompiled: {
stateListModel.addNewContracts(); stateListModel.addNewContracts();
} }
onContractRenamed: {
stateListModel.renameContracts(_oldName, _newName);
}
} }
StateDialog { StateDialog {
@ -206,11 +209,35 @@ Item {
return item; return item;
} }
function renameContracts(oldName, newName) {
var changed = false;
for(var c in codeModel.contracts) {
for (var s = 0; s < stateListModel.count; s++) {
var state = stateList[s];
for (var t = 0; t < state.transactions.length; t++) {
var transaction = state.transactions[t];
if (transaction.contractId === oldName) {
transaction.contractId = newName;
if (transaction.functionId === oldName)
transaction.functionId = newName;
changed = true;
state.transactions[t] = transaction;
}
}
stateListModel.set(s, state);
stateList[s] = state;
}
}
if (changed)
save();
}
function addNewContracts() { function addNewContracts() {
//add new contracts for all states //add new contracts for all states
var changed = false;
for(var c in codeModel.contracts) { for(var c in codeModel.contracts) {
for (var s = 0; s < stateListModel.count; s++) { for (var s = 0; s < stateListModel.count; s++) {
var state = stateList[s];//toPlainStateItem(stateListModel.get(s)); var state = stateList[s];
for (var t = 0; t < state.transactions.length; t++) { for (var t = 0; t < state.transactions.length; t++) {
var transaction = state.transactions[t]; var transaction = state.transactions[t];
if (transaction.functionId === c && transaction.contractId === c) if (transaction.functionId === c && transaction.contractId === c)
@ -223,13 +250,14 @@ Item {
ctorTr.contractId = c; ctorTr.contractId = c;
ctorTr.sender = state.accounts[0].secret; ctorTr.sender = state.accounts[0].secret;
state.transactions.push(ctorTr); state.transactions.push(ctorTr);
var item = state;//fromPlainStateItem(state); changed = true;
stateListModel.set(s, item); stateListModel.set(s, state);
stateList[s] = item; stateList[s] = state;
} }
} }
} }
save(); if (changed)
save();
} }
function addState() { function addState() {

6
mix/qml/TransactionLog.qml

@ -163,10 +163,14 @@ Item {
Keys.onPressed: { Keys.onPressed: {
if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logTable.model.count) { if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C && currentRow >=0 && currentRow < logTable.model.count) {
var item = logTable.model.get(currentRow); var item = logTable.model.get(currentRow);
appContext.toClipboard(item.returned); clipboard.text = item.returned;
} }
} }
} }
Rectangle {
height: 6
color: "transparent"
}
} }
Connections { Connections {

4
mix/qml/WebCodeEditor.qml

@ -35,7 +35,7 @@ Item {
function syncClipboard() { function syncClipboard() {
if (Qt.platform.os == "osx") { if (Qt.platform.os == "osx") {
var text = appContext.clipboard; var text = clipboard.text;
editorBrowser.runJavaScript("setClipboardBase64(\"" + Qt.btoa(text) + "\")"); editorBrowser.runJavaScript("setClipboardBase64(\"" + Qt.btoa(text) + "\")");
} }
} }
@ -60,7 +60,7 @@ Item {
} }
Connections { Connections {
target: appContext target: clipboard
onClipboardChanged: syncClipboard() onClipboardChanged: syncClipboard()
} }

13
mix/qml/WebPreview.qml

@ -58,16 +58,11 @@ Item {
function changePage() { function changePage() {
setPreviewUrl(urlInput.text); setPreviewUrl(urlInput.text);
/*if (pageCombo.currentIndex >= 0 && pageCombo.currentIndex < pageListModel.count) {
urlInput.text = httpServer.url + "/" + pageListModel.get(pageCombo.currentIndex).documentId;
setPreviewUrl(httpServer.url + "/" + pageListModel.get(pageCombo.currentIndex).documentId);
} else {
setPreviewUrl("");
}*/
} }
Connections { Connections {
target: appContext target: mainApplication
onAppLoaded: { onLoaded: {
//We need to load the container using file scheme so that web security would allow loading local files in iframe //We need to load the container using file scheme so that web security would allow loading local files in iframe
var containerPage = fileIo.readFile("qrc:///qml/html/WebContainer.html"); var containerPage = fileIo.readFile("qrc:///qml/html/WebContainer.html");
webView.loadHtml(containerPage, httpServer.url + "/WebContainer.html") webView.loadHtml(containerPage, httpServer.url + "/WebContainer.html")
@ -292,7 +287,7 @@ Item {
color: WebPreviewStyle.general.separatorColor color: WebPreviewStyle.general.separatorColor
} }
SplitView Splitter
{ {
Layout.preferredWidth: parent.width Layout.preferredWidth: parent.width
Layout.fillHeight: true Layout.fillHeight: true

2864
mix/qml/html/cm/acorn.js

File diff suppressed because it is too large

1164
mix/qml/html/cm/acorn_loose.js

File diff suppressed because it is too large

87
mix/qml/html/cm/comment.js

@ -0,0 +1,87 @@
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
return mod(exports);
if (typeof define == "function" && define.amd) // AMD
return define(["exports"], mod);
mod(tern.comment || (tern.comment = {}));
})(function(exports) {
function isSpace(ch) {
return (ch < 14 && ch > 8) || ch === 32 || ch === 160;
}
function onOwnLine(text, pos) {
for (; pos > 0; --pos) {
var ch = text.charCodeAt(pos - 1);
if (ch == 10) break;
if (!isSpace(ch)) return false;
}
return true;
}
// Gather comments directly before a function
exports.commentsBefore = function(text, pos) {
var found = null, emptyLines = 0, topIsLineComment;
out: while (pos > 0) {
var prev = text.charCodeAt(pos - 1);
if (prev == 10) {
for (var scan = --pos, sawNonWS = false; scan > 0; --scan) {
prev = text.charCodeAt(scan - 1);
if (prev == 47 && text.charCodeAt(scan - 2) == 47) {
if (!onOwnLine(text, scan - 2)) break out;
var content = text.slice(scan, pos);
if (!emptyLines && topIsLineComment) found[0] = content + "\n" + found[0];
else (found || (found = [])).unshift(content);
topIsLineComment = true;
emptyLines = 0;
pos = scan - 2;
break;
} else if (prev == 10) {
if (!sawNonWS && ++emptyLines > 1) break out;
break;
} else if (!sawNonWS && !isSpace(prev)) {
sawNonWS = true;
}
}
} else if (prev == 47 && text.charCodeAt(pos - 2) == 42) {
for (var scan = pos - 2; scan > 1; --scan) {
if (text.charCodeAt(scan - 1) == 42 && text.charCodeAt(scan - 2) == 47) {
if (!onOwnLine(text, scan - 2)) break out;
(found || (found = [])).unshift(text.slice(scan, pos - 2));
topIsLineComment = false;
emptyLines = 0;
break;
}
}
pos = scan - 2;
} else if (isSpace(prev)) {
--pos;
} else {
break;
}
}
return found;
};
exports.commentAfter = function(text, pos) {
while (pos < text.length) {
var next = text.charCodeAt(pos);
if (next == 47) {
var after = text.charCodeAt(pos + 1), end;
if (after == 47) // line comment
end = text.indexOf("\n", pos + 2);
else if (after == 42) // block comment
end = text.indexOf("*/", pos + 2);
else
return;
return text.slice(pos + 2, end < 0 ? text.length : end);
} else if (isSpace(next)) {
++pos;
}
}
};
exports.ensureCommentsBefore = function(text, node) {
if (node.hasOwnProperty("commentsBefore")) return node.commentsBefore;
return node.commentsBefore = exports.commentsBefore(text, node.start);
};
});

547
mix/qml/html/cm/def.js

@ -0,0 +1,547 @@
// Type description parser
//
// Type description JSON files (such as ecma5.json and browser.json)
// are used to
//
// A) describe types that come from native code
//
// B) to cheaply load the types for big libraries, or libraries that
// can't be inferred well
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
return exports.init = mod;
if (typeof define == "function" && define.amd) // AMD
return define({init: mod});
tern.def = {init: mod};
})(function(exports, infer) {
"use strict";
function hop(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
var TypeParser = exports.TypeParser = function(spec, start, base, forceNew) {
this.pos = start || 0;
this.spec = spec;
this.base = base;
this.forceNew = forceNew;
};
TypeParser.prototype = {
eat: function(str) {
if (str.length == 1 ? this.spec.charAt(this.pos) == str : this.spec.indexOf(str, this.pos) == this.pos) {
this.pos += str.length;
return true;
}
},
word: function(re) {
var word = "", ch, re = re || /[\w$]/;
while ((ch = this.spec.charAt(this.pos)) && re.test(ch)) { word += ch; ++this.pos; }
return word;
},
error: function() {
throw new Error("Unrecognized type spec: " + this.spec + " (at " + this.pos + ")");
},
parseFnType: function(name, top) {
var args = [], names = [];
if (!this.eat(")")) for (var i = 0; ; ++i) {
var colon = this.spec.indexOf(": ", this.pos), argname;
if (colon != -1) {
argname = this.spec.slice(this.pos, colon);
if (/^[$\w?]+$/.test(argname))
this.pos = colon + 2;
else
argname = null;
}
names.push(argname);
args.push(this.parseType());
if (!this.eat(", ")) {
this.eat(")") || this.error();
break;
}
}
var retType, computeRet, computeRetStart, fn;
if (this.eat(" -> ")) {
if (top && this.spec.indexOf("!", this.pos) > -1) {
retType = infer.ANull;
computeRetStart = this.pos;
computeRet = this.parseRetType();
} else retType = this.parseType();
} else retType = infer.ANull;
if (top && (fn = this.base))
infer.Fn.call(this.base, name, infer.ANull, args, names, retType);
else
fn = new infer.Fn(name, infer.ANull, args, names, retType);
if (computeRet) fn.computeRet = computeRet;
if (computeRetStart != null) fn.computeRetSource = this.spec.slice(computeRetStart, this.pos);
return fn;
},
parseType: function(name, top) {
var type, union = false;
for (;;) {
var inner = this.parseTypeInner(name, top);
if (union) inner.propagate(union);
else type = inner;
if (!this.eat("|")) break;
if (!union) {
union = new infer.AVal;
type.propagate(union);
type = union;
}
}
return type;
},
parseTypeInner: function(name, top) {
if (this.eat("fn(")) {
return this.parseFnType(name, top);
} else if (this.eat("[")) {
var inner = this.parseType();
this.eat("]") || this.error();
if (top && this.base) {
infer.Arr.call(this.base, inner);
return this.base;
}
return new infer.Arr(inner);
} else if (this.eat("+")) {
var path = this.word(/[\w$<>\.!]/);
var base = parsePath(path + ".prototype");
if (!(base instanceof infer.Obj)) base = parsePath(path);
if (!(base instanceof infer.Obj)) return base;
if (top && this.forceNew) return new infer.Obj(base);
return infer.getInstance(base);
} else if (this.eat("?")) {
return infer.ANull;
} else {
return this.fromWord(this.word(/[\w$<>\.!`]/));
}
},
fromWord: function(spec) {
var cx = infer.cx();
switch (spec) {
case "number": return cx.num;
case "string": return cx.str;
case "bool": return cx.bool;
case "<top>": return cx.topScope;
}
if (cx.localDefs && spec in cx.localDefs) return cx.localDefs[spec];
return parsePath(spec);
},
parseBaseRetType: function() {
if (this.eat("[")) {
var inner = this.parseRetType();
this.eat("]") || this.error();
return function(self, args) { return new infer.Arr(inner(self, args)); };
} else if (this.eat("+")) {
var base = this.parseRetType();
var result = function(self, args) {
var proto = base(self, args);
if (proto instanceof infer.Fn && proto.hasProp("prototype"))
proto = proto.getProp("prototype").getObjType();
if (!(proto instanceof infer.Obj)) return proto;
return new infer.Obj(proto);
};
if (this.eat("[")) return this.parsePoly(result);
return result;
} else if (this.eat("!")) {
var arg = this.word(/\d/);
if (arg) {
arg = Number(arg);
return function(_self, args) {return args[arg] || infer.ANull;};
} else if (this.eat("this")) {
return function(self) {return self;};
} else if (this.eat("custom:")) {
var fname = this.word(/[\w$]/);
return customFunctions[fname] || function() { return infer.ANull; };
} else {
return this.fromWord("!" + arg + this.word(/[\w$<>\.!]/));
}
}
var t = this.parseType();
return function(){return t;};
},
extendRetType: function(base) {
var propName = this.word(/[\w<>$!]/) || this.error();
if (propName == "!ret") return function(self, args) {
var lhs = base(self, args);
if (lhs.retval) return lhs.retval;
var rv = new infer.AVal;
lhs.propagate(new infer.IsCallee(infer.ANull, [], null, rv));
return rv;
};
return function(self, args) {return base(self, args).getProp(propName);};
},
parsePoly: function(base) {
var propName = "<i>", match;
if (match = this.spec.slice(this.pos).match(/^\s*(\w+)\s*=\s*/)) {
propName = match[1];
this.pos += match[0].length;
}
var value = this.parseRetType();
if (!this.eat("]")) this.error();
return function(self, args) {
var instance = base(self, args);
if (instance instanceof infer.Obj)
value(self, args).propagate(instance.defProp(propName));
return instance;
};
},
parseRetType: function() {
var tp = this.parseBaseRetType();
while (this.eat(".")) tp = this.extendRetType(tp);
return tp;
}
};
function parseType(spec, name, base, forceNew) {
var type = new TypeParser(spec, null, base, forceNew).parseType(name, true);
if (/^fn\(/.test(spec)) for (var i = 0; i < type.args.length; ++i) (function(i) {
var arg = type.args[i];
if (arg instanceof infer.Fn && arg.args && arg.args.length) addEffect(type, function(_self, fArgs) {
var fArg = fArgs[i];
if (fArg) fArg.propagate(new infer.IsCallee(infer.cx().topScope, arg.args, null, infer.ANull));
});
})(i);
return type;
}
function addEffect(fn, handler, replaceRet) {
var oldCmp = fn.computeRet, rv = fn.retval;
fn.computeRet = function(self, args, argNodes) {
var handled = handler(self, args, argNodes);
var old = oldCmp ? oldCmp(self, args, argNodes) : rv;
return replaceRet ? handled : old;
};
}
var parseEffect = exports.parseEffect = function(effect, fn) {
var m;
if (effect.indexOf("propagate ") == 0) {
var p = new TypeParser(effect, 10);
var getOrigin = p.parseRetType();
if (!p.eat(" ")) p.error();
var getTarget = p.parseRetType();
addEffect(fn, function(self, args) {
getOrigin(self, args).propagate(getTarget(self, args));
});
} else if (effect.indexOf("call ") == 0) {
var andRet = effect.indexOf("and return ", 5) == 5;
var p = new TypeParser(effect, andRet ? 16 : 5);
var getCallee = p.parseRetType(), getSelf = null, getArgs = [];
if (p.eat(" this=")) getSelf = p.parseRetType();
while (p.eat(" ")) getArgs.push(p.parseRetType());
addEffect(fn, function(self, args) {
var callee = getCallee(self, args);
var slf = getSelf ? getSelf(self, args) : infer.ANull, as = [];
for (var i = 0; i < getArgs.length; ++i) as.push(getArgs[i](self, args));
var result = andRet ? new infer.AVal : infer.ANull;
callee.propagate(new infer.IsCallee(slf, as, null, result));
return result;
}, andRet);
} else if (m = effect.match(/^custom (\S+)\s*(.*)/)) {
var customFunc = customFunctions[m[1]];
if (customFunc) addEffect(fn, m[2] ? customFunc(m[2]) : customFunc);
} else if (effect.indexOf("copy ") == 0) {
var p = new TypeParser(effect, 5);
var getFrom = p.parseRetType();
p.eat(" ");
var getTo = p.parseRetType();
addEffect(fn, function(self, args) {
var from = getFrom(self, args), to = getTo(self, args);
from.forAllProps(function(prop, val, local) {
if (local && prop != "<i>")
to.propagate(new infer.PropHasSubset(prop, val));
});
});
} else {
throw new Error("Unknown effect type: " + effect);
}
};
var currentTopScope;
var parsePath = exports.parsePath = function(path, scope) {
var cx = infer.cx(), cached = cx.paths[path], origPath = path;
if (cached != null) return cached;
cx.paths[path] = infer.ANull;
var base = scope || currentTopScope || cx.topScope;
if (cx.localDefs) for (var name in cx.localDefs) {
if (path.indexOf(name) == 0) {
if (path == name) return cx.paths[path] = cx.localDefs[path];
if (path.charAt(name.length) == ".") {
base = cx.localDefs[name];
path = path.slice(name.length + 1);
break;
}
}
}
var parts = path.split(".");
for (var i = 0; i < parts.length && base != infer.ANull; ++i) {
var prop = parts[i];
if (prop.charAt(0) == "!") {
if (prop == "!proto") {
base = (base instanceof infer.Obj && base.proto) || infer.ANull;
} else {
var fn = base.getFunctionType();
if (!fn) {
base = infer.ANull;
} else if (prop == "!ret") {
base = fn.retval && fn.retval.getType(false) || infer.ANull;
} else {
var arg = fn.args && fn.args[Number(prop.slice(1))];
base = (arg && arg.getType(false)) || infer.ANull;
}
}
} else if (base instanceof infer.Obj) {
var propVal = (prop == "prototype" && base instanceof infer.Fn) ? base.getProp(prop) : base.props[prop];
if (!propVal || propVal.isEmpty())
base = infer.ANull;
else
base = propVal.types[0];
}
}
// Uncomment this to get feedback on your poorly written .json files
// if (base == infer.ANull) console.error("bad path: " + origPath + " (" + cx.curOrigin + ")");
cx.paths[origPath] = base == infer.ANull ? null : base;
return base;
};
function emptyObj(ctor) {
var empty = Object.create(ctor.prototype);
empty.props = Object.create(null);
empty.isShell = true;
return empty;
}
function isSimpleAnnotation(spec) {
if (!spec["!type"] || /^(fn\(|\[)/.test(spec["!type"])) return false;
for (var prop in spec)
if (prop != "!type" && prop != "!doc" && prop != "!url" && prop != "!span" && prop != "!data")
return false;
return true;
}
function passOne(base, spec, path) {
if (!base) {
var tp = spec["!type"];
if (tp) {
if (/^fn\(/.test(tp)) base = emptyObj(infer.Fn);
else if (tp.charAt(0) == "[") base = emptyObj(infer.Arr);
else throw new Error("Invalid !type spec: " + tp);
} else if (spec["!stdProto"]) {
base = infer.cx().protos[spec["!stdProto"]];
} else {
base = emptyObj(infer.Obj);
}
base.name = path;
}
for (var name in spec) if (hop(spec, name) && name.charCodeAt(0) != 33) {
var inner = spec[name];
if (typeof inner == "string" || isSimpleAnnotation(inner)) continue;
var prop = base.defProp(name);
passOne(prop.getObjType(), inner, path ? path + "." + name : name).propagate(prop);
}
return base;
}
function passTwo(base, spec, path) {
if (base.isShell) {
delete base.isShell;
var tp = spec["!type"];
if (tp) {
parseType(tp, path, base);
} else {
var proto = spec["!proto"] && parseType(spec["!proto"]);
infer.Obj.call(base, proto instanceof infer.Obj ? proto : true, path);
}
}
var effects = spec["!effects"];
if (effects && base instanceof infer.Fn) for (var i = 0; i < effects.length; ++i)
parseEffect(effects[i], base);
copyInfo(spec, base);
for (var name in spec) if (hop(spec, name) && name.charCodeAt(0) != 33) {
var inner = spec[name], known = base.defProp(name), innerPath = path ? path + "." + name : name;
if (typeof inner == "string") {
if (known.isEmpty()) parseType(inner, innerPath).propagate(known);
} else {
if (!isSimpleAnnotation(inner))
passTwo(known.getObjType(), inner, innerPath);
else if (known.isEmpty())
parseType(inner["!type"], innerPath, null, true).propagate(known);
else
continue;
if (inner["!doc"]) known.doc = inner["!doc"];
if (inner["!url"]) known.url = inner["!url"];
if (inner["!span"]) known.span = inner["!span"];
}
}
return base;
}
function copyInfo(spec, type) {
if (spec["!doc"]) type.doc = spec["!doc"];
if (spec["!url"]) type.url = spec["!url"];
if (spec["!span"]) type.span = spec["!span"];
if (spec["!data"]) type.metaData = spec["!data"];
}
function runPasses(type, arg) {
var parent = infer.cx().parent, pass = parent && parent.passes && parent.passes[type];
if (pass) for (var i = 0; i < pass.length; i++) pass[i](arg);
}
function doLoadEnvironment(data, scope) {
var cx = infer.cx();
infer.addOrigin(cx.curOrigin = data["!name"] || "env#" + cx.origins.length);
cx.localDefs = cx.definitions[cx.curOrigin] = Object.create(null);
runPasses("preLoadDef", data);
passOne(scope, data);
var def = data["!define"];
if (def) {
for (var name in def) {
var spec = def[name];
cx.localDefs[name] = typeof spec == "string" ? parsePath(spec) : passOne(null, spec, name);
}
for (var name in def) {
var spec = def[name];
if (typeof spec != "string") passTwo(cx.localDefs[name], def[name], name);
}
}
passTwo(scope, data);
runPasses("postLoadDef", data);
cx.curOrigin = cx.localDefs = null;
}
exports.load = function(data, scope) {
if (!scope) scope = infer.cx().topScope;
var oldScope = currentTopScope;
currentTopScope = scope;
try {
doLoadEnvironment(data, scope);
} finally {
currentTopScope = oldScope;
}
};
exports.parse = function(data, origin, path) {
var cx = infer.cx();
if (origin) {
cx.origin = origin;
cx.localDefs = cx.definitions[origin];
}
try {
if (typeof data == "string")
return parseType(data, path);
else
return passTwo(passOne(null, data, path), data, path);
} finally {
if (origin) cx.origin = cx.localDefs = null;
}
};
// Used to register custom logic for more involved effect or type
// computation.
var customFunctions = Object.create(null);
infer.registerFunction = function(name, f) { customFunctions[name] = f; };
var IsCreated = infer.constraint("created, target, spec", {
addType: function(tp) {
if (tp instanceof infer.Obj && this.created++ < 5) {
var derived = new infer.Obj(tp), spec = this.spec;
if (spec instanceof infer.AVal) spec = spec.getObjType(false);
if (spec instanceof infer.Obj) for (var prop in spec.props) {
var cur = spec.props[prop].types[0];
var p = derived.defProp(prop);
if (cur && cur instanceof infer.Obj && cur.props.value) {
var vtp = cur.props.value.getType(false);
if (vtp) p.addType(vtp);
}
}
this.target.addType(derived);
}
}
});
infer.registerFunction("Object_create", function(_self, args, argNodes) {
if (argNodes && argNodes.length && argNodes[0].type == "Literal" && argNodes[0].value == null)
return new infer.Obj();
var result = new infer.AVal;
if (args[0]) args[0].propagate(new IsCreated(0, result, args[1]));
return result;
});
var PropSpec = infer.constraint("target", {
addType: function(tp) {
if (!(tp instanceof infer.Obj)) return;
if (tp.hasProp("value"))
tp.getProp("value").propagate(this.target);
else if (tp.hasProp("get"))
tp.getProp("get").propagate(new infer.IsCallee(infer.ANull, [], null, this.target));
}
});
infer.registerFunction("Object_defineProperty", function(_self, args, argNodes) {
if (argNodes && argNodes.length >= 3 && argNodes[1].type == "Literal" &&
typeof argNodes[1].value == "string") {
var obj = args[0], connect = new infer.AVal;
obj.propagate(new infer.PropHasSubset(argNodes[1].value, connect, argNodes[1]));
args[2].propagate(new PropSpec(connect));
}
return infer.ANull;
});
var IsBound = infer.constraint("self, args, target", {
addType: function(tp) {
if (!(tp instanceof infer.Fn)) return;
this.target.addType(new infer.Fn(tp.name, tp.self, tp.args.slice(this.args.length),
tp.argNames.slice(this.args.length), tp.retval));
this.self.propagate(tp.self);
for (var i = 0; i < Math.min(tp.args.length, this.args.length); ++i)
this.args[i].propagate(tp.args[i]);
}
});
infer.registerFunction("Function_bind", function(self, args) {
if (!args.length) return infer.ANull;
var result = new infer.AVal;
self.propagate(new IsBound(args[0], args.slice(1), result));
return result;
});
infer.registerFunction("Array_ctor", function(_self, args) {
var arr = new infer.Arr;
if (args.length != 1 || !args[0].hasType(infer.cx().num)) {
var content = arr.getProp("<i>");
for (var i = 0; i < args.length; ++i) args[i].propagate(content);
}
return arr;
});
infer.registerFunction("Promise_ctor", function(_self, args, argNodes) {
if (args.length < 1) return infer.ANull;
var self = new infer.Obj(infer.cx().definitions.ecma6["Promise.prototype"]);
var valProp = self.defProp("value", argNodes && argNodes[0]);
var valArg = new infer.AVal;
valArg.propagate(valProp);
var exec = new infer.Fn("execute", infer.ANull, [valArg], ["value"], infer.ANull);
var reject = infer.cx().definitions.ecma6.promiseReject;
args[0].propagate(new infer.IsCallee(infer.ANull, [exec, reject], null, infer.ANull));
return self;
});
return exports;
});

402
mix/qml/html/cm/doc_comment.js

@ -0,0 +1,402 @@
// Parses comments above variable declarations, function declarations,
// and object properties as docstrings and JSDoc-style type
// annotations.
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
return mod(require("../lib/infer"), require("../lib/tern"), require("../lib/comment"),
require("acorn/acorn"), require("acorn/util/walk"));
if (typeof define == "function" && define.amd) // AMD
return define(["../lib/infer", "../lib/tern", "../lib/comment", "acorn/acorn", "acorn/util/walk"], mod);
mod(tern, tern, tern.comment, acorn, acorn.walk);
})(function(infer, tern, comment, acorn, walk) {
"use strict";
var WG_MADEUP = 1, WG_STRONG = 101;
tern.registerPlugin("doc_comment", function(server, options) {
server.jsdocTypedefs = Object.create(null);
server.on("reset", function() {
server.jsdocTypedefs = Object.create(null);
});
server._docComment = {
weight: options && options.strong ? WG_STRONG : undefined,
fullDocs: options && options.fullDocs
};
return {
passes: {
postParse: postParse,
postInfer: postInfer,
postLoadDef: postLoadDef
}
};
});
function postParse(ast, text) {
function attachComments(node) { comment.ensureCommentsBefore(text, node); }
walk.simple(ast, {
VariableDeclaration: attachComments,
FunctionDeclaration: attachComments,
AssignmentExpression: function(node) {
if (node.operator == "=") attachComments(node);
},
ObjectExpression: function(node) {
for (var i = 0; i < node.properties.length; ++i)
attachComments(node.properties[i]);
},
CallExpression: function(node) {
if (isDefinePropertyCall(node)) attachComments(node);
}
});
}
function isDefinePropertyCall(node) {
return node.callee.type == "MemberExpression" &&
node.callee.object.name == "Object" &&
node.callee.property.name == "defineProperty" &&
node.arguments.length >= 3 &&
typeof node.arguments[1].value == "string";
}
function postInfer(ast, scope) {
jsdocParseTypedefs(ast.sourceFile.text, scope);
walk.simple(ast, {
VariableDeclaration: function(node, scope) {
if (node.commentsBefore)
interpretComments(node, node.commentsBefore, scope,
scope.getProp(node.declarations[0].id.name));
},
FunctionDeclaration: function(node, scope) {
if (node.commentsBefore)
interpretComments(node, node.commentsBefore, scope,
scope.getProp(node.id.name),
node.body.scope.fnType);
},
AssignmentExpression: function(node, scope) {
if (node.commentsBefore)
interpretComments(node, node.commentsBefore, scope,
infer.expressionType({node: node.left, state: scope}));
},
ObjectExpression: function(node, scope) {
for (var i = 0; i < node.properties.length; ++i) {
var prop = node.properties[i];
if (prop.commentsBefore)
interpretComments(prop, prop.commentsBefore, scope,
node.objType.getProp(prop.key.name));
}
},
CallExpression: function(node, scope) {
if (node.commentsBefore && isDefinePropertyCall(node)) {
var type = infer.expressionType({node: node.arguments[0], state: scope}).getObjType();
if (type && type instanceof infer.Obj) {
var prop = type.props[node.arguments[1].value];
if (prop) interpretComments(node, node.commentsBefore, scope, prop);
}
}
}
}, infer.searchVisitor, scope);
}
function postLoadDef(data) {
var defs = data["!typedef"];
var cx = infer.cx(), orig = data["!name"];
if (defs) for (var name in defs)
cx.parent.jsdocTypedefs[name] =
maybeInstance(infer.def.parse(defs[name], orig, name), name);
}
// COMMENT INTERPRETATION
function interpretComments(node, comments, scope, aval, type) {
jsdocInterpretComments(node, scope, aval, comments);
var cx = infer.cx();
if (!type && aval instanceof infer.AVal && aval.types.length) {
type = aval.types[aval.types.length - 1];
if (!(type instanceof infer.Obj) || type.origin != cx.curOrigin || type.doc)
type = null;
}
var result = comments[comments.length - 1];
if (cx.parent._docComment.fullDocs) {
result = result.trim().replace(/\n[ \t]*\* ?/g, "\n");
} else {
var dot = result.search(/\.\s/);
if (dot > 5) result = result.slice(0, dot + 1);
result = result.trim().replace(/\s*\n\s*\*\s*|\s{1,}/g, " ");
}
result = result.replace(/^\s*\*+\s*/, "");
if (aval instanceof infer.AVal) aval.doc = result;
if (type) type.doc = result;
}
// Parses a subset of JSDoc-style comments in order to include the
// explicitly defined types in the analysis.
function skipSpace(str, pos) {
while (/\s/.test(str.charAt(pos))) ++pos;
return pos;
}
function isIdentifier(string) {
if (!acorn.isIdentifierStart(string.charCodeAt(0))) return false;
for (var i = 1; i < string.length; i++)
if (!acorn.isIdentifierChar(string.charCodeAt(i))) return false;
return true;
}
function parseLabelList(scope, str, pos, close) {
var labels = [], types = [], madeUp = false;
for (var first = true; ; first = false) {
pos = skipSpace(str, pos);
if (first && str.charAt(pos) == close) break;
var colon = str.indexOf(":", pos);
if (colon < 0) return null;
var label = str.slice(pos, colon);
if (!isIdentifier(label)) return null;
labels.push(label);
pos = colon + 1;
var type = parseType(scope, str, pos);
if (!type) return null;
pos = type.end;
madeUp = madeUp || type.madeUp;
types.push(type.type);
pos = skipSpace(str, pos);
var next = str.charAt(pos);
++pos;
if (next == close) break;
if (next != ",") return null;
}
return {labels: labels, types: types, end: pos, madeUp: madeUp};
}
function parseType(scope, str, pos) {
var type, union = false, madeUp = false;
for (;;) {
var inner = parseTypeInner(scope, str, pos);
if (!inner) return null;
madeUp = madeUp || inner.madeUp;
if (union) inner.type.propagate(union);
else type = inner.type;
pos = skipSpace(str, inner.end);
if (str.charAt(pos) != "|") break;
pos++;
if (!union) {
union = new infer.AVal;
type.propagate(union);
type = union;
}
}
var isOptional = false;
if (str.charAt(pos) == "=") {
++pos;
isOptional = true;
}
return {type: type, end: pos, isOptional: isOptional, madeUp: madeUp};
}
function parseTypeInner(scope, str, pos) {
pos = skipSpace(str, pos);
var type, madeUp = false;
if (str.indexOf("function(", pos) == pos) {
var args = parseLabelList(scope, str, pos + 9, ")"), ret = infer.ANull;
if (!args) return null;
pos = skipSpace(str, args.end);
if (str.charAt(pos) == ":") {
++pos;
var retType = parseType(scope, str, pos + 1);
if (!retType) return null;
pos = retType.end;
ret = retType.type;
madeUp = retType.madeUp;
}
type = new infer.Fn(null, infer.ANull, args.types, args.labels, ret);
} else if (str.charAt(pos) == "[") {
var inner = parseType(scope, str, pos + 1);
if (!inner) return null;
pos = skipSpace(str, inner.end);
madeUp = inner.madeUp;
if (str.charAt(pos) != "]") return null;
++pos;
type = new infer.Arr(inner.type);
} else if (str.charAt(pos) == "{") {
var fields = parseLabelList(scope, str, pos + 1, "}");
if (!fields) return null;
type = new infer.Obj(true);
for (var i = 0; i < fields.types.length; ++i) {
var field = type.defProp(fields.labels[i]);
field.initializer = true;
fields.types[i].propagate(field);
}
pos = fields.end;
madeUp = fields.madeUp;
} else if (str.charAt(pos) == "(") {
var inner = parseType(scope, str, pos + 1);
if (!inner) return null;
pos = skipSpace(str, inner.end);
if (str.charAt(pos) != ")") return null;
++pos;
type = inner.type;
} else {
var start = pos;
if (!acorn.isIdentifierStart(str.charCodeAt(pos))) return null;
while (acorn.isIdentifierChar(str.charCodeAt(pos))) ++pos;
if (start == pos) return null;
var word = str.slice(start, pos);
if (/^(number|integer)$/i.test(word)) type = infer.cx().num;
else if (/^bool(ean)?$/i.test(word)) type = infer.cx().bool;
else if (/^string$/i.test(word)) type = infer.cx().str;
else if (/^(null|undefined)$/i.test(word)) type = infer.ANull;
else if (/^array$/i.test(word)) {
var inner = null;
if (str.charAt(pos) == "." && str.charAt(pos + 1) == "<") {
var inAngles = parseType(scope, str, pos + 2);
if (!inAngles) return null;
pos = skipSpace(str, inAngles.end);
madeUp = inAngles.madeUp;
if (str.charAt(pos++) != ">") return null;
inner = inAngles.type;
}
type = new infer.Arr(inner);
} else if (/^object$/i.test(word)) {
type = new infer.Obj(true);
if (str.charAt(pos) == "." && str.charAt(pos + 1) == "<") {
var key = parseType(scope, str, pos + 2);
if (!key) return null;
pos = skipSpace(str, key.end);
madeUp = madeUp || key.madeUp;
if (str.charAt(pos++) != ",") return null;
var val = parseType(scope, str, pos);
if (!val) return null;
pos = skipSpace(str, val.end);
madeUp = key.madeUp || val.madeUp;
if (str.charAt(pos++) != ">") return null;
val.type.propagate(type.defProp("<i>"));
}
} else {
while (str.charCodeAt(pos) == 46 ||
acorn.isIdentifierChar(str.charCodeAt(pos))) ++pos;
var path = str.slice(start, pos);
var cx = infer.cx(), defs = cx.parent && cx.parent.jsdocTypedefs, found;
if (defs && (path in defs)) {
type = defs[path];
} else if (found = infer.def.parsePath(path, scope).getObjType()) {
type = maybeInstance(found, path);
} else {
if (!cx.jsdocPlaceholders) cx.jsdocPlaceholders = Object.create(null);
if (!(path in cx.jsdocPlaceholders))
type = cx.jsdocPlaceholders[path] = new infer.Obj(null, path);
else
type = cx.jsdocPlaceholders[path];
madeUp = true;
}
}
}
return {type: type, end: pos, madeUp: madeUp};
}
function maybeInstance(type, path) {
if (type instanceof infer.Fn && /^[A-Z]/.test(path)) {
var proto = type.getProp("prototype").getObjType();
if (proto instanceof infer.Obj) return infer.getInstance(proto);
}
return type;
}
function parseTypeOuter(scope, str, pos) {
pos = skipSpace(str, pos || 0);
if (str.charAt(pos) != "{") return null;
var result = parseType(scope, str, pos + 1);
if (!result) return null;
var end = skipSpace(str, result.end);
if (str.charAt(end) != "}") return null;
result.end = end + 1;
return result;
}
function jsdocInterpretComments(node, scope, aval, comments) {
var type, args, ret, foundOne, self, parsed;
for (var i = 0; i < comments.length; ++i) {
var comment = comments[i];
var decl = /(?:\n|$|\*)\s*@(type|param|arg(?:ument)?|returns?|this)\s+(.*)/g, m;
while (m = decl.exec(comment)) {
if (m[1] == "this" && (parsed = parseType(scope, m[2], 0))) {
self = parsed;
foundOne = true;
continue;
}
if (!(parsed = parseTypeOuter(scope, m[2]))) continue;
foundOne = true;
switch(m[1]) {
case "returns": case "return":
ret = parsed; break;
case "type":
type = parsed; break;
case "param": case "arg": case "argument":
var name = m[2].slice(parsed.end).match(/^\s*(\[?)\s*([^\]\s=]+)\s*(?:=[^\]]+\s*)?(\]?).*/);
if (!name) continue;
var argname = name[2] + (parsed.isOptional || (name[1] === '[' && name[3] === ']') ? "?" : "");
(args || (args = Object.create(null)))[argname] = parsed;
break;
}
}
}
if (foundOne) applyType(type, self, args, ret, node, aval);
};
function jsdocParseTypedefs(text, scope) {
var cx = infer.cx();
var re = /\s@typedef\s+(.*)/g, m;
while (m = re.exec(text)) {
var parsed = parseTypeOuter(scope, m[1]);
var name = parsed && m[1].slice(parsed.end).match(/^\s*(\S+)/);
if (name)
cx.parent.jsdocTypedefs[name[1]] = parsed.type;
}
}
function propagateWithWeight(type, target) {
var weight = infer.cx().parent._docComment.weight;
type.type.propagate(target, weight || (type.madeUp ? WG_MADEUP : undefined));
}
function applyType(type, self, args, ret, node, aval) {
var fn;
if (node.type == "VariableDeclaration") {
var decl = node.declarations[0];
if (decl.init && decl.init.type == "FunctionExpression") fn = decl.init.body.scope.fnType;
} else if (node.type == "FunctionDeclaration") {
fn = node.body.scope.fnType;
} else if (node.type == "AssignmentExpression") {
if (node.right.type == "FunctionExpression")
fn = node.right.body.scope.fnType;
} else if (node.type == "CallExpression") {
} else { // An object property
if (node.value.type == "FunctionExpression") fn = node.value.body.scope.fnType;
}
if (fn && (args || ret || self)) {
if (args) for (var i = 0; i < fn.argNames.length; ++i) {
var name = fn.argNames[i], known = args[name];
if (!known && (known = args[name + "?"]))
fn.argNames[i] += "?";
if (known) propagateWithWeight(known, fn.args[i]);
}
if (ret) propagateWithWeight(ret, fn.retval);
if (self) propagateWithWeight(self, fn.self);
} else if (type) {
propagateWithWeight(type, aval);
}
};
});

969
mix/qml/html/cm/ecma5spec.js

@ -0,0 +1,969 @@
function ecma5Spec()
{
return {
"!name": "ecma5",
"!define": {"Error.prototype": "Error.prototype"},
"Infinity": {
"!type": "number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Infinity",
"!doc": "A numeric value representing infinity."
},
"undefined": {
"!type": "?",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/undefined",
"!doc": "The value undefined."
},
"NaN": {
"!type": "number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/NaN",
"!doc": "A value representing Not-A-Number."
},
"Object": {
"!type": "fn()",
"getPrototypeOf": {
"!type": "fn(obj: ?) -> ?",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/getPrototypeOf",
"!doc": "Returns the prototype (i.e. the internal prototype) of the specified object."
},
"create": {
"!type": "fn(proto: ?) -> !custom:Object_create",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create",
"!doc": "Creates a new object with the specified prototype object and properties."
},
"defineProperty": {
"!type": "fn(obj: ?, prop: string, desc: ?) -> !custom:Object_defineProperty",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty",
"!doc": "Defines a new property directly on an object, or modifies an existing property on an object, and returns the object. If you want to see how to use the Object.defineProperty method with a binary-flags-like syntax, see this article."
},
"defineProperties": {
"!type": "fn(obj: ?, props: ?)",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty",
"!doc": "Defines a new property directly on an object, or modifies an existing property on an object, and returns the object. If you want to see how to use the Object.defineProperty method with a binary-flags-like syntax, see this article."
},
"getOwnPropertyDescriptor": {
"!type": "fn(obj: ?, prop: string) -> ?",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor",
"!doc": "Returns a property descriptor for an own property (that is, one directly present on an object, not present by dint of being along an object's prototype chain) of a given object."
},
"keys": {
"!type": "fn(obj: ?) -> [string]",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys",
"!doc": "Returns an array of a given object's own enumerable properties, in the same order as that provided by a for-in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well)."
},
"getOwnPropertyNames": {
"!type": "fn(obj: ?) -> [string]",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames",
"!doc": "Returns an array of all properties (enumerable or not) found directly upon a given object."
},
"seal": {
"!type": "fn(obj: ?)",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/seal",
"!doc": "Seals an object, preventing new properties from being added to it and marking all existing properties as non-configurable. Values of present properties can still be changed as long as they are writable."
},
"isSealed": {
"!type": "fn(obj: ?) -> bool",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/isSealed",
"!doc": "Determine if an object is sealed."
},
"freeze": {
"!type": "fn(obj: ?)",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/freeze",
"!doc": "Freezes an object: that is, prevents new properties from being added to it; prevents existing properties from being removed; and prevents existing properties, or their enumerability, configurability, or writability, from being changed. In essence the object is made effectively immutable. The method returns the object being frozen."
},
"isFrozen": {
"!type": "fn(obj: ?) -> bool",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/isFrozen",
"!doc": "Determine if an object is frozen."
},
"preventExtensions": {
"!type": "fn(obj: ?)",
"!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions",
"!doc": "Prevents new properties from ever being added to an object."
},
"isExtensible": {
"!type": "fn(obj: ?) -> bool",
"!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible",
"!doc": "The Object.isExtensible() method determines if an object is extensible (whether it can have new properties added to it)."
},
"prototype": {
"!stdProto": "Object",
"toString": {
"!type": "fn() -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/toString",
"!doc": "Returns a string representing the object."
},
"toLocaleString": {
"!type": "fn() -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/toLocaleString",
"!doc": "Returns a string representing the object. This method is meant to be overriden by derived objects for locale-specific purposes."
},
"valueOf": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/valueOf",
"!doc": "Returns the primitive value of the specified object"
},
"hasOwnProperty": {
"!type": "fn(prop: string) -> bool",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/hasOwnProperty",
"!doc": "Returns a boolean indicating whether the object has the specified property."
},
"propertyIsEnumerable": {
"!type": "fn(prop: string) -> bool",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/propertyIsEnumerable",
"!doc": "Returns a Boolean indicating whether the specified property is enumerable."
},
"isPrototypeOf": {
"!type": "fn(obj: ?) -> bool",
"!url": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isPrototypeOf",
"!doc": "Tests for an object in another object's prototype chain."
}
},
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object",
"!doc": "Creates an object wrapper."
},
"Function": {
"!type": "fn(body: string) -> fn()",
"prototype": {
"!stdProto": "Function",
"apply": {
"!type": "fn(this: ?, args: [?])",
"!effects": [
"call and return !this this=!0 !1.<i> !1.<i> !1.<i>"
],
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/apply",
"!doc": "Calls a function with a given this value and arguments provided as an array (or an array like object)."
},
"call": {
"!type": "fn(this: ?, args?: ?) -> !this.!ret",
"!effects": [
"call and return !this this=!0 !1 !2 !3 !4"
],
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/call",
"!doc": "Calls a function with a given this value and arguments provided individually."
},
"bind": {
"!type": "fn(this: ?, args?: ?) -> !custom:Function_bind",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind",
"!doc": "Creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function was called."
},
"prototype": "?"
},
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function",
"!doc": "Every function in JavaScript is actually a Function object."
},
"Array": {
"!type": "fn(size: number) -> !custom:Array_ctor",
"isArray": {
"!type": "fn(value: ?) -> bool",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/isArray",
"!doc": "Returns true if an object is an array, false if it is not."
},
"prototype": {
"!stdProto": "Array",
"length": {
"!type": "number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/length",
"!doc": "An unsigned, 32-bit integer that specifies the number of elements in an array."
},
"concat": {
"!type": "fn(other: [?]) -> !this",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/concat",
"!doc": "Returns a new array comprised of this array joined with other array(s) and/or value(s)."
},
"join": {
"!type": "fn(separator?: string) -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/join",
"!doc": "Joins all elements of an array into a string."
},
"splice": {
"!type": "fn(pos: number, amount: number)",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/splice",
"!doc": "Changes the content of an array, adding new elements while removing old elements."
},
"pop": {
"!type": "fn() -> !this.<i>",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/pop",
"!doc": "Removes the last element from an array and returns that element."
},
"push": {
"!type": "fn(newelt: ?) -> number",
"!effects": [
"propagate !0 !this.<i>"
],
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/push",
"!doc": "Mutates an array by appending the given elements and returning the new length of the array."
},
"shift": {
"!type": "fn() -> !this.<i>",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/shift",
"!doc": "Removes the first element from an array and returns that element. This method changes the length of the array."
},
"unshift": {
"!type": "fn(newelt: ?) -> number",
"!effects": [
"propagate !0 !this.<i>"
],
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/unshift",
"!doc": "Adds one or more elements to the beginning of an array and returns the new length of the array."
},
"slice": {
"!type": "fn(from: number, to?: number) -> !this",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/slice",
"!doc": "Returns a shallow copy of a portion of an array."
},
"reverse": {
"!type": "fn()",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/reverse",
"!doc": "Reverses an array in place. The first array element becomes the last and the last becomes the first."
},
"sort": {
"!type": "fn(compare?: fn(a: ?, b: ?) -> number)",
"!effects": [
"call !0 !this.<i> !this.<i>"
],
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/sort",
"!doc": "Sorts the elements of an array in place and returns the array."
},
"indexOf": {
"!type": "fn(elt: ?, from?: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf",
"!doc": "Returns the first index at which a given element can be found in the array, or -1 if it is not present."
},
"lastIndexOf": {
"!type": "fn(elt: ?, from?: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/lastIndexOf",
"!doc": "Returns the last index at which a given element can be found in the array, or -1 if it is not present. The array is searched backwards, starting at fromIndex."
},
"every": {
"!type": "fn(test: fn(elt: ?, i: number) -> bool, context?: ?) -> bool",
"!effects": [
"call !0 this=!1 !this.<i> number"
],
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/every",
"!doc": "Tests whether all elements in the array pass the test implemented by the provided function."
},
"some": {
"!type": "fn(test: fn(elt: ?, i: number) -> bool, context?: ?) -> bool",
"!effects": [
"call !0 this=!1 !this.<i> number"
],
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/some",
"!doc": "Tests whether some element in the array passes the test implemented by the provided function."
},
"filter": {
"!type": "fn(test: fn(elt: ?, i: number) -> bool, context?: ?) -> !this",
"!effects": [
"call !0 this=!1 !this.<i> number"
],
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/filter",
"!doc": "Creates a new array with all elements that pass the test implemented by the provided function."
},
"forEach": {
"!type": "fn(f: fn(elt: ?, i: number), context?: ?)",
"!effects": [
"call !0 this=!1 !this.<i> number"
],
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach",
"!doc": "Executes a provided function once per array element."
},
"map": {
"!type": "fn(f: fn(elt: ?, i: number) -> ?, context?: ?) -> [!0.!ret]",
"!effects": [
"call !0 this=!1 !this.<i> number"
],
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/map",
"!doc": "Creates a new array with the results of calling a provided function on every element in this array."
},
"reduce": {
"!type": "fn(combine: fn(sum: ?, elt: ?, i: number) -> ?, init?: ?) -> !0.!ret",
"!effects": [
"call !0 !1 !this.<i> number"
],
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/Reduce",
"!doc": "Apply a function against an accumulator and each value of the array (from left-to-right) as to reduce it to a single value."
},
"reduceRight": {
"!type": "fn(combine: fn(sum: ?, elt: ?, i: number) -> ?, init?: ?) -> !0.!ret",
"!effects": [
"call !0 !1 !this.<i> number"
],
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/ReduceRight",
"!doc": "Apply a function simultaneously against two values of the array (from right-to-left) as to reduce it to a single value."
}
},
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array",
"!doc": "The JavaScript Array global object is a constructor for arrays, which are high-level, list-like objects."
},
"String": {
"!type": "fn(value: ?) -> string",
"fromCharCode": {
"!type": "fn(code: number) -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/fromCharCode",
"!doc": "Returns a string created by using the specified sequence of Unicode values."
},
"prototype": {
"!stdProto": "String",
"length": {
"!type": "number",
"!url": "https://developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/String/length",
"!doc": "Represents the length of a string."
},
"<i>": "string",
"charAt": {
"!type": "fn(i: number) -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/charAt",
"!doc": "Returns the specified character from a string."
},
"charCodeAt": {
"!type": "fn(i: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/charCodeAt",
"!doc": "Returns the numeric Unicode value of the character at the given index (except for unicode codepoints > 0x10000)."
},
"indexOf": {
"!type": "fn(char: string, from?: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/indexOf",
"!doc": "Returns the index within the calling String object of the first occurrence of the specified value, starting the search at fromIndex,\nreturns -1 if the value is not found."
},
"lastIndexOf": {
"!type": "fn(char: string, from?: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/lastIndexOf",
"!doc": "Returns the index within the calling String object of the last occurrence of the specified value, or -1 if not found. The calling string is searched backward, starting at fromIndex."
},
"substring": {
"!type": "fn(from: number, to?: number) -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/substring",
"!doc": "Returns a subset of a string between one index and another, or through the end of the string."
},
"substr": {
"!type": "fn(from: number, length?: number) -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/substr",
"!doc": "Returns the characters in a string beginning at the specified location through the specified number of characters."
},
"slice": {
"!type": "fn(from: number, to?: number) -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/slice",
"!doc": "Extracts a section of a string and returns a new string."
},
"trim": {
"!type": "fn() -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/Trim",
"!doc": "Removes whitespace from both ends of the string."
},
"toUpperCase": {
"!type": "fn() -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toUpperCase",
"!doc": "Returns the calling string value converted to uppercase."
},
"toLowerCase": {
"!type": "fn() -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toLowerCase",
"!doc": "Returns the calling string value converted to lowercase."
},
"toLocaleUpperCase": {
"!type": "fn() -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toLocaleUpperCase",
"!doc": "Returns the calling string value converted to upper case, according to any locale-specific case mappings."
},
"toLocaleLowerCase": {
"!type": "fn() -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toLocaleLowerCase",
"!doc": "Returns the calling string value converted to lower case, according to any locale-specific case mappings."
},
"split": {
"!type": "fn(pattern: string) -> [string]",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/split",
"!doc": "Splits a String object into an array of strings by separating the string into substrings."
},
"concat": {
"!type": "fn(other: string) -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/concat",
"!doc": "Combines the text of two or more strings and returns a new string."
},
"localeCompare": {
"!type": "fn(other: string) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/localeCompare",
"!doc": "Returns a number indicating whether a reference string comes before or after or is the same as the given string in sort order."
},
"match": {
"!type": "fn(pattern: +RegExp) -> [string]",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/match",
"!doc": "Used to retrieve the matches when matching a string against a regular expression."
},
"replace": {
"!type": "fn(pattern: +RegExp, replacement: string) -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/replace",
"!doc": "Returns a new string with some or all matches of a pattern replaced by a replacement. The pattern can be a string or a RegExp, and the replacement can be a string or a function to be called for each match."
},
"search": {
"!type": "fn(pattern: +RegExp) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/search",
"!doc": "Executes the search for a match between a regular expression and this String object."
}
},
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String",
"!doc": "The String global object is a constructor for strings, or a sequence of characters."
},
"Number": {
"!type": "fn(value: ?) -> number",
"MAX_VALUE": {
"!type": "number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/MAX_VALUE",
"!doc": "The maximum numeric value representable in JavaScript."
},
"MIN_VALUE": {
"!type": "number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/MIN_VALUE",
"!doc": "The smallest positive numeric value representable in JavaScript."
},
"POSITIVE_INFINITY": {
"!type": "number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/POSITIVE_INFINITY",
"!doc": "A value representing the positive Infinity value."
},
"NEGATIVE_INFINITY": {
"!type": "number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/NEGATIVE_INFINITY",
"!doc": "A value representing the negative Infinity value."
},
"prototype": {
"!stdProto": "Number",
"toString": {
"!type": "fn(radix?: number) -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toString",
"!doc": "Returns a string representing the specified Number object"
},
"toFixed": {
"!type": "fn(digits: number) -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toFixed",
"!doc": "Formats a number using fixed-point notation"
},
"toExponential": {
"!type": "fn(digits: number) -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toExponential",
"!doc": "Returns a string representing the Number object in exponential notation"
},
"toPrecision": {
"!type": "fn(digits: number) -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number/toPrecision",
"!doc": "The toPrecision() method returns a string representing the number to the specified precision."
}
},
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Number",
"!doc": "The Number JavaScript object is a wrapper object allowing you to work with numerical values. A Number object is created using the Number() constructor."
},
"Boolean": {
"!type": "fn(value: ?) -> bool",
"prototype": {
"!stdProto": "Boolean"
},
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Boolean",
"!doc": "The Boolean object is an object wrapper for a boolean value."
},
"RegExp": {
"!type": "fn(source: string, flags?: string)",
"prototype": {
"!stdProto": "RegExp",
"exec": {
"!type": "fn(input: string) -> [string]",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/exec",
"!doc": "Executes a search for a match in a specified string. Returns a result array, or null."
},
"test": {
"!type": "fn(input: string) -> bool",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/test",
"!doc": "Executes the search for a match between a regular expression and a specified string. Returns true or false."
},
"global": {
"!type": "bool",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp",
"!doc": "Creates a regular expression object for matching text with a pattern."
},
"ignoreCase": {
"!type": "bool",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp",
"!doc": "Creates a regular expression object for matching text with a pattern."
},
"multiline": {
"!type": "bool",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/multiline",
"!doc": "Reflects whether or not to search in strings across multiple lines.\n"
},
"source": {
"!type": "string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/source",
"!doc": "A read-only property that contains the text of the pattern, excluding the forward slashes.\n"
},
"lastIndex": {
"!type": "number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/lastIndex",
"!doc": "A read/write integer property that specifies the index at which to start the next match."
}
},
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp",
"!doc": "Creates a regular expression object for matching text with a pattern."
},
"Date": {
"!type": "fn(ms: number)",
"parse": {
"!type": "fn(source: string) -> +Date",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/parse",
"!doc": "Parses a string representation of a date, and returns the number of milliseconds since January 1, 1970, 00:00:00 UTC."
},
"UTC": {
"!type": "fn(year: number, month: number, date: number, hour?: number, min?: number, sec?: number, ms?: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/UTC",
"!doc": "Accepts the same parameters as the longest form of the constructor, and returns the number of milliseconds in a Date object since January 1, 1970, 00:00:00, universal time."
},
"now": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/now",
"!doc": "Returns the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC."
},
"prototype": {
"toUTCString": {
"!type": "fn() -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toUTCString",
"!doc": "Converts a date to a string, using the universal time convention."
},
"toISOString": {
"!type": "fn() -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toISOString",
"!doc": "JavaScript provides a direct way to convert a date object into a string in ISO format, the ISO 8601 Extended Format."
},
"toDateString": {
"!type": "fn() -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toDateString",
"!doc": "Returns the date portion of a Date object in human readable form in American English."
},
"toTimeString": {
"!type": "fn() -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toTimeString",
"!doc": "Returns the time portion of a Date object in human readable form in American English."
},
"toLocaleDateString": {
"!type": "fn() -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toLocaleDateString",
"!doc": "Converts a date to a string, returning the \"date\" portion using the operating system's locale's conventions.\n"
},
"toLocaleTimeString": {
"!type": "fn() -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toLocaleTimeString",
"!doc": "Converts a date to a string, returning the \"time\" portion using the current locale's conventions."
},
"getTime": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getTime",
"!doc": "Returns the numeric value corresponding to the time for the specified date according to universal time."
},
"getFullYear": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getFullYear",
"!doc": "Returns the year of the specified date according to local time."
},
"getYear": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getYear",
"!doc": "Returns the year in the specified date according to local time."
},
"getMonth": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getMonth",
"!doc": "Returns the month in the specified date according to local time."
},
"getUTCMonth": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCMonth",
"!doc": "Returns the month of the specified date according to universal time.\n"
},
"getDate": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getDate",
"!doc": "Returns the day of the month for the specified date according to local time."
},
"getUTCDate": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCDate",
"!doc": "Returns the day (date) of the month in the specified date according to universal time.\n"
},
"getDay": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getDay",
"!doc": "Returns the day of the week for the specified date according to local time."
},
"getUTCDay": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCDay",
"!doc": "Returns the day of the week in the specified date according to universal time.\n"
},
"getHours": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getHours",
"!doc": "Returns the hour for the specified date according to local time."
},
"getUTCHours": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCHours",
"!doc": "Returns the hours in the specified date according to universal time.\n"
},
"getMinutes": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getMinutes",
"!doc": "Returns the minutes in the specified date according to local time."
},
"getUTCMinutes": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date",
"!doc": "Creates JavaScript Date instances which let you work with dates and times."
},
"getSeconds": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getSeconds",
"!doc": "Returns the seconds in the specified date according to local time."
},
"getUTCSeconds": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCSeconds",
"!doc": "Returns the seconds in the specified date according to universal time.\n"
},
"getMilliseconds": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getMilliseconds",
"!doc": "Returns the milliseconds in the specified date according to local time."
},
"getUTCMilliseconds": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getUTCMilliseconds",
"!doc": "Returns the milliseconds in the specified date according to universal time.\n"
},
"getTimezoneOffset": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset",
"!doc": "Returns the time-zone offset from UTC, in minutes, for the current locale."
},
"setTime": {
"!type": "fn(date: +Date) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setTime",
"!doc": "Sets the Date object to the time represented by a number of milliseconds since January 1, 1970, 00:00:00 UTC.\n"
},
"setFullYear": {
"!type": "fn(year: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setFullYear",
"!doc": "Sets the full year for a specified date according to local time.\n"
},
"setUTCFullYear": {
"!type": "fn(year: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCFullYear",
"!doc": "Sets the full year for a specified date according to universal time.\n"
},
"setMonth": {
"!type": "fn(month: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setMonth",
"!doc": "Set the month for a specified date according to local time."
},
"setUTCMonth": {
"!type": "fn(month: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCMonth",
"!doc": "Sets the month for a specified date according to universal time.\n"
},
"setDate": {
"!type": "fn(day: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setDate",
"!doc": "Sets the day of the month for a specified date according to local time."
},
"setUTCDate": {
"!type": "fn(day: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCDate",
"!doc": "Sets the day of the month for a specified date according to universal time.\n"
},
"setHours": {
"!type": "fn(hour: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setHours",
"!doc": "Sets the hours for a specified date according to local time, and returns the number of milliseconds since 1 January 1970 00:00:00 UTC until the time represented by the updated Date instance."
},
"setUTCHours": {
"!type": "fn(hour: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCHours",
"!doc": "Sets the hour for a specified date according to universal time.\n"
},
"setMinutes": {
"!type": "fn(min: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setMinutes",
"!doc": "Sets the minutes for a specified date according to local time."
},
"setUTCMinutes": {
"!type": "fn(min: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCMinutes",
"!doc": "Sets the minutes for a specified date according to universal time.\n"
},
"setSeconds": {
"!type": "fn(sec: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setSeconds",
"!doc": "Sets the seconds for a specified date according to local time."
},
"setUTCSeconds": {
"!type": "fn(sec: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCSeconds",
"!doc": "Sets the seconds for a specified date according to universal time.\n"
},
"setMilliseconds": {
"!type": "fn(ms: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setMilliseconds",
"!doc": "Sets the milliseconds for a specified date according to local time.\n"
},
"setUTCMilliseconds": {
"!type": "fn(ms: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/setUTCMilliseconds",
"!doc": "Sets the milliseconds for a specified date according to universal time.\n"
}
},
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date",
"!doc": "Creates JavaScript Date instances which let you work with dates and times."
},
"Error": {
"!type": "fn(message: string)",
"prototype": {
"name": {
"!type": "string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Error/name",
"!doc": "A name for the type of error."
},
"message": {
"!type": "string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Error/message",
"!doc": "A human-readable description of the error."
}
},
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Error",
"!doc": "Creates an error object."
},
"SyntaxError": {
"!type": "fn(message: string)",
"prototype": "Error.prototype",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/SyntaxError",
"!doc": "Represents an error when trying to interpret syntactically invalid code."
},
"ReferenceError": {
"!type": "fn(message: string)",
"prototype": "Error.prototype",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/ReferenceError",
"!doc": "Represents an error when a non-existent variable is referenced."
},
"URIError": {
"!type": "fn(message: string)",
"prototype": "Error.prototype",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/URIError",
"!doc": "Represents an error when a malformed URI is encountered."
},
"EvalError": {
"!type": "fn(message: string)",
"prototype": "Error.prototype",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/EvalError",
"!doc": "Represents an error regarding the eval function."
},
"RangeError": {
"!type": "fn(message: string)",
"prototype": "Error.prototype",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RangeError",
"!doc": "Represents an error when a number is not within the correct range allowed."
},
"TypeError": {
"!type": "fn(message: string)",
"prototype": "Error.prototype",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/TypeError",
"!doc": "Represents an error an error when a value is not of the expected type."
},
"parseInt": {
"!type": "fn(string: string, radix?: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/parseInt",
"!doc": "Parses a string argument and returns an integer of the specified radix or base."
},
"parseFloat": {
"!type": "fn(string: string) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/parseFloat",
"!doc": "Parses a string argument and returns a floating point number."
},
"isNaN": {
"!type": "fn(value: number) -> bool",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/isNaN",
"!doc": "Determines whether a value is NaN or not. Be careful, this function is broken. You may be interested in ECMAScript 6 Number.isNaN."
},
"isFinite": {
"!type": "fn(value: number) -> bool",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/isFinite",
"!doc": "Determines whether the passed value is a finite number."
},
"eval": {
"!type": "fn(code: string) -> ?",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/eval",
"!doc": "Evaluates JavaScript code represented as a string."
},
"encodeURI": {
"!type": "fn(uri: string) -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURI",
"!doc": "Encodes a Uniform Resource Identifier (URI) by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character (will only be four escape sequences for characters composed of two \"surrogate\" characters)."
},
"encodeURIComponent": {
"!type": "fn(uri: string) -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURIComponent",
"!doc": "Encodes a Uniform Resource Identifier (URI) component by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character (will only be four escape sequences for characters composed of two \"surrogate\" characters)."
},
"decodeURI": {
"!type": "fn(uri: string) -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/decodeURI",
"!doc": "Decodes a Uniform Resource Identifier (URI) previously created by encodeURI or by a similar routine."
},
"decodeURIComponent": {
"!type": "fn(uri: string) -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/decodeURIComponent",
"!doc": "Decodes a Uniform Resource Identifier (URI) component previously created by encodeURIComponent or by a similar routine."
},
"Math": {
"E": {
"!type": "number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/E",
"!doc": "The base of natural logarithms, e, approximately 2.718."
},
"LN2": {
"!type": "number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LN2",
"!doc": "The natural logarithm of 2, approximately 0.693."
},
"LN10": {
"!type": "number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LN10",
"!doc": "The natural logarithm of 10, approximately 2.302."
},
"LOG2E": {
"!type": "number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LOG2E",
"!doc": "The base 2 logarithm of E (approximately 1.442)."
},
"LOG10E": {
"!type": "number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/LOG10E",
"!doc": "The base 10 logarithm of E (approximately 0.434)."
},
"SQRT1_2": {
"!type": "number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/SQRT1_2",
"!doc": "The square root of 1/2; equivalently, 1 over the square root of 2, approximately 0.707."
},
"SQRT2": {
"!type": "number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/SQRT2",
"!doc": "The square root of 2, approximately 1.414."
},
"PI": {
"!type": "number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/PI",
"!doc": "The ratio of the circumference of a circle to its diameter, approximately 3.14159."
},
"abs": {
"!type": "fn(number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/abs",
"!doc": "Returns the absolute value of a number."
},
"cos": {
"!type": "fn(number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/cos",
"!doc": "Returns the cosine of a number."
},
"sin": {
"!type": "fn(number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/sin",
"!doc": "Returns the sine of a number."
},
"tan": {
"!type": "fn(number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/tan",
"!doc": "Returns the tangent of a number."
},
"acos": {
"!type": "fn(number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/acos",
"!doc": "Returns the arccosine (in radians) of a number."
},
"asin": {
"!type": "fn(number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/asin",
"!doc": "Returns the arcsine (in radians) of a number."
},
"atan": {
"!type": "fn(number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/atan",
"!doc": "Returns the arctangent (in radians) of a number."
},
"atan2": {
"!type": "fn(y: number, x: number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/atan2",
"!doc": "Returns the arctangent of the quotient of its arguments."
},
"ceil": {
"!type": "fn(number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/ceil",
"!doc": "Returns the smallest integer greater than or equal to a number."
},
"floor": {
"!type": "fn(number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/floor",
"!doc": "Returns the largest integer less than or equal to a number."
},
"round": {
"!type": "fn(number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/round",
"!doc": "Returns the value of a number rounded to the nearest integer."
},
"exp": {
"!type": "fn(number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/exp",
"!doc": "Returns Ex, where x is the argument, and E is Euler's constant, the base of the natural logarithms."
},
"log": {
"!type": "fn(number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/log",
"!doc": "Returns the natural logarithm (base E) of a number."
},
"sqrt": {
"!type": "fn(number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/sqrt",
"!doc": "Returns the square root of a number."
},
"pow": {
"!type": "fn(number, number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/pow",
"!doc": "Returns base to the exponent power, that is, baseexponent."
},
"max": {
"!type": "fn(number, number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/max",
"!doc": "Returns the largest of zero or more numbers."
},
"min": {
"!type": "fn(number, number) -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/min",
"!doc": "Returns the smallest of zero or more numbers."
},
"random": {
"!type": "fn() -> number",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/random",
"!doc": "Returns a floating-point, pseudo-random number in the range [0, 1) that is, from 0 (inclusive) up to but not including 1 (exclusive), which you can then scale to your desired range."
},
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math",
"!doc": "A built-in object that has properties and methods for mathematical constants and functions."
},
"JSON": {
"parse": {
"!type": "fn(json: string) -> ?",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/parse",
"!doc": "Parse a string as JSON, optionally transforming the value produced by parsing."
},
"stringify": {
"!type": "fn(value: ?) -> string",
"!url": "https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify",
"!doc": "Convert a value to JSON, optionally replacing values if a replacer function is specified, or optionally including only the specified properties if a replacer array is specified."
},
"!url": "https://developer.mozilla.org/en-US/docs/JSON",
"!doc": "JSON (JavaScript Object Notation) is a data-interchange format. It closely resembles a subset of JavaScript syntax, although it is not a strict subset. (See JSON in the JavaScript Reference for full details.) It is useful when writing any kind of JavaScript-based application, including websites and browser extensions. For example, you might store user information in JSON format in a cookie, or you might store extension preferences in JSON in a string-valued browser preference."
}
}
}

1615
mix/qml/html/cm/infer.js

File diff suppressed because it is too large

139
mix/qml/html/cm/show-hint.js

@ -1,31 +1,57 @@
(function() { // CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict"; "use strict";
var HINT_ELEMENT_CLASS = "CodeMirror-hint"; var HINT_ELEMENT_CLASS = "CodeMirror-hint";
var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active"; var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
// This is the old interface, kept around for now to stay
// backwards-compatible.
CodeMirror.showHint = function(cm, getHints, options) { CodeMirror.showHint = function(cm, getHints, options) {
// We want a single cursor position. if (!getHints) return cm.showHint(options);
if (cm.somethingSelected()) return; if (options && options.async) getHints.async = true;
if (getHints == null) { var newOpts = {hint: getHints};
if (options && options.async) return; if (options) for (var prop in options) newOpts[prop] = options[prop];
else getHints = CodeMirror.hint.auto; return cm.showHint(newOpts);
};
var asyncRunID = 0;
function retrieveHints(getter, cm, options, then) {
if (getter.async) {
var id = ++asyncRunID;
getter(cm, function(hints) {
if (asyncRunID == id) then(hints);
}, options);
} else {
then(getter(cm, options));
} }
}
if (cm.state.completionActive) cm.state.completionActive.close(); CodeMirror.defineExtension("showHint", function(options) {
// We want a single cursor position.
if (this.listSelections().length > 1 || this.somethingSelected()) return;
var completion = cm.state.completionActive = new Completion(cm, getHints, options || {}); if (this.state.completionActive) this.state.completionActive.close();
CodeMirror.signal(cm, "startCompletion", cm); var completion = this.state.completionActive = new Completion(this, options);
if (completion.options.async) var getHints = completion.options.hint;
getHints(cm, function(hints) { completion.showHints(hints); }, completion.options); if (!getHints) return;
else
return completion.showHints(getHints(cm, completion.options));
};
function Completion(cm, getHints, options) { CodeMirror.signal(this, "startCompletion", this);
return retrieveHints(getHints, this, completion.options, function(hints) { completion.showHints(hints); });
});
function Completion(cm, options) {
this.cm = cm; this.cm = cm;
this.getHints = getHints; this.options = this.buildOptions(options);
this.options = options;
this.widget = this.onClose = null; this.widget = this.onClose = null;
} }
@ -46,7 +72,8 @@
pick: function(data, i) { pick: function(data, i) {
var completion = data.list[i]; var completion = data.list[i];
if (completion.hint) completion.hint(this.cm, data, completion); if (completion.hint) completion.hint(this.cm, data, completion);
else this.cm.replaceRange(getText(completion), completion.from||data.from, completion.to||data.to); else this.cm.replaceRange(getText(completion), completion.from || data.from,
completion.to || data.to, "complete");
CodeMirror.signal(data, "pick", completion); CodeMirror.signal(data, "pick", completion);
this.close(); this.close();
}, },
@ -54,7 +81,7 @@
showHints: function(data) { showHints: function(data) {
if (!data || !data.list.length || !this.active()) return this.close(); if (!data || !data.list.length || !this.active()) return this.close();
if (this.options.completeSingle != false && data.list.length == 1) if (this.options.completeSingle && data.list.length == 1)
this.pick(data, 0); this.pick(data, 0);
else else
this.showWidget(data); this.showWidget(data);
@ -65,7 +92,7 @@
CodeMirror.signal(data, "shown"); CodeMirror.signal(data, "shown");
var debounce = 0, completion = this, finished; var debounce = 0, completion = this, finished;
var closeOn = this.options.closeCharacters || /[\s()\[\]{};:>,]/; var closeOn = this.options.closeCharacters;
var startPos = this.cm.getCursor(), startLen = this.cm.getLine(startPos.line).length; var startPos = this.cm.getCursor(), startLen = this.cm.getLine(startPos.line).length;
var requestAnimationFrame = window.requestAnimationFrame || function(fn) { var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
@ -84,10 +111,7 @@
function update() { function update() {
if (finished) return; if (finished) return;
CodeMirror.signal(data, "update"); CodeMirror.signal(data, "update");
if (completion.options.async) retrieveHints(completion.options.hint, completion.cm, completion.options, finishUpdate);
completion.getHints(completion.cm, finishUpdate, completion.options);
else
finishUpdate(completion.getHints(completion.cm, completion.options));
} }
function finishUpdate(data_) { function finishUpdate(data_) {
data = data_; data = data_;
@ -118,6 +142,17 @@
} }
this.cm.on("cursorActivity", activity); this.cm.on("cursorActivity", activity);
this.onClose = done; this.onClose = done;
},
buildOptions: function(options) {
var editor = this.cm.options.hintOptions;
var out = {};
for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
if (editor) for (var prop in editor)
if (editor[prop] !== undefined) out[prop] = editor[prop];
if (options) for (var prop in options)
if (options[prop] !== undefined) out[prop] = options[prop];
return out;
} }
}; };
@ -126,7 +161,7 @@
else return completion.text; else return completion.text;
} }
function buildKeyMap(options, handle) { function buildKeyMap(completion, handle) {
var baseMap = { var baseMap = {
Up: function() {handle.moveFocus(-1);}, Up: function() {handle.moveFocus(-1);},
Down: function() {handle.moveFocus(1);}, Down: function() {handle.moveFocus(1);},
@ -138,7 +173,8 @@
Tab: handle.pick, Tab: handle.pick,
Esc: handle.close Esc: handle.close
}; };
var ourMap = options.customKeys ? {} : baseMap; var custom = completion.options.customKeys;
var ourMap = custom ? {} : baseMap;
function addBinding(key, val) { function addBinding(key, val) {
var bound; var bound;
if (typeof val != "string") if (typeof val != "string")
@ -150,12 +186,13 @@
bound = val; bound = val;
ourMap[key] = bound; ourMap[key] = bound;
} }
if (options.customKeys) if (custom)
for (var key in options.customKeys) if (options.customKeys.hasOwnProperty(key)) for (var key in custom) if (custom.hasOwnProperty(key))
addBinding(key, options.customKeys[key]); addBinding(key, custom[key]);
if (options.extraKeys) var extra = completion.options.extraKeys;
for (var key in options.extraKeys) if (options.extraKeys.hasOwnProperty(key)) if (extra)
addBinding(key, options.extraKeys[key]); for (var key in extra) if (extra.hasOwnProperty(key))
addBinding(key, extra[key]);
return ourMap; return ourMap;
} }
@ -169,11 +206,11 @@
function Widget(completion, data) { function Widget(completion, data) {
this.completion = completion; this.completion = completion;
this.data = data; this.data = data;
var widget = this, cm = completion.cm, options = completion.options; var widget = this, cm = completion.cm;
var hints = this.hints = document.createElement("ul"); var hints = this.hints = document.createElement("ul");
hints.className = "CodeMirror-hints"; hints.className = "CodeMirror-hints";
this.selectedHint = options.getDefaultSelection ? options.getDefaultSelection(cm,options,data) : 0; this.selectedHint = data.selectedHint || 0;
var completions = data.list; var completions = data.list;
for (var i = 0; i < completions.length; ++i) { for (var i = 0; i < completions.length; ++i) {
@ -186,19 +223,19 @@
elt.hintId = i; elt.hintId = i;
} }
var pos = cm.cursorCoords(options.alignWithWord !== false ? data.from : null); var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
var left = pos.left, top = pos.bottom, below = true; var left = pos.left, top = pos.bottom, below = true;
hints.style.left = left + "px"; hints.style.left = left + "px";
hints.style.top = top + "px"; hints.style.top = top + "px";
// If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth); var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight); var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
(options.container || document.body).appendChild(hints); (completion.options.container || document.body).appendChild(hints);
var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH; var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
if (overlapY > 0) { if (overlapY > 0) {
var height = box.bottom - box.top, curTop = box.top - (pos.bottom - pos.top); var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
if (curTop - height > 0) { // Fits above cursor if (curTop - height > 0) { // Fits above cursor
hints.style.top = (top = curTop - height) + "px"; hints.style.top = (top = pos.top - height) + "px";
below = false; below = false;
} else if (height > winH) { } else if (height > winH) {
hints.style.height = (winH - 5) + "px"; hints.style.height = (winH - 5) + "px";
@ -211,7 +248,7 @@
} }
} }
} }
var overlapX = box.left - winW; var overlapX = box.right - winW;
if (overlapX > 0) { if (overlapX > 0) {
if (box.right - box.left > winW) { if (box.right - box.left > winW) {
hints.style.width = (winW - 5) + "px"; hints.style.width = (winW - 5) + "px";
@ -220,7 +257,7 @@
hints.style.left = (left = pos.left - overlapX) + "px"; hints.style.left = (left = pos.left - overlapX) + "px";
} }
cm.addKeyMap(this.keyMap = buildKeyMap(options, { cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); }, moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
setFocus: function(n) { widget.changeActive(n); }, setFocus: function(n) { widget.changeActive(n); },
menuSize: function() { return widget.screenAmount(); }, menuSize: function() { return widget.screenAmount(); },
@ -230,7 +267,7 @@
data: data data: data
})); }));
if (options.closeOnUnfocus !== false) { if (completion.options.closeOnUnfocus) {
var closingOnBlur; var closingOnBlur;
cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); }); cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); }); cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
@ -256,7 +293,7 @@
var t = getHintElement(hints, e.target || e.srcElement); var t = getHintElement(hints, e.target || e.srcElement);
if (t && t.hintId != null) { if (t && t.hintId != null) {
widget.changeActive(t.hintId); widget.changeActive(t.hintId);
if (options.completeOnSingleClick) widget.pick(); if (completion.options.completeOnSingleClick) widget.pick();
} }
}); });
@ -276,7 +313,7 @@
this.completion.cm.removeKeyMap(this.keyMap); this.completion.cm.removeKeyMap(this.keyMap);
var cm = this.completion.cm; var cm = this.completion.cm;
if (this.completion.options.closeOnUnfocus !== false) { if (this.completion.options.closeOnUnfocus) {
cm.off("blur", this.onBlur); cm.off("blur", this.onBlur);
cm.off("focus", this.onFocus); cm.off("focus", this.onFocus);
} }
@ -340,4 +377,18 @@
}); });
CodeMirror.commands.autocomplete = CodeMirror.showHint; CodeMirror.commands.autocomplete = CodeMirror.showHint;
})();
var defaultOptions = {
hint: CodeMirror.hint.auto,
completeSingle: true,
alignWithWord: true,
closeCharacters: /[\s()\[\]{};:>,]/,
closeOnUnfocus: true,
completeOnSingleClick: false,
container: null,
customKeys: null,
extraKeys: null
};
CodeMirror.defineOption("hintOptions", null);
});

26
mix/qml/html/cm/signal.js

@ -0,0 +1,26 @@
(function(root, mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
return mod(exports);
if (typeof define == "function" && define.amd) // AMD
return define(["exports"], mod);
mod((root.tern || (root.tern = {})).signal = {}); // Plain browser env
})(this, function(exports) {
function on(type, f) {
var handlers = this._handlers || (this._handlers = Object.create(null));
(handlers[type] || (handlers[type] = [])).push(f);
}
function off(type, f) {
var arr = this._handlers && this._handlers[type];
if (arr) for (var i = 0; i < arr.length; ++i)
if (arr[i] == f) { arr.splice(i, 1); break; }
}
function signal(type, a1, a2, a3, a4) {
var arr = this._handlers && this._handlers[type];
if (arr) for (var i = 0; i < arr.length; ++i) arr[i].call(this, a1, a2, a3, a4);
}
exports.mixin = function(obj) {
obj.on = on; obj.off = off; obj.signal = signal;
return obj;
};
});

697
mix/qml/html/cm/tern.js

@ -0,0 +1,697 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
// Glue code between CodeMirror and Tern.
//
// Create a CodeMirror.TernServer to wrap an actual Tern server,
// register open documents (CodeMirror.Doc instances) with it, and
// call its methods to activate the assisting functions that Tern
// provides.
//
// Options supported (all optional):
// * defs: An array of JSON definition data structures.
// * plugins: An object mapping plugin names to configuration
// options.
// * getFile: A function(name, c) that can be used to access files in
// the project that haven't been loaded yet. Simply do c(null) to
// indicate that a file is not available.
// * fileFilter: A function(value, docName, doc) that will be applied
// to documents before passing them on to Tern.
// * switchToDoc: A function(name, doc) that should, when providing a
// multi-file view, switch the view or focus to the named file.
// * showError: A function(editor, message) that can be used to
// override the way errors are displayed.
// * completionTip: Customize the content in tooltips for completions.
// Is passed a single argument—the completion's data as returned by
// Tern—and may return a string, DOM node, or null to indicate that
// no tip should be shown. By default the docstring is shown.
// * typeTip: Like completionTip, but for the tooltips shown for type
// queries.
// * responseFilter: A function(doc, query, request, error, data) that
// will be applied to the Tern responses before treating them
//
//
// It is possible to run the Tern server in a web worker by specifying
// these additional options:
// * useWorker: Set to true to enable web worker mode. You'll probably
// want to feature detect the actual value you use here, for example
// !!window.Worker.
// * workerScript: The main script of the worker. Point this to
// wherever you are hosting worker.js from this directory.
// * workerDeps: An array of paths pointing (relative to workerScript)
// to the Acorn and Tern libraries and any Tern plugins you want to
// load. Or, if you minified those into a single script and included
// them in the workerScript, simply leave this undefined.
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
// declare global: tern
CodeMirror.TernServer = function(options) {
var self = this;
this.options = options || {};
var plugins = this.options.plugins || (this.options.plugins = {});
if (!plugins.doc_comment) plugins.doc_comment = true;
if (this.options.useWorker) {
this.server = new WorkerServer(this);
} else {
this.server = new tern.Server({
getFile: function(name, c) { return getFile(self, name, c); },
async: true,
defs: this.options.defs || [],
plugins: plugins
});
}
this.docs = Object.create(null);
this.trackChange = function(doc, change) { trackChange(self, doc, change); };
this.cachedArgHints = null;
this.activeArgHints = null;
this.jumpStack = [];
this.getHint = function(cm, c) { return hint(self, cm, c); };
this.getHint.async = true;
};
CodeMirror.TernServer.prototype = {
addDoc: function(name, doc) {
var data = {doc: doc, name: name, changed: null};
this.server.addFile(name, docValue(this, data));
CodeMirror.on(doc, "change", this.trackChange);
return this.docs[name] = data;
},
delDoc: function(id) {
var found = resolveDoc(this, id);
if (!found) return;
CodeMirror.off(found.doc, "change", this.trackChange);
delete this.docs[found.name];
this.server.delFile(found.name);
},
hideDoc: function(id) {
closeArgHints(this);
var found = resolveDoc(this, id);
if (found && found.changed) sendDoc(this, found);
},
complete: function(cm) {
cm.showHint({hint: this.getHint});
},
showType: function(cm, pos, c) { showContextInfo(this, cm, pos, "type", c); },
showDocs: function(cm, pos, c) { showContextInfo(this, cm, pos, "documentation", c); },
updateArgHints: function(cm) { updateArgHints(this, cm); },
jumpToDef: function(cm) { jumpToDef(this, cm); },
jumpBack: function(cm) { jumpBack(this, cm); },
rename: function(cm) { rename(this, cm); },
selectName: function(cm) { selectName(this, cm); },
request: function (cm, query, c, pos) {
var self = this;
var doc = findDoc(this, cm.getDoc());
var request = buildRequest(this, doc, query, pos);
this.server.request(request, function (error, data) {
if (!error && self.options.responseFilter)
data = self.options.responseFilter(doc, query, request, error, data);
c(error, data);
});
},
destroy: function () {
if (this.worker) {
this.worker.terminate();
this.worker = null;
}
}
};
var Pos = CodeMirror.Pos;
var cls = "CodeMirror-Tern-";
var bigDoc = 250;
function getFile(ts, name, c) {
var buf = ts.docs[name];
if (buf)
c(docValue(ts, buf));
else if (ts.options.getFile)
ts.options.getFile(name, c);
else
c(null);
}
function findDoc(ts, doc, name) {
for (var n in ts.docs) {
var cur = ts.docs[n];
if (cur.doc == doc) return cur;
}
if (!name) for (var i = 0;; ++i) {
n = "[doc" + (i || "") + "]";
if (!ts.docs[n]) { name = n; break; }
}
return ts.addDoc(name, doc);
}
function resolveDoc(ts, id) {
if (typeof id == "string") return ts.docs[id];
if (id instanceof CodeMirror) id = id.getDoc();
if (id instanceof CodeMirror.Doc) return findDoc(ts, id);
}
function trackChange(ts, doc, change) {
var data = findDoc(ts, doc);
var argHints = ts.cachedArgHints;
if (argHints && argHints.doc == doc && cmpPos(argHints.start, change.to) <= 0)
ts.cachedArgHints = null;
var changed = data.changed;
if (changed == null)
data.changed = changed = {from: change.from.line, to: change.from.line};
var end = change.from.line + (change.text.length - 1);
if (change.from.line < changed.to) changed.to = changed.to - (change.to.line - end);
if (end >= changed.to) changed.to = end + 1;
if (changed.from > change.from.line) changed.from = change.from.line;
if (doc.lineCount() > bigDoc && change.to - changed.from > 100) setTimeout(function() {
if (data.changed && data.changed.to - data.changed.from > 100) sendDoc(ts, data);
}, 200);
}
function sendDoc(ts, doc) {
ts.server.request({files: [{type: "full", name: doc.name, text: docValue(ts, doc)}]}, function(error) {
if (error) window.console.error(error);
else doc.changed = null;
});
}
// Completion
function hint(ts, cm, c) {
ts.request(cm, {type: "completions", types: true, docs: true, urls: true}, function(error, data) {
if (error) return showError(ts, cm, error);
var completions = [], after = "";
var from = data.start, to = data.end;
if (cm.getRange(Pos(from.line, from.ch - 2), from) == "[\"" &&
cm.getRange(to, Pos(to.line, to.ch + 2)) != "\"]")
after = "\"]";
for (var i = 0; i < data.completions.length; ++i) {
var completion = data.completions[i], className = typeToIcon(completion.type);
if (data.guess) className += " " + cls + "guess";
completions.push({text: completion.name + after,
displayText: completion.name,
className: className,
data: completion});
}
var obj = {from: from, to: to, list: completions};
var tooltip = null;
CodeMirror.on(obj, "close", function() { remove(tooltip); });
CodeMirror.on(obj, "update", function() { remove(tooltip); });
CodeMirror.on(obj, "select", function(cur, node) {
remove(tooltip);
var content = ts.options.completionTip ? ts.options.completionTip(cur.data) : cur.data.doc;
if (content) {
tooltip = makeTooltip(node.parentNode.getBoundingClientRect().right + window.pageXOffset,
node.getBoundingClientRect().top + window.pageYOffset, content);
tooltip.className += " " + cls + "hint-doc";
}
});
c(obj);
});
}
function typeToIcon(type) {
var suffix;
if (type == "?") suffix = "unknown";
else if (type == "number" || type == "string" || type == "bool") suffix = type;
else if (/^fn\(/.test(type)) suffix = "fn";
else if (/^\[/.test(type)) suffix = "array";
else suffix = "object";
return cls + "completion " + cls + "completion-" + suffix;
}
// Type queries
function showContextInfo(ts, cm, pos, queryName, c) {
ts.request(cm, queryName, function(error, data) {
if (error) return showError(ts, cm, error);
if (ts.options.typeTip) {
var tip = ts.options.typeTip(data);
} else {
var tip = elt("span", null, elt("strong", null, data.type || "not found"));
if (data.doc)
tip.appendChild(document.createTextNode(" — " + data.doc));
if (data.url) {
tip.appendChild(document.createTextNode(" "));
var child = tip.appendChild(elt("a", null, "[docs]"));
child.href = data.url;
child.target = "_blank";
}
}
tempTooltip(cm, tip);
if (c) c();
}, pos);
}
// Maintaining argument hints
function updateArgHints(ts, cm) {
closeArgHints(ts);
if (cm.somethingSelected()) return;
var state = cm.getTokenAt(cm.getCursor()).state;
var inner = CodeMirror.innerMode(cm.getMode(), state);
if (inner.mode.name != "javascript") return;
var lex = inner.state.lexical;
if (lex.info != "call") return;
var ch, argPos = lex.pos || 0, tabSize = cm.getOption("tabSize");
for (var line = cm.getCursor().line, e = Math.max(0, line - 9), found = false; line >= e; --line) {
var str = cm.getLine(line), extra = 0;
for (var pos = 0;;) {
var tab = str.indexOf("\t", pos);
if (tab == -1) break;
extra += tabSize - (tab + extra) % tabSize - 1;
pos = tab + 1;
}
ch = lex.column - extra;
if (str.charAt(ch) == "(") {found = true; break;}
}
if (!found) return;
var start = Pos(line, ch);
var cache = ts.cachedArgHints;
if (cache && cache.doc == cm.getDoc() && cmpPos(start, cache.start) == 0)
return showArgHints(ts, cm, argPos);
ts.request(cm, {type: "type", preferFunction: true, end: start}, function(error, data) {
if (error || !data.type || !(/^fn\(/).test(data.type)) return;
ts.cachedArgHints = {
start: pos,
type: parseFnType(data.type),
name: data.exprName || data.name || "fn",
guess: data.guess,
doc: cm.getDoc()
};
showArgHints(ts, cm, argPos);
});
}
function showArgHints(ts, cm, pos) {
closeArgHints(ts);
var cache = ts.cachedArgHints, tp = cache.type;
var tip = elt("span", cache.guess ? cls + "fhint-guess" : null,
elt("span", cls + "fname", cache.name), "(");
for (var i = 0; i < tp.args.length; ++i) {
if (i) tip.appendChild(document.createTextNode(", "));
var arg = tp.args[i];
tip.appendChild(elt("span", cls + "farg" + (i == pos ? " " + cls + "farg-current" : ""), arg.name || "?"));
if (arg.type != "?") {
tip.appendChild(document.createTextNode(":\u00a0"));
tip.appendChild(elt("span", cls + "type", arg.type));
}
}
tip.appendChild(document.createTextNode(tp.rettype ? ") ->\u00a0" : ")"));
if (tp.rettype) tip.appendChild(elt("span", cls + "type", tp.rettype));
var place = cm.cursorCoords(null, "page");
ts.activeArgHints = makeTooltip(place.right + 1, place.bottom, tip);
}
function parseFnType(text) {
var args = [], pos = 3;
function skipMatching(upto) {
var depth = 0, start = pos;
for (;;) {
var next = text.charAt(pos);
if (upto.test(next) && !depth) return text.slice(start, pos);
if (/[{\[\(]/.test(next)) ++depth;
else if (/[}\]\)]/.test(next)) --depth;
++pos;
}
}
// Parse arguments
if (text.charAt(pos) != ")") for (;;) {
var name = text.slice(pos).match(/^([^, \(\[\{]+): /);
if (name) {
pos += name[0].length;
name = name[1];
}
args.push({name: name, type: skipMatching(/[\),]/)});
if (text.charAt(pos) == ")") break;
pos += 2;
}
var rettype = text.slice(pos).match(/^\) -> (.*)$/);
return {args: args, rettype: rettype && rettype[1]};
}
// Moving to the definition of something
function jumpToDef(ts, cm) {
function inner(varName) {
var req = {type: "definition", variable: varName || null};
var doc = findDoc(ts, cm.getDoc());
ts.server.request(buildRequest(ts, doc, req), function(error, data) {
if (error) return showError(ts, cm, error);
if (!data.file && data.url) { window.open(data.url); return; }
if (data.file) {
var localDoc = ts.docs[data.file], found;
if (localDoc && (found = findContext(localDoc.doc, data))) {
ts.jumpStack.push({file: doc.name,
start: cm.getCursor("from"),
end: cm.getCursor("to")});
moveTo(ts, doc, localDoc, found.start, found.end);
return;
}
}
showError(ts, cm, "Could not find a definition.");
});
}
if (!atInterestingExpression(cm))
dialog(cm, "Jump to variable", function(name) { if (name) inner(name); });
else
inner();
}
function jumpBack(ts, cm) {
var pos = ts.jumpStack.pop(), doc = pos && ts.docs[pos.file];
if (!doc) return;
moveTo(ts, findDoc(ts, cm.getDoc()), doc, pos.start, pos.end);
}
function moveTo(ts, curDoc, doc, start, end) {
doc.doc.setSelection(start, end);
if (curDoc != doc && ts.options.switchToDoc) {
closeArgHints(ts);
ts.options.switchToDoc(doc.name, doc.doc);
}
}
// The {line,ch} representation of positions makes this rather awkward.
function findContext(doc, data) {
var before = data.context.slice(0, data.contextOffset).split("\n");
var startLine = data.start.line - (before.length - 1);
var start = Pos(startLine, (before.length == 1 ? data.start.ch : doc.getLine(startLine).length) - before[0].length);
var text = doc.getLine(startLine).slice(start.ch);
for (var cur = startLine + 1; cur < doc.lineCount() && text.length < data.context.length; ++cur)
text += "\n" + doc.getLine(cur);
if (text.slice(0, data.context.length) == data.context) return data;
var cursor = doc.getSearchCursor(data.context, 0, false);
var nearest, nearestDist = Infinity;
while (cursor.findNext()) {
var from = cursor.from(), dist = Math.abs(from.line - start.line) * 10000;
if (!dist) dist = Math.abs(from.ch - start.ch);
if (dist < nearestDist) { nearest = from; nearestDist = dist; }
}
if (!nearest) return null;
if (before.length == 1)
nearest.ch += before[0].length;
else
nearest = Pos(nearest.line + (before.length - 1), before[before.length - 1].length);
if (data.start.line == data.end.line)
var end = Pos(nearest.line, nearest.ch + (data.end.ch - data.start.ch));
else
var end = Pos(nearest.line + (data.end.line - data.start.line), data.end.ch);
return {start: nearest, end: end};
}
function atInterestingExpression(cm) {
var pos = cm.getCursor("end"), tok = cm.getTokenAt(pos);
if (tok.start < pos.ch && (tok.type == "comment" || tok.type == "string")) return false;
return /[\w)\]]/.test(cm.getLine(pos.line).slice(Math.max(pos.ch - 1, 0), pos.ch + 1));
}
// Variable renaming
function rename(ts, cm) {
var token = cm.getTokenAt(cm.getCursor());
if (!/\w/.test(token.string)) return showError(ts, cm, "Not at a variable");
dialog(cm, "New name for " + token.string, function(newName) {
ts.request(cm, {type: "rename", newName: newName, fullDocs: true}, function(error, data) {
if (error) return showError(ts, cm, error);
applyChanges(ts, data.changes);
});
});
}
function selectName(ts, cm) {
var name = findDoc(ts, cm.doc).name;
ts.request(cm, {type: "refs"}, function(error, data) {
if (error) return showError(ts, cm, error);
var ranges = [], cur = 0;
for (var i = 0; i < data.refs.length; i++) {
var ref = data.refs[i];
if (ref.file == name) {
ranges.push({anchor: ref.start, head: ref.end});
if (cmpPos(cur, ref.start) >= 0 && cmpPos(cur, ref.end) <= 0)
cur = ranges.length - 1;
}
}
cm.setSelections(ranges, cur);
});
}
var nextChangeOrig = 0;
function applyChanges(ts, changes) {
var perFile = Object.create(null);
for (var i = 0; i < changes.length; ++i) {
var ch = changes[i];
(perFile[ch.file] || (perFile[ch.file] = [])).push(ch);
}
for (var file in perFile) {
var known = ts.docs[file], chs = perFile[file];;
if (!known) continue;
chs.sort(function(a, b) { return cmpPos(b.start, a.start); });
var origin = "*rename" + (++nextChangeOrig);
for (var i = 0; i < chs.length; ++i) {
var ch = chs[i];
known.doc.replaceRange(ch.text, ch.start, ch.end, origin);
}
}
}
// Generic request-building helper
function buildRequest(ts, doc, query, pos) {
var files = [], offsetLines = 0, allowFragments = !query.fullDocs;
if (!allowFragments) delete query.fullDocs;
if (typeof query == "string") query = {type: query};
query.lineCharPositions = true;
if (query.end == null) {
query.end = pos || doc.doc.getCursor("end");
if (doc.doc.somethingSelected())
query.start = doc.doc.getCursor("start");
}
var startPos = query.start || query.end;
if (doc.changed) {
if (doc.doc.lineCount() > bigDoc && allowFragments !== false &&
doc.changed.to - doc.changed.from < 100 &&
doc.changed.from <= startPos.line && doc.changed.to > query.end.line) {
files.push(getFragmentAround(doc, startPos, query.end));
query.file = "#0";
var offsetLines = files[0].offsetLines;
if (query.start != null) query.start = Pos(query.start.line - -offsetLines, query.start.ch);
query.end = Pos(query.end.line - offsetLines, query.end.ch);
} else {
files.push({type: "full",
name: doc.name,
text: docValue(ts, doc)});
query.file = doc.name;
doc.changed = null;
}
} else {
query.file = doc.name;
}
for (var name in ts.docs) {
var cur = ts.docs[name];
if (cur.changed && cur != doc) {
files.push({type: "full", name: cur.name, text: docValue(ts, cur)});
cur.changed = null;
}
}
return {query: query, files: files};
}
function getFragmentAround(data, start, end) {
var doc = data.doc;
var minIndent = null, minLine = null, endLine, tabSize = 4;
for (var p = start.line - 1, min = Math.max(0, p - 50); p >= min; --p) {
var line = doc.getLine(p), fn = line.search(/\bfunction\b/);
if (fn < 0) continue;
var indent = CodeMirror.countColumn(line, null, tabSize);
if (minIndent != null && minIndent <= indent) continue;
minIndent = indent;
minLine = p;
}
if (minLine == null) minLine = min;
var max = Math.min(doc.lastLine(), end.line + 20);
if (minIndent == null || minIndent == CodeMirror.countColumn(doc.getLine(start.line), null, tabSize))
endLine = max;
else for (endLine = end.line + 1; endLine < max; ++endLine) {
var indent = CodeMirror.countColumn(doc.getLine(endLine), null, tabSize);
if (indent <= minIndent) break;
}
var from = Pos(minLine, 0);
return {type: "part",
name: data.name,
offsetLines: from.line,
text: doc.getRange(from, Pos(endLine, 0))};
}
// Generic utilities
var cmpPos = CodeMirror.cmpPos;
function elt(tagname, cls /*, ... elts*/) {
var e = document.createElement(tagname);
if (cls) e.className = cls;
for (var i = 2; i < arguments.length; ++i) {
var elt = arguments[i];
if (typeof elt == "string") elt = document.createTextNode(elt);
e.appendChild(elt);
}
return e;
}
function dialog(cm, text, f) {
if (cm.openDialog)
cm.openDialog(text + ": <input type=text>", f);
else
f(prompt(text, ""));
}
// Tooltips
function tempTooltip(cm, content) {
if (cm.state.ternTooltip) remove(cm.state.ternTooltip);
var where = cm.cursorCoords();
var tip = cm.state.ternTooltip = makeTooltip(where.right + 1, where.bottom, content);
function maybeClear() {
old = true;
if (!mouseOnTip) clear();
}
function clear() {
cm.state.ternTooltip = null;
if (!tip.parentNode) return;
cm.off("cursorActivity", clear);
cm.off('blur', clear);
cm.off('scroll', clear);
fadeOut(tip);
}
var mouseOnTip = false, old = false;
CodeMirror.on(tip, "mousemove", function() { mouseOnTip = true; });
CodeMirror.on(tip, "mouseout", function(e) {
if (!CodeMirror.contains(tip, e.relatedTarget || e.toElement)) {
if (old) clear();
else mouseOnTip = false;
}
});
setTimeout(maybeClear, 1700);
cm.on("cursorActivity", clear);
cm.on('blur', clear);
cm.on('scroll', clear);
}
function makeTooltip(x, y, content) {
var node = elt("div", cls + "tooltip", content);
node.style.left = x + "px";
node.style.top = y + "px";
document.body.appendChild(node);
return node;
}
function remove(node) {
var p = node && node.parentNode;
if (p) p.removeChild(node);
}
function fadeOut(tooltip) {
tooltip.style.opacity = "0";
setTimeout(function() { remove(tooltip); }, 1100);
}
function showError(ts, cm, msg) {
if (ts.options.showError)
ts.options.showError(cm, msg);
else
tempTooltip(cm, String(msg));
}
function closeArgHints(ts) {
if (ts.activeArgHints) { remove(ts.activeArgHints); ts.activeArgHints = null; }
}
function docValue(ts, doc) {
var val = doc.doc.getValue();
if (ts.options.fileFilter) val = ts.options.fileFilter(val, doc.name, doc.doc);
return val;
}
// Worker wrapper
function WorkerServer(ts) {
var worker = ts.worker = new Worker(ts.options.workerScript);
worker.postMessage({type: "init",
defs: ts.options.defs,
plugins: ts.options.plugins,
scripts: ts.options.workerDeps});
var msgId = 0, pending = {};
function send(data, c) {
if (c) {
data.id = ++msgId;
pending[msgId] = c;
}
worker.postMessage(data);
}
worker.onmessage = function(e) {
var data = e.data;
if (data.type == "getFile") {
getFile(ts, data.name, function(err, text) {
send({type: "getFile", err: String(err), text: text, id: data.id});
});
} else if (data.type == "debug") {
window.console.log(data.message);
} else if (data.id && pending[data.id]) {
pending[data.id](data.err, data.body);
delete pending[data.id];
}
};
worker.onerror = function(e) {
for (var id in pending) pending[id](e);
pending = {};
};
this.addFile = function(name, text) { send({type: "add", name: name, text: text}); };
this.delFile = function(name) { send({type: "del", name: name}); };
this.request = function(body, c) { send({type: "req", body: body}, c); };
}
});

994
mix/qml/html/cm/ternserver.js

@ -0,0 +1,994 @@
// The Tern server object
// A server is a stateful object that manages the analysis for a
// project, and defines an interface for querying the code in the
// project.
(function(root, mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
return mod(exports, require("./infer"), require("./signal"),
require("acorn/acorn"), require("acorn/util/walk"));
if (typeof define == "function" && define.amd) // AMD
return define(["exports", "./infer", "./signal", "acorn/acorn", "acorn/util/walk"], mod);
mod(root.tern || (root.tern = {}), tern, tern.signal, acorn, acorn.walk); // Plain browser env
})(this, function(exports, infer, signal, acorn, walk) {
"use strict";
var plugins = Object.create(null);
exports.registerPlugin = function(name, init) { plugins[name] = init; };
var defaultOptions = exports.defaultOptions = {
debug: false,
async: false,
getFile: function(_f, c) { if (this.async) c(null, null); },
defs: [],
plugins: {},
fetchTimeout: 1000,
dependencyBudget: 20000,
reuseInstances: true,
stripCRs: false
};
var queryTypes = {
completions: {
takesFile: true,
run: findCompletions
},
properties: {
run: findProperties
},
type: {
takesFile: true,
run: findTypeAt
},
documentation: {
takesFile: true,
run: findDocs
},
definition: {
takesFile: true,
run: findDef
},
refs: {
takesFile: true,
fullFile: true,
run: findRefs
},
rename: {
takesFile: true,
fullFile: true,
run: buildRename
},
files: {
run: listFiles
}
};
exports.defineQueryType = function(name, desc) { queryTypes[name] = desc; };
function File(name, parent) {
this.name = name;
this.parent = parent;
this.scope = this.text = this.ast = this.lineOffsets = null;
}
File.prototype.asLineChar = function(pos) { return asLineChar(this, pos); };
function updateText(file, text, srv) {
file.text = srv.options.stripCRs ? text.replace(/\r\n/g, "\n") : text;
infer.withContext(srv.cx, function() {
file.ast = infer.parse(file.text, srv.passes, {directSourceFile: file, allowReturnOutsideFunction: true});
});
file.lineOffsets = null;
}
var Server = exports.Server = function(options) {
this.cx = null;
this.options = options || {};
for (var o in defaultOptions) if (!options.hasOwnProperty(o))
options[o] = defaultOptions[o];
this.handlers = Object.create(null);
this.files = [];
this.fileMap = Object.create(null);
this.needsPurge = [];
this.budgets = Object.create(null);
this.uses = 0;
this.pending = 0;
this.asyncError = null;
this.passes = Object.create(null);
this.defs = options.defs.slice(0);
for (var plugin in options.plugins) if (options.plugins.hasOwnProperty(plugin) && plugin in plugins) {
var init = plugins[plugin](this, options.plugins[plugin]);
if (init && init.defs) {
if (init.loadFirst) this.defs.unshift(init.defs);
else this.defs.push(init.defs);
}
if (init && init.passes) for (var type in init.passes) if (init.passes.hasOwnProperty(type))
(this.passes[type] || (this.passes[type] = [])).push(init.passes[type]);
}
this.reset();
};
Server.prototype = signal.mixin({
addFile: function(name, /*optional*/ text, parent) {
// Don't crash when sloppy plugins pass non-existent parent ids
if (parent && !(parent in this.fileMap)) parent = null;
ensureFile(this, name, parent, text);
},
delFile: function(name) {
var file = this.findFile(name);
if (file) {
this.needsPurge.push(file.name);
this.files.splice(this.files.indexOf(file), 1);
delete this.fileMap[name];
}
},
reset: function() {
this.signal("reset");
this.cx = new infer.Context(this.defs, this);
this.uses = 0;
this.budgets = Object.create(null);
for (var i = 0; i < this.files.length; ++i) {
var file = this.files[i];
file.scope = null;
}
},
request: function(doc, c) {
var inv = invalidDoc(doc);
if (inv) return c(inv);
var self = this;
doRequest(this, doc, function(err, data) {
c(err, data);
if (self.uses > 40) {
self.reset();
analyzeAll(self, null, function(){});
}
});
},
findFile: function(name) {
return this.fileMap[name];
},
flush: function(c) {
var cx = this.cx;
analyzeAll(this, null, function(err) {
if (err) return c(err);
infer.withContext(cx, c);
});
},
startAsyncAction: function() {
++this.pending;
},
finishAsyncAction: function(err) {
if (err) this.asyncError = err;
if (--this.pending === 0) this.signal("everythingFetched");
}
});
function doRequest(srv, doc, c) {
if (doc.query && !queryTypes.hasOwnProperty(doc.query.type))
return c("No query type '" + doc.query.type + "' defined");
var query = doc.query;
// Respond as soon as possible when this just uploads files
if (!query) c(null, {});
var files = doc.files || [];
if (files.length) ++srv.uses;
for (var i = 0; i < files.length; ++i) {
var file = files[i];
if (file.type == "delete")
srv.delFile(file.name);
else
ensureFile(srv, file.name, null, file.type == "full" ? file.text : null);
}
var timeBudget = typeof doc.timeout == "number" ? [doc.timeout] : null;
if (!query) {
analyzeAll(srv, timeBudget, function(){});
return;
}
var queryType = queryTypes[query.type];
if (queryType.takesFile) {
if (typeof query.file != "string") return c(".query.file must be a string");
if (!/^#/.test(query.file)) ensureFile(srv, query.file, null);
}
analyzeAll(srv, timeBudget, function(err) {
if (err) return c(err);
var file = queryType.takesFile && resolveFile(srv, files, query.file);
if (queryType.fullFile && file.type == "part")
return c("Can't run a " + query.type + " query on a file fragment");
function run() {
var result;
try {
result = queryType.run(srv, query, file);
} catch (e) {
if (srv.options.debug && e.name != "TernError") console.error(e.stack);
return c(e);
}
c(null, result);
}
infer.withContext(srv.cx, timeBudget ? function() { infer.withTimeout(timeBudget[0], run); } : run);
});
}
function analyzeFile(srv, file) {
infer.withContext(srv.cx, function() {
file.scope = srv.cx.topScope;
srv.signal("beforeLoad", file);
infer.analyze(file.ast, file.name, file.scope, srv.passes);
srv.signal("afterLoad", file);
});
return file;
}
function ensureFile(srv, name, parent, text) {
var known = srv.findFile(name);
if (known) {
if (text != null) {
if (known.scope) {
srv.needsPurge.push(name);
known.scope = null;
}
updateText(known, text, srv);
}
if (parentDepth(srv, known.parent) > parentDepth(srv, parent)) {
known.parent = parent;
if (known.excluded) known.excluded = null;
}
return;
}
var file = new File(name, parent);
srv.files.push(file);
srv.fileMap[name] = file;
if (text != null) {
updateText(file, text, srv);
} else if (srv.options.async) {
srv.startAsyncAction();
srv.options.getFile(name, function(err, text) {
updateText(file, text || "", srv);
srv.finishAsyncAction(err);
});
} else {
updateText(file, srv.options.getFile(name) || "", srv);
}
}
function fetchAll(srv, c) {
var done = true, returned = false;
srv.files.forEach(function(file) {
if (file.text != null) return;
if (srv.options.async) {
done = false;
srv.options.getFile(file.name, function(err, text) {
if (err && !returned) { returned = true; return c(err); }
updateText(file, text || "", srv);
fetchAll(srv, c);
});
} else {
try {
updateText(file, srv.options.getFile(file.name) || "", srv);
} catch (e) { return c(e); }
}
});
if (done) c();
}
function waitOnFetch(srv, timeBudget, c) {
var done = function() {
srv.off("everythingFetched", done);
clearTimeout(timeout);
analyzeAll(srv, timeBudget, c);
};
srv.on("everythingFetched", done);
var timeout = setTimeout(done, srv.options.fetchTimeout);
}
function analyzeAll(srv, timeBudget, c) {
if (srv.pending) return waitOnFetch(srv, timeBudget, c);
var e = srv.fetchError;
if (e) { srv.fetchError = null; return c(e); }
if (srv.needsPurge.length > 0) infer.withContext(srv.cx, function() {
infer.purge(srv.needsPurge);
srv.needsPurge.length = 0;
});
var done = true;
// The second inner loop might add new files. The outer loop keeps
// repeating both inner loops until all files have been looked at.
for (var i = 0; i < srv.files.length;) {
var toAnalyze = [];
for (; i < srv.files.length; ++i) {
var file = srv.files[i];
if (file.text == null) done = false;
else if (file.scope == null && !file.excluded) toAnalyze.push(file);
}
toAnalyze.sort(function(a, b) {
return parentDepth(srv, a.parent) - parentDepth(srv, b.parent);
});
for (var j = 0; j < toAnalyze.length; j++) {
var file = toAnalyze[j];
if (file.parent && !chargeOnBudget(srv, file)) {
file.excluded = true;
} else if (timeBudget) {
var startTime = +new Date;
infer.withTimeout(timeBudget[0], function() { analyzeFile(srv, file); });
timeBudget[0] -= +new Date - startTime;
} else {
analyzeFile(srv, file);
}
}
}
if (done) c();
else waitOnFetch(srv, timeBudget, c);
}
function firstLine(str) {
var end = str.indexOf("\n");
if (end < 0) return str;
return str.slice(0, end);
}
function findMatchingPosition(line, file, near) {
var pos = Math.max(0, near - 500), closest = null;
if (!/^\s*$/.test(line)) for (;;) {
var found = file.indexOf(line, pos);
if (found < 0 || found > near + 500) break;
if (closest == null || Math.abs(closest - near) > Math.abs(found - near))
closest = found;
pos = found + line.length;
}
return closest;
}
function scopeDepth(s) {
for (var i = 0; s; ++i, s = s.prev) {}
return i;
}
function ternError(msg) {
var err = new Error(msg);
err.name = "TernError";
return err;
}
function resolveFile(srv, localFiles, name) {
var isRef = name.match(/^#(\d+)$/);
if (!isRef) return srv.findFile(name);
var file = localFiles[isRef[1]];
if (!file || file.type == "delete") throw ternError("Reference to unknown file " + name);
if (file.type == "full") return srv.findFile(file.name);
// This is a partial file
var realFile = file.backing = srv.findFile(file.name);
var offset = file.offset;
if (file.offsetLines) offset = {line: file.offsetLines, ch: 0};
file.offset = offset = resolvePos(realFile, file.offsetLines == null ? file.offset : {line: file.offsetLines, ch: 0}, true);
var line = firstLine(file.text);
var foundPos = findMatchingPosition(line, realFile.text, offset);
var pos = foundPos == null ? Math.max(0, realFile.text.lastIndexOf("\n", offset)) : foundPos;
var inObject, atFunction;
infer.withContext(srv.cx, function() {
infer.purge(file.name, pos, pos + file.text.length);
var text = file.text, m;
if (m = text.match(/(?:"([^"]*)"|([\w$]+))\s*:\s*function\b/)) {
var objNode = walk.findNodeAround(file.backing.ast, pos, "ObjectExpression");
if (objNode && objNode.node.objType)
inObject = {type: objNode.node.objType, prop: m[2] || m[1]};
}
if (foundPos && (m = line.match(/^(.*?)\bfunction\b/))) {
var cut = m[1].length, white = "";
for (var i = 0; i < cut; ++i) white += " ";
text = white + text.slice(cut);
atFunction = true;
}
var scopeStart = infer.scopeAt(realFile.ast, pos, realFile.scope);
var scopeEnd = infer.scopeAt(realFile.ast, pos + text.length, realFile.scope);
var scope = file.scope = scopeDepth(scopeStart) < scopeDepth(scopeEnd) ? scopeEnd : scopeStart;
file.ast = infer.parse(text, srv.passes, {directSourceFile: file, allowReturnOutsideFunction: true});
infer.analyze(file.ast, file.name, scope, srv.passes);
// This is a kludge to tie together the function types (if any)
// outside and inside of the fragment, so that arguments and
// return values have some information known about them.
tieTogether: if (inObject || atFunction) {
var newInner = infer.scopeAt(file.ast, line.length, scopeStart);
if (!newInner.fnType) break tieTogether;
if (inObject) {
var prop = inObject.type.getProp(inObject.prop);
prop.addType(newInner.fnType);
} else if (atFunction) {
var inner = infer.scopeAt(realFile.ast, pos + line.length, realFile.scope);
if (inner == scopeStart || !inner.fnType) break tieTogether;
var fOld = inner.fnType, fNew = newInner.fnType;
if (!fNew || (fNew.name != fOld.name && fOld.name)) break tieTogether;
for (var i = 0, e = Math.min(fOld.args.length, fNew.args.length); i < e; ++i)
fOld.args[i].propagate(fNew.args[i]);
fOld.self.propagate(fNew.self);
fNew.retval.propagate(fOld.retval);
}
}
});
return file;
}
// Budget management
function astSize(node) {
var size = 0;
walk.simple(node, {Expression: function() { ++size; }});
return size;
}
function parentDepth(srv, parent) {
var depth = 0;
while (parent) {
parent = srv.findFile(parent).parent;
++depth;
}
return depth;
}
function budgetName(srv, file) {
for (;;) {
var parent = srv.findFile(file.parent);
if (!parent.parent) break;
file = parent;
}
return file.name;
}
function chargeOnBudget(srv, file) {
var bName = budgetName(srv, file);
var size = astSize(file.ast);
var known = srv.budgets[bName];
if (known == null)
known = srv.budgets[bName] = srv.options.dependencyBudget;
if (known < size) return false;
srv.budgets[bName] = known - size;
return true;
}
// Query helpers
function isPosition(val) {
return typeof val == "number" || typeof val == "object" &&
typeof val.line == "number" && typeof val.ch == "number";
}
// Baseline query document validation
function invalidDoc(doc) {
if (doc.query) {
if (typeof doc.query.type != "string") return ".query.type must be a string";
if (doc.query.start && !isPosition(doc.query.start)) return ".query.start must be a position";
if (doc.query.end && !isPosition(doc.query.end)) return ".query.end must be a position";
}
if (doc.files) {
if (!Array.isArray(doc.files)) return "Files property must be an array";
for (var i = 0; i < doc.files.length; ++i) {
var file = doc.files[i];
if (typeof file != "object") return ".files[n] must be objects";
else if (typeof file.name != "string") return ".files[n].name must be a string";
else if (file.type == "delete") continue;
else if (typeof file.text != "string") return ".files[n].text must be a string";
else if (file.type == "part") {
if (!isPosition(file.offset) && typeof file.offsetLines != "number")
return ".files[n].offset must be a position";
} else if (file.type != "full") return ".files[n].type must be \"full\" or \"part\"";
}
}
}
var offsetSkipLines = 25;
function findLineStart(file, line) {
var text = file.text, offsets = file.lineOffsets || (file.lineOffsets = [0]);
var pos = 0, curLine = 0;
var storePos = Math.min(Math.floor(line / offsetSkipLines), offsets.length - 1);
var pos = offsets[storePos], curLine = storePos * offsetSkipLines;
while (curLine < line) {
++curLine;
pos = text.indexOf("\n", pos) + 1;
if (pos === 0) return null;
if (curLine % offsetSkipLines === 0) offsets.push(pos);
}
return pos;
}
var resolvePos = exports.resolvePos = function(file, pos, tolerant) {
if (typeof pos != "number") {
var lineStart = findLineStart(file, pos.line);
if (lineStart == null) {
if (tolerant) pos = file.text.length;
else throw ternError("File doesn't contain a line " + pos.line);
} else {
pos = lineStart + pos.ch;
}
}
if (pos > file.text.length) {
if (tolerant) pos = file.text.length;
else throw ternError("Position " + pos + " is outside of file.");
}
return pos;
};
function asLineChar(file, pos) {
if (!file) return {line: 0, ch: 0};
var offsets = file.lineOffsets || (file.lineOffsets = [0]);
var text = file.text, line, lineStart;
for (var i = offsets.length - 1; i >= 0; --i) if (offsets[i] <= pos) {
line = i * offsetSkipLines;
lineStart = offsets[i];
}
for (;;) {
var eol = text.indexOf("\n", lineStart);
if (eol >= pos || eol < 0) break;
lineStart = eol + 1;
++line;
}
return {line: line, ch: pos - lineStart};
}
var outputPos = exports.outputPos = function(query, file, pos) {
if (query.lineCharPositions) {
var out = asLineChar(file, pos);
if (file.type == "part")
out.line += file.offsetLines != null ? file.offsetLines : asLineChar(file.backing, file.offset).line;
return out;
} else {
return pos + (file.type == "part" ? file.offset : 0);
}
};
// Delete empty fields from result objects
function clean(obj) {
for (var prop in obj) if (obj[prop] == null) delete obj[prop];
return obj;
}
function maybeSet(obj, prop, val) {
if (val != null) obj[prop] = val;
}
// Built-in query types
function compareCompletions(a, b) {
if (typeof a != "string") { a = a.name; b = b.name; }
var aUp = /^[A-Z]/.test(a), bUp = /^[A-Z]/.test(b);
if (aUp == bUp) return a < b ? -1 : a == b ? 0 : 1;
else return aUp ? 1 : -1;
}
function isStringAround(node, start, end) {
return node.type == "Literal" && typeof node.value == "string" &&
node.start == start - 1 && node.end <= end + 1;
}
function pointInProp(objNode, point) {
for (var i = 0; i < objNode.properties.length; i++) {
var curProp = objNode.properties[i];
if (curProp.key.start <= point && curProp.key.end >= point)
return curProp;
}
}
var jsKeywords = ("break do instanceof typeof case else new var " +
"catch finally return void continue for switch while debugger " +
"function this with default if throw delete in try").split(" ");
function findCompletions(srv, query, file) {
if (query.end == null) throw ternError("missing .query.end field");
if (srv.passes.completion) for (var i = 0; i < srv.passes.completion.length; i++) {
var result = srv.passes.completion[i](file, query);
if (result) return result;
}
var wordStart = resolvePos(file, query.end), wordEnd = wordStart, text = file.text;
while (wordStart && acorn.isIdentifierChar(text.charCodeAt(wordStart - 1))) --wordStart;
if (query.expandWordForward !== false)
while (wordEnd < text.length && acorn.isIdentifierChar(text.charCodeAt(wordEnd))) ++wordEnd;
var word = text.slice(wordStart, wordEnd), completions = [], ignoreObj;
if (query.caseInsensitive) word = word.toLowerCase();
var wrapAsObjs = query.types || query.depths || query.docs || query.urls || query.origins;
function gather(prop, obj, depth, addInfo) {
// 'hasOwnProperty' and such are usually just noise, leave them
// out when no prefix is provided.
if (query.omitObjectPrototype !== false && obj == srv.cx.protos.Object && !word) return;
if (query.filter !== false && word &&
(query.caseInsensitive ? prop.toLowerCase() : prop).indexOf(word) !== 0) return;
if (ignoreObj && ignoreObj.props[prop]) return;
for (var i = 0; i < completions.length; ++i) {
var c = completions[i];
if ((wrapAsObjs ? c.name : c) == prop) return;
}
var rec = wrapAsObjs ? {name: prop} : prop;
completions.push(rec);
if (obj && (query.types || query.docs || query.urls || query.origins)) {
var val = obj.props[prop];
infer.resetGuessing();
var type = val.getType();
rec.guess = infer.didGuess();
if (query.types)
rec.type = infer.toString(val);
if (query.docs)
maybeSet(rec, "doc", val.doc || type && type.doc);
if (query.urls)
maybeSet(rec, "url", val.url || type && type.url);
if (query.origins)
maybeSet(rec, "origin", val.origin || type && type.origin);
}
if (query.depths) rec.depth = depth;
if (wrapAsObjs && addInfo) addInfo(rec);
}
var hookname, prop, objType, isKey;
var exprAt = infer.findExpressionAround(file.ast, null, wordStart, file.scope);
var memberExpr, objLit;
// Decide whether this is an object property, either in a member
// expression or an object literal.
if (exprAt) {
if (exprAt.node.type == "MemberExpression" && exprAt.node.object.end < wordStart) {
memberExpr = exprAt;
} else if (isStringAround(exprAt.node, wordStart, wordEnd)) {
var parent = infer.parentNode(exprAt.node, file.ast);
if (parent.type == "MemberExpression" && parent.property == exprAt.node)
memberExpr = {node: parent, state: exprAt.state};
} else if (exprAt.node.type == "ObjectExpression") {
var objProp = pointInProp(exprAt.node, wordEnd);
if (objProp) {
objLit = exprAt;
prop = isKey = objProp.key.name;
} else if (!word && !/:\s*$/.test(file.text.slice(0, wordStart))) {
objLit = exprAt;
prop = isKey = true;
}
}
}
if (objLit) {
// Since we can't use the type of the literal itself to complete
// its properties (it doesn't contain the information we need),
// we have to try asking the surrounding expression for type info.
objType = infer.typeFromContext(file.ast, objLit);
ignoreObj = objLit.node.objType;
} else if (memberExpr) {
prop = memberExpr.node.property;
prop = prop.type == "Literal" ? prop.value.slice(1) : prop.name;
memberExpr.node = memberExpr.node.object;
objType = infer.expressionType(memberExpr);
} else if (text.charAt(wordStart - 1) == ".") {
var pathStart = wordStart - 1;
while (pathStart && (text.charAt(pathStart - 1) == "." || acorn.isIdentifierChar(text.charCodeAt(pathStart - 1)))) pathStart--;
var path = text.slice(pathStart, wordStart - 1);
if (path) {
objType = infer.def.parsePath(path, file.scope).getObjType();
prop = word;
}
}
if (prop != null) {
srv.cx.completingProperty = prop;
if (objType) infer.forAllPropertiesOf(objType, gather);
if (!completions.length && query.guess !== false && objType && objType.guessProperties)
objType.guessProperties(function(p, o, d) {if (p != prop && p != "✖") gather(p, o, d);});
if (!completions.length && word.length >= 2 && query.guess !== false)
for (var prop in srv.cx.props) gather(prop, srv.cx.props[prop][0], 0);
hookname = "memberCompletion";
} else {
infer.forAllLocalsAt(file.ast, wordStart, file.scope, gather);
if (query.includeKeywords) jsKeywords.forEach(function(kw) {
gather(kw, null, 0, function(rec) { rec.isKeyword = true; });
});
hookname = "variableCompletion";
}
if (srv.passes[hookname])
srv.passes[hookname].forEach(function(hook) {hook(file, wordStart, wordEnd, gather);});
if (query.sort !== false) completions.sort(compareCompletions);
srv.cx.completingProperty = null;
return {start: outputPos(query, file, wordStart),
end: outputPos(query, file, wordEnd),
isProperty: !!prop,
isObjectKey: !!isKey,
completions: completions};
}
function findProperties(srv, query) {
var prefix = query.prefix, found = [];
for (var prop in srv.cx.props)
if (prop != "<i>" && (!prefix || prop.indexOf(prefix) === 0)) found.push(prop);
if (query.sort !== false) found.sort(compareCompletions);
return {completions: found};
}
var findExpr = exports.findQueryExpr = function(file, query, wide) {
if (query.end == null) throw ternError("missing .query.end field");
if (query.variable) {
var scope = infer.scopeAt(file.ast, resolvePos(file, query.end), file.scope);
return {node: {type: "Identifier", name: query.variable, start: query.end, end: query.end + 1},
state: scope};
} else {
var start = query.start && resolvePos(file, query.start), end = resolvePos(file, query.end);
var expr = infer.findExpressionAt(file.ast, start, end, file.scope);
if (expr) return expr;
expr = infer.findExpressionAround(file.ast, start, end, file.scope);
if (expr && (expr.node.type == "ObjectExpression" || wide ||
(start == null ? end : start) - expr.node.start < 20 || expr.node.end - end < 20))
return expr;
return null;
}
};
function findExprOrThrow(file, query, wide) {
var expr = findExpr(file, query, wide);
if (expr) return expr;
throw ternError("No expression at the given position.");
}
function ensureObj(tp) {
if (!tp || !(tp = tp.getType()) || !(tp instanceof infer.Obj)) return null;
return tp;
}
function findExprType(srv, query, file, expr) {
var type;
if (expr) {
infer.resetGuessing();
type = infer.expressionType(expr);
}
if (srv.passes["typeAt"]) {
var pos = resolvePos(file, query.end);
srv.passes["typeAt"].forEach(function(hook) {
type = hook(file, pos, expr, type);
});
}
if (!type) throw ternError("No type found at the given position.");
var objProp;
if (expr.node.type == "ObjectExpression" && query.end != null &&
(objProp = pointInProp(expr.node, resolvePos(file, query.end)))) {
var name = objProp.key.name;
var fromCx = ensureObj(infer.typeFromContext(file.ast, expr));
if (fromCx && fromCx.hasProp(name)) {
type = fromCx.hasProp(name);
} else {
var fromLocal = ensureObj(type);
if (fromLocal && fromLocal.hasProp(name))
type = fromLocal.hasProp(name);
}
}
return type;
};
function findTypeAt(srv, query, file) {
var expr = findExpr(file, query), exprName;
var type = findExprType(srv, query, file, expr), exprType = type;
if (query.preferFunction)
type = type.getFunctionType() || type.getType();
else
type = type.getType();
if (expr) {
if (expr.node.type == "Identifier")
exprName = expr.node.name;
else if (expr.node.type == "MemberExpression" && !expr.node.computed)
exprName = expr.node.property.name;
}
if (query.depth != null && typeof query.depth != "number")
throw ternError(".query.depth must be a number");
var result = {guess: infer.didGuess(),
type: infer.toString(exprType, query.depth),
name: type && type.name,
exprName: exprName};
if (type) storeTypeDocs(type, result);
if (!result.doc && exprType.doc) result.doc = exprType.doc;
return clean(result);
}
function findDocs(srv, query, file) {
var expr = findExpr(file, query);
var type = findExprType(srv, query, file, expr);
var result = {url: type.url, doc: type.doc, type: infer.toString(type)};
var inner = type.getType();
if (inner) storeTypeDocs(inner, result);
return clean(result);
}
function storeTypeDocs(type, out) {
if (!out.url) out.url = type.url;
if (!out.doc) out.doc = type.doc;
if (!out.origin) out.origin = type.origin;
var ctor, boring = infer.cx().protos;
if (!out.url && !out.doc && type.proto && (ctor = type.proto.hasCtor) &&
type.proto != boring.Object && type.proto != boring.Function && type.proto != boring.Array) {
out.url = ctor.url;
out.doc = ctor.doc;
}
}
var getSpan = exports.getSpan = function(obj) {
if (!obj.origin) return;
if (obj.originNode) {
var node = obj.originNode;
if (/^Function/.test(node.type) && node.id) node = node.id;
return {origin: obj.origin, node: node};
}
if (obj.span) return {origin: obj.origin, span: obj.span};
};
var storeSpan = exports.storeSpan = function(srv, query, span, target) {
target.origin = span.origin;
if (span.span) {
var m = /^(\d+)\[(\d+):(\d+)\]-(\d+)\[(\d+):(\d+)\]$/.exec(span.span);
target.start = query.lineCharPositions ? {line: Number(m[2]), ch: Number(m[3])} : Number(m[1]);
target.end = query.lineCharPositions ? {line: Number(m[5]), ch: Number(m[6])} : Number(m[4]);
} else {
var file = srv.findFile(span.origin);
target.start = outputPos(query, file, span.node.start);
target.end = outputPos(query, file, span.node.end);
}
};
function findDef(srv, query, file) {
var expr = findExpr(file, query);
var type = findExprType(srv, query, file, expr);
if (infer.didGuess()) return {};
var span = getSpan(type);
var result = {url: type.url, doc: type.doc, origin: type.origin};
if (type.types) for (var i = type.types.length - 1; i >= 0; --i) {
var tp = type.types[i];
storeTypeDocs(tp, result);
if (!span) span = getSpan(tp);
}
if (span && span.node) { // refers to a loaded file
var spanFile = span.node.sourceFile || srv.findFile(span.origin);
var start = outputPos(query, spanFile, span.node.start), end = outputPos(query, spanFile, span.node.end);
result.start = start; result.end = end;
result.file = span.origin;
var cxStart = Math.max(0, span.node.start - 50);
result.contextOffset = span.node.start - cxStart;
result.context = spanFile.text.slice(cxStart, cxStart + 50);
} else if (span) { // external
result.file = span.origin;
storeSpan(srv, query, span, result);
}
return clean(result);
}
function findRefsToVariable(srv, query, file, expr, checkShadowing) {
var name = expr.node.name;
for (var scope = expr.state; scope && !(name in scope.props); scope = scope.prev) {}
if (!scope) throw ternError("Could not find a definition for " + name + " " + !!srv.cx.topScope.props.x);
var type, refs = [];
function storeRef(file) {
return function(node, scopeHere) {
if (checkShadowing) for (var s = scopeHere; s != scope; s = s.prev) {
var exists = s.hasProp(checkShadowing);
if (exists)
throw ternError("Renaming `" + name + "` to `" + checkShadowing + "` would make a variable at line " +
(asLineChar(file, node.start).line + 1) + " point to the definition at line " +
(asLineChar(file, exists.name.start).line + 1));
}
refs.push({file: file.name,
start: outputPos(query, file, node.start),
end: outputPos(query, file, node.end)});
};
}
if (scope.originNode) {
type = "local";
if (checkShadowing) {
for (var prev = scope.prev; prev; prev = prev.prev)
if (checkShadowing in prev.props) break;
if (prev) infer.findRefs(scope.originNode, scope, checkShadowing, prev, function(node) {
throw ternError("Renaming `" + name + "` to `" + checkShadowing + "` would shadow the definition used at line " +
(asLineChar(file, node.start).line + 1));
});
}
infer.findRefs(scope.originNode, scope, name, scope, storeRef(file));
} else {
type = "global";
for (var i = 0; i < srv.files.length; ++i) {
var cur = srv.files[i];
infer.findRefs(cur.ast, cur.scope, name, scope, storeRef(cur));
}
}
return {refs: refs, type: type, name: name};
}
function findRefsToProperty(srv, query, expr, prop) {
var objType = infer.expressionType(expr).getObjType();
if (!objType) throw ternError("Couldn't determine type of base object.");
var refs = [];
function storeRef(file) {
return function(node) {
refs.push({file: file.name,
start: outputPos(query, file, node.start),
end: outputPos(query, file, node.end)});
};
}
for (var i = 0; i < srv.files.length; ++i) {
var cur = srv.files[i];
infer.findPropRefs(cur.ast, cur.scope, objType, prop.name, storeRef(cur));
}
return {refs: refs, name: prop.name};
}
function findRefs(srv, query, file) {
var expr = findExprOrThrow(file, query, true);
if (expr && expr.node.type == "Identifier") {
return findRefsToVariable(srv, query, file, expr);
} else if (expr && expr.node.type == "MemberExpression" && !expr.node.computed) {
var p = expr.node.property;
expr.node = expr.node.object;
return findRefsToProperty(srv, query, expr, p);
} else if (expr && expr.node.type == "ObjectExpression") {
var pos = resolvePos(file, query.end);
for (var i = 0; i < expr.node.properties.length; ++i) {
var k = expr.node.properties[i].key;
if (k.start <= pos && k.end >= pos)
return findRefsToProperty(srv, query, expr, k);
}
}
throw ternError("Not at a variable or property name.");
}
function buildRename(srv, query, file) {
if (typeof query.newName != "string") throw ternError(".query.newName should be a string");
var expr = findExprOrThrow(file, query);
if (!expr || expr.node.type != "Identifier") throw ternError("Not at a variable.");
var data = findRefsToVariable(srv, query, file, expr, query.newName), refs = data.refs;
delete data.refs;
data.files = srv.files.map(function(f){return f.name;});
var changes = data.changes = [];
for (var i = 0; i < refs.length; ++i) {
var use = refs[i];
use.text = query.newName;
changes.push(use);
}
return data;
}
function listFiles(srv) {
return {files: srv.files.map(function(f){return f.name;})};
}
exports.version = "0.9.1";
});

359
mix/qml/html/cm/walk.js

@ -0,0 +1,359 @@
// AST walker module for Mozilla Parser API compatible trees
(function(mod) {
if (typeof exports == "object" && typeof module == "object") return mod(exports); // CommonJS
if (typeof define == "function" && define.amd) return define(["exports"], mod); // AMD
mod((this.acorn || (this.acorn = {})).walk = {}); // Plain browser env
})(function(exports) {
"use strict";
// A simple walk is one where you simply specify callbacks to be
// called on specific nodes. The last two arguments are optional. A
// simple use would be
//
// walk.simple(myTree, {
// Expression: function(node) { ... }
// });
//
// to do something with all expressions. All Parser API node types
// can be used to identify node types, as well as Expression,
// Statement, and ScopeBody, which denote categories of nodes.
//
// The base argument can be used to pass a custom (recursive)
// walker, and state can be used to give this walked an initial
// state.
exports.simple = function(node, visitors, base, state) {
if (!base) base = exports.base;
function c(node, st, override) {
var type = override || node.type, found = visitors[type];
base[type](node, st, c);
if (found) found(node, st);
}
c(node, state);
};
// An ancestor walk builds up an array of ancestor nodes (including
// the current node) and passes them to the callback as the state parameter.
exports.ancestor = function(node, visitors, base, state) {
if (!base) base = exports.base;
if (!state) state = [];
function c(node, st, override) {
var type = override || node.type, found = visitors[type];
if (node != st[st.length - 1]) {
st = st.slice();
st.push(node);
}
base[type](node, st, c);
if (found) found(node, st);
}
c(node, state);
};
// A recursive walk is one where your functions override the default
// walkers. They can modify and replace the state parameter that's
// threaded through the walk, and can opt how and whether to walk
// their child nodes (by calling their third argument on these
// nodes).
exports.recursive = function(node, state, funcs, base) {
var visitor = funcs ? exports.make(funcs, base) : base;
function c(node, st, override) {
visitor[override || node.type](node, st, c);
}
c(node, state);
};
function makeTest(test) {
if (typeof test == "string")
return function(type) { return type == test; };
else if (!test)
return function() { return true; };
else
return test;
}
function Found(node, state) { this.node = node; this.state = state; }
// Find a node with a given start, end, and type (all are optional,
// null can be used as wildcard). Returns a {node, state} object, or
// undefined when it doesn't find a matching node.
exports.findNodeAt = function(node, start, end, test, base, state) {
test = makeTest(test);
try {
if (!base) base = exports.base;
var c = function(node, st, override) {
var type = override || node.type;
if ((start == null || node.start <= start) &&
(end == null || node.end >= end))
base[type](node, st, c);
if (test(type, node) &&
(start == null || node.start == start) &&
(end == null || node.end == end))
throw new Found(node, st);
};
c(node, state);
} catch (e) {
if (e instanceof Found) return e;
throw e;
}
};
// Find the innermost node of a given type that contains the given
// position. Interface similar to findNodeAt.
exports.findNodeAround = function(node, pos, test, base, state) {
test = makeTest(test);
try {
if (!base) base = exports.base;
var c = function(node, st, override) {
var type = override || node.type;
if (node.start > pos || node.end < pos) return;
base[type](node, st, c);
if (test(type, node)) throw new Found(node, st);
};
c(node, state);
} catch (e) {
if (e instanceof Found) return e;
throw e;
}
};
// Find the outermost matching node after a given position.
exports.findNodeAfter = function(node, pos, test, base, state) {
test = makeTest(test);
try {
if (!base) base = exports.base;
var c = function(node, st, override) {
if (node.end < pos) return;
var type = override || node.type;
if (node.start >= pos && test(type, node)) throw new Found(node, st);
base[type](node, st, c);
};
c(node, state);
} catch (e) {
if (e instanceof Found) return e;
throw e;
}
};
// Find the outermost matching node before a given position.
exports.findNodeBefore = function(node, pos, test, base, state) {
test = makeTest(test);
if (!base) base = exports.base;
var max;
var c = function(node, st, override) {
if (node.start > pos) return;
var type = override || node.type;
if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node))
max = new Found(node, st);
base[type](node, st, c);
};
c(node, state);
return max;
};
// Used to create a custom walker. Will fill in all missing node
// type properties with the defaults.
exports.make = function(funcs, base) {
if (!base) base = exports.base;
var visitor = {};
for (var type in base) visitor[type] = base[type];
for (var type in funcs) visitor[type] = funcs[type];
return visitor;
};
function skipThrough(node, st, c) { c(node, st); }
function ignore(_node, _st, _c) {}
// Node walkers.
var base = exports.base = {};
base.Program = base.BlockStatement = function(node, st, c) {
for (var i = 0; i < node.body.length; ++i)
c(node.body[i], st, "Statement");
};
base.Statement = skipThrough;
base.EmptyStatement = ignore;
base.ExpressionStatement = base.ParenthesizedExpression = function(node, st, c) {
c(node.expression, st, "Expression");
};
base.IfStatement = function(node, st, c) {
c(node.test, st, "Expression");
c(node.consequent, st, "Statement");
if (node.alternate) c(node.alternate, st, "Statement");
};
base.LabeledStatement = function(node, st, c) {
c(node.body, st, "Statement");
};
base.BreakStatement = base.ContinueStatement = ignore;
base.WithStatement = function(node, st, c) {
c(node.object, st, "Expression");
c(node.body, st, "Statement");
};
base.SwitchStatement = function(node, st, c) {
c(node.discriminant, st, "Expression");
for (var i = 0; i < node.cases.length; ++i) {
var cs = node.cases[i];
if (cs.test) c(cs.test, st, "Expression");
for (var j = 0; j < cs.consequent.length; ++j)
c(cs.consequent[j], st, "Statement");
}
};
base.ReturnStatement = base.YieldExpression = function(node, st, c) {
if (node.argument) c(node.argument, st, "Expression");
};
base.ThrowStatement = base.SpreadElement = base.RestElement = function(node, st, c) {
c(node.argument, st, "Expression");
};
base.TryStatement = function(node, st, c) {
c(node.block, st, "Statement");
if (node.handler) c(node.handler.body, st, "ScopeBody");
if (node.finalizer) c(node.finalizer, st, "Statement");
};
base.WhileStatement = function(node, st, c) {
c(node.test, st, "Expression");
c(node.body, st, "Statement");
};
base.DoWhileStatement = base.WhileStatement;
base.ForStatement = function(node, st, c) {
if (node.init) c(node.init, st, "ForInit");
if (node.test) c(node.test, st, "Expression");
if (node.update) c(node.update, st, "Expression");
c(node.body, st, "Statement");
};
base.ForInStatement = base.ForOfStatement = function(node, st, c) {
c(node.left, st, "ForInit");
c(node.right, st, "Expression");
c(node.body, st, "Statement");
};
base.ForInit = function(node, st, c) {
if (node.type == "VariableDeclaration") c(node, st);
else c(node, st, "Expression");
};
base.DebuggerStatement = ignore;
base.FunctionDeclaration = function(node, st, c) {
c(node, st, "Function");
};
base.VariableDeclaration = function(node, st, c) {
for (var i = 0; i < node.declarations.length; ++i) {
var decl = node.declarations[i];
if (decl.init) c(decl.init, st, "Expression");
}
};
base.Function = function(node, st, c) {
c(node.body, st, "ScopeBody");
};
base.ScopeBody = function(node, st, c) {
c(node, st, "Statement");
};
base.Expression = skipThrough;
base.ThisExpression = ignore;
base.ArrayExpression = base.ArrayPattern = function(node, st, c) {
for (var i = 0; i < node.elements.length; ++i) {
var elt = node.elements[i];
if (elt) c(elt, st, "Expression");
}
};
base.ObjectExpression = base.ObjectPattern = function(node, st, c) {
for (var i = 0; i < node.properties.length; ++i)
c(node.properties[i], st);
};
base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration;
base.SequenceExpression = base.TemplateLiteral = function(node, st, c) {
for (var i = 0; i < node.expressions.length; ++i)
c(node.expressions[i], st, "Expression");
};
base.UnaryExpression = base.UpdateExpression = function(node, st, c) {
c(node.argument, st, "Expression");
};
base.BinaryExpression = base.AssignmentExpression = base.AssignmentPattern = base.LogicalExpression = function(node, st, c) {
c(node.left, st, "Expression");
c(node.right, st, "Expression");
};
base.ConditionalExpression = function(node, st, c) {
c(node.test, st, "Expression");
c(node.consequent, st, "Expression");
c(node.alternate, st, "Expression");
};
base.NewExpression = base.CallExpression = function(node, st, c) {
c(node.callee, st, "Expression");
if (node.arguments) for (var i = 0; i < node.arguments.length; ++i)
c(node.arguments[i], st, "Expression");
};
base.MemberExpression = function(node, st, c) {
c(node.object, st, "Expression");
if (node.computed) c(node.property, st, "Expression");
};
base.ExportDeclaration = function (node, st, c) {
c(node.declaration, st);
};
base.ImportDeclaration = function (node, st, c) {
node.specifiers.forEach(function (specifier) {
c(specifier, st);
});
};
base.ImportSpecifier = base.ImportBatchSpecifier = base.Identifier = base.Literal = ignore;
base.TaggedTemplateExpression = function(node, st, c) {
c(node.tag, st, "Expression");
c(node.quasi, st);
};
base.ClassDeclaration = base.ClassExpression = function(node, st, c) {
if (node.superClass) c(node.superClass, st, "Expression");
for (var i = 0; i < node.body.body.length; i++)
c(node.body.body[i], st);
};
base.MethodDefinition = base.Property = function(node, st, c) {
if (node.computed) c(node.key, st, "Expression");
c(node.value, st, "Expression");
};
base.ComprehensionExpression = function(node, st, c) {
for (var i = 0; i < node.blocks.length; i++)
c(node.blocks[i].right, st, "Expression");
c(node.body, st, "Expression");
};
// NOTE: the stuff below is deprecated, and will be removed when 1.0 is released
// A custom walker that keeps track of the scope chain and the
// variables defined in it.
function makeScope(prev, isCatch) {
return {vars: Object.create(null), prev: prev, isCatch: isCatch};
}
function normalScope(scope) {
while (scope.isCatch) scope = scope.prev;
return scope;
}
exports.scopeVisitor = exports.make({
Function: function(node, scope, c) {
var inner = makeScope(scope);
for (var i = 0; i < node.params.length; ++i)
inner.vars[node.params[i].name] = {type: "argument", node: node.params[i]};
if (node.id) {
var decl = node.type == "FunctionDeclaration";
(decl ? normalScope(scope) : inner).vars[node.id.name] =
{type: decl ? "function" : "function name", node: node.id};
}
c(node.body, inner, "ScopeBody");
},
TryStatement: function(node, scope, c) {
c(node.block, scope, "Statement");
if (node.handler) {
var inner = makeScope(scope, true);
inner.vars[node.handler.param.name] = {type: "catch clause", node: node.handler.param};
c(node.handler.body, inner, "ScopeBody");
}
if (node.finalizer) c(node.finalizer, scope, "Statement");
},
VariableDeclaration: function(node, scope, c) {
var target = normalScope(scope);
for (var i = 0; i < node.declarations.length; ++i) {
var decl = node.declarations[i];
target.vars[decl.id.name] = {type: "var", node: decl.id};
if (decl.init) c(decl.init, scope, "Expression");
}
}
});
});

11
mix/qml/html/codeeditor.html

@ -23,6 +23,17 @@
<script src="cm/anyword-hint.js"></script> <script src="cm/anyword-hint.js"></script>
<script src="cm/closebrackets.js"></script> <script src="cm/closebrackets.js"></script>
<script src="cm/errorannotation.js"></script> <script src="cm/errorannotation.js"></script>
<script src="cm/tern.js"></script>
<script src="cm/acorn.js"></script>
<script src="cm/acorn_loose.js"></script>
<script src="cm/walk.js"></script>
<script src="cm/signal.js"></script>
<script src="cm/ternserver.js"></script>
<script src="cm/def.js"></script>
<script src="cm/comment.js"></script>
<script src="cm/infer.js"></script>
<script src="cm/doc_comment.js"></script>
<script src="cm/ecma5spec.js"></script>
<body oncontextmenu="return false;"></body> <body oncontextmenu="return false;"></body>
<script src="codeeditor.js"></script> <script src="codeeditor.js"></script>

20
mix/qml/html/codeeditor.js

@ -4,9 +4,9 @@ var editor = CodeMirror(document.body, {
matchBrackets: true, matchBrackets: true,
autofocus: true, autofocus: true,
gutters: ["CodeMirror-linenumbers", "breakpoints"], gutters: ["CodeMirror-linenumbers", "breakpoints"],
extraKeys: { "Ctrl-Space": "autocomplete" },
autoCloseBrackets: true autoCloseBrackets: true
}); });
var ternServer;
editor.setOption("theme", "solarized dark"); editor.setOption("theme", "solarized dark");
editor.setOption("indentUnit", 4); editor.setOption("indentUnit", 4);
@ -100,15 +100,27 @@ setMode = function(mode) {
if (mode === "javascript") if (mode === "javascript")
{ {
CodeMirror.commands.autocomplete = function(cm) { ternServer = new CodeMirror.TernServer({defs: [ ecma5Spec() ]});
CodeMirror.showHint(cm, CodeMirror.hint.anyword); // TODO change to a proper JavaScript language completion editor.setOption("extraKeys", {
} "Ctrl-Space": function(cm) { ternServer.complete(cm); },
"Ctrl-I": function(cm) { ternServer.showType(cm); },
"Ctrl-O": function(cm) { ternServer.showDocs(cm); },
"Alt-.": function(cm) { ternServer.jumpToDef(cm); },
"Alt-,": function(cm) { ternServer.jumpBack(cm); },
"Ctrl-Q": function(cm) { ternServer.rename(cm); },
"Ctrl-.": function(cm) { ternServer.selectName(cm); },
"'.'": function(cm) { setTimeout(function() { ternServer.complete(cm); }, 100); throw CodeMirror.Pass; }
})
editor.on("cursorActivity", function(cm) { ternServer.updateArgHints(cm); });
} }
else if (mode === "solidity") else if (mode === "solidity")
{ {
CodeMirror.commands.autocomplete = function(cm) { CodeMirror.commands.autocomplete = function(cm) {
CodeMirror.showHint(cm, CodeMirror.hint.anyword); CodeMirror.showHint(cm, CodeMirror.hint.anyword);
} }
editor.setOption("extraKeys", {
"Ctrl-Space": "autocomplete"
})
} }
}; };

149
mix/qml/js/ProjectModel.js

@ -44,18 +44,16 @@ function createProject() {
} }
function closeProject(callBack) { function closeProject(callBack) {
if (!isEmpty) { if (!isEmpty && unsavedFiles.length > 0)
if (unsavedFiles.length > 0) {
{ saveMessageDialog.callBack = callBack;
saveMessageDialog.callBack = callBack; saveMessageDialog.open();
saveMessageDialog.open(); }
} else
else {
{ doCloseProject();
doCloseProject(); if (callBack)
if (callBack) callBack();
callBack();
}
} }
} }
@ -97,46 +95,47 @@ function saveProjectFile()
} }
function loadProject(path) { function loadProject(path) {
closeProject(); closeProject(function() {
console.log("Loading project at " + path); console.log("Loading project at " + path);
var projectFile = path + projectFileName; var projectFile = path + projectFileName;
var json = fileIo.readFile(projectFile); var json = fileIo.readFile(projectFile);
var projectData = JSON.parse(json); var projectData = JSON.parse(json);
if (projectData.deploymentDir) if (projectData.deploymentDir)
projectModel.deploymentDir = projectData.deploymentDir projectModel.deploymentDir = projectData.deploymentDir
if (projectData.packageHash) if (projectData.packageHash)
deploymentDialog.packageHash = projectData.packageHash deploymentDialog.packageHash = projectData.packageHash
if (projectData.packageBase64) if (projectData.packageBase64)
deploymentDialog.packageBase64 = projectData.packageBase64 deploymentDialog.packageBase64 = projectData.packageBase64
if (projectData.applicationUrlEth) if (projectData.applicationUrlEth)
deploymentDialog.applicationUrlEth = projectData.applicationUrlEth deploymentDialog.applicationUrlEth = projectData.applicationUrlEth
if (projectData.applicationUrlHttp) if (projectData.applicationUrlHttp)
deploymentDialog.applicationUrlHttp = projectData.applicationUrlHttp deploymentDialog.applicationUrlHttp = projectData.applicationUrlHttp
if (!projectData.title) { if (!projectData.title) {
var parts = path.split("/"); var parts = path.split("/");
projectData.title = parts[parts.length - 2]; projectData.title = parts[parts.length - 2];
} }
deploymentAddresses = projectData.deploymentAddresses ? projectData.deploymentAddresses : []; deploymentAddresses = projectData.deploymentAddresses ? projectData.deploymentAddresses : [];
projectTitle = projectData.title; projectTitle = projectData.title;
projectPath = path; projectPath = path;
if (!projectData.files) if (!projectData.files)
projectData.files = []; projectData.files = [];
for(var i = 0; i < projectData.files.length; i++) { for(var i = 0; i < projectData.files.length; i++) {
addFile(projectData.files[i]); addFile(projectData.files[i]);
} }
projectSettings.lastProjectPath = path; projectSettings.lastProjectPath = path;
projectLoading(projectData); projectLoading(projectData);
projectLoaded() projectLoaded()
//TODO: move this to codemodel //TODO: move this to codemodel
var contractSources = {}; var contractSources = {};
for (var d = 0; d < listModel.count; d++) { for (var d = 0; d < listModel.count; d++) {
var doc = listModel.get(d); var doc = listModel.get(d);
if (doc.isContract) if (doc.isContract)
contractSources[doc.documentId] = fileIo.readFile(doc.path); contractSources[doc.documentId] = fileIo.readFile(doc.path);
} }
codeModel.reset(contractSources); codeModel.reset(contractSources);
});
} }
function addFile(fileName) { function addFile(fileName) {
@ -163,7 +162,6 @@ function addFile(fileName) {
}; };
projectListModel.append(docData); projectListModel.append(docData);
saveProjectFile();
fileIo.watchFileChanged(p); fileIo.watchFileChanged(p);
return docData.documentId; return docData.documentId;
} }
@ -232,27 +230,28 @@ function doCloseProject() {
} }
function doCreateProject(title, path) { function doCreateProject(title, path) {
closeProject(); closeProject(function() {
console.log("Creating project " + title + " at " + path); console.log("Creating project " + title + " at " + path);
if (path[path.length - 1] !== "/") if (path[path.length - 1] !== "/")
path += "/"; path += "/";
var dirPath = path + title + "/"; var dirPath = path + title + "/";
fileIo.makeDir(dirPath); fileIo.makeDir(dirPath);
var projectFile = dirPath + projectFileName; var projectFile = dirPath + projectFileName;
var indexFile = "index.html"; var indexFile = "index.html";
var contractsFile = "contract.sol"; var contractsFile = "contract.sol";
var projectData = { var projectData = {
title: title, title: title,
files: [ contractsFile, indexFile ] files: [ contractsFile, indexFile ]
}; };
//TODO: copy from template //TODO: copy from template
fileIo.writeFile(dirPath + indexFile, htmlTemplate); fileIo.writeFile(dirPath + indexFile, htmlTemplate);
fileIo.writeFile(dirPath + contractsFile, contractTemplate); fileIo.writeFile(dirPath + contractsFile, contractTemplate);
newProject(projectData); newProject(projectData);
var json = JSON.stringify(projectData, null, "\t"); var json = JSON.stringify(projectData, null, "\t");
fileIo.writeFile(projectFile, json); fileIo.writeFile(projectFile, json);
loadProject(dirPath); loadProject(dirPath);
});
} }
function doAddExistingFiles(files) { function doAddExistingFiles(files) {
@ -263,6 +262,7 @@ function doAddExistingFiles(files) {
if (sourcePath !== destPath) if (sourcePath !== destPath)
fileIo.copyFile(sourcePath, destPath); fileIo.copyFile(sourcePath, destPath);
var id = addFile(sourceFileName); var id = addFile(sourceFileName);
saveProjectFile();
documentAdded(id) documentAdded(id)
} }
} }
@ -321,6 +321,7 @@ function createAndAddFile(name, extension, content) {
var filePath = projectPath + fileName; var filePath = projectPath + fileName;
fileIo.writeFile(filePath, content); fileIo.writeFile(filePath, content);
var id = addFile(fileName); var id = addFile(fileName);
saveProjectFile();
documentAdded(id); documentAdded(id);
} }

37
mix/qml/main.qml

@ -7,9 +7,15 @@ import QtQuick.Window 2.1
import QtQuick.PrivateWidgets 1.1 import QtQuick.PrivateWidgets 1.1
import Qt.labs.settings 1.0 import Qt.labs.settings 1.0
import org.ethereum.qml.QEther 1.0 import org.ethereum.qml.QEther 1.0
import org.ethereum.qml.CodeModel 1.0
import org.ethereum.qml.ClientModel 1.0
import org.ethereum.qml.FileIo 1.0
import org.ethereum.qml.Clipboard 1.0
ApplicationWindow { ApplicationWindow {
id: mainApplication id: mainApplication
signal loaded;
visible: true visible: true
width: 1200 width: 1200
height: 800 height: 800
@ -17,8 +23,28 @@ ApplicationWindow {
minimumHeight: 300 minimumHeight: 300
title: qsTr("Mix") title: qsTr("Mix")
Connections CodeModel {
{ id: codeModel
}
ClientModel {
id: clientModel
codeModel: codeModel
}
ProjectModel {
id: projectModel
}
FileIo {
id: fileIo
}
Clipboard {
id: clipboard
}
Connections {
target: mainApplication target: mainApplication
onClosing: onClosing:
{ {
@ -27,8 +53,11 @@ ApplicationWindow {
} }
} }
function close() Component.onCompleted: {
{ loaded();
}
function close() {
projectModel.appIsClosing = true; projectModel.appIsClosing = true;
if (projectModel.projectPath !== "") if (projectModel.projectPath !== "")
projectModel.closeProject(function() { Qt.quit(); }) projectModel.closeProject(function() { Qt.quit(); })

124
mix/res.qrc

@ -1,65 +1,5 @@
<RCC> <RCC>
<qresource prefix="/"> <qresource prefix="/">
<file>qml/main.qml</file>
<file>qml/AlertMessageDialog.qml</file>
<file>qml/BasicMessage.qml</file>
<file>qml/Debugger.qml</file>
<file>qml/MainContent.qml</file>
<file>qml/ModalDialog.qml</file>
<file>qml/ProjectList.qml</file>
<file>qml/StateDialog.qml</file>
<file>qml/StateList.qml</file>
<file>qml/StateListModel.qml</file>
<file>qml/img/dappProjectIcon.png</file>
<file>qml/img/jumpintoback.png</file>
<file>qml/img/jumpintoforward.png</file>
<file>qml/img/jumpoutback.png</file>
<file>qml/img/jumpoutforward.png</file>
<file>qml/img/jumpoverback.png</file>
<file>qml/img/jumpoverforward.png</file>
<file>qml/img/jumpoverforwarddisabled.png</file>
<file>qml/StepActionImage.qml</file>
<file>qml/img/jumpintobackdisabled.png</file>
<file>qml/img/jumpintoforwarddisabled.png</file>
<file>qml/img/jumpoutbackdisabled.png</file>
<file>qml/img/jumpoutforwarddisabled.png</file>
<file>qml/img/jumpoverbackdisabled.png</file>
<file>qml/DebugBasicInfo.qml</file>
<file>qml/js/ErrorLocationFormater.js</file>
<file>qml/img/closedtriangleindicator.png</file>
<file>qml/img/opentriangleindicator.png</file>
<file>qml/img/bugiconactive.png</file>
<file>qml/img/bugiconinactive.png</file>
<file>qml/DebugInfoList.qml</file>
<file>qml/ItemDelegateDataDump.qml</file>
<file>qml/StatusPane.qml</file>
<file>qml/TabStyle.qml</file>
<file>qml/TransactionDialog.qml</file>
<file>qml/js/Debugger.js</file>
<file>qml/NewProjectDialog.qml</file>
<file>qml/ProjectModel.qml</file>
<file>qml/CodeEditorView.qml</file>
<file>qml/js/ProjectModel.js</file>
<file>qml/Ether.qml</file>
<file>qml/EtherValue.qml</file>
<file>qml/BigIntValue.qml</file>
<file>qml/QVariableDefinition.qml</file>
<file>qml/js/QEtherHelper.js</file>
<file>qml/js/TransactionHelper.js</file>
<file>qml/QBoolTypeView.qml</file>
<file>qml/QIntTypeView.qml</file>
<file>qml/QRealTypeView.qml</file>
<file>qml/QStringTypeView.qml</file>
<file>qml/QHashTypeView.qml</file>
<file>qml/ContractLibrary.qml</file>
<file>stdc/config.sol</file>
<file>stdc/namereg.sol</file>
<file>stdc/std.sol</file>
<file>qml/TransactionLog.qml</file>
<file>res/mix_256x256x32.png</file>
<file>qml/QVariableDeclaration.qml</file>
<file>qml/qmldir</file>
<file>qml/FilesSection.qml</file>
<file>qml/fonts/SourceSansPro-Black.ttf</file> <file>qml/fonts/SourceSansPro-Black.ttf</file>
<file>qml/fonts/SourceSansPro-BlackIt.ttf</file> <file>qml/fonts/SourceSansPro-BlackIt.ttf</file>
<file>qml/fonts/SourceSansPro-Bold.ttf</file> <file>qml/fonts/SourceSansPro-Bold.ttf</file>
@ -75,43 +15,43 @@
<file>qml/fonts/SourceSerifPro-Bold.ttf</file> <file>qml/fonts/SourceSerifPro-Bold.ttf</file>
<file>qml/fonts/SourceSerifPro-Regular.ttf</file> <file>qml/fonts/SourceSerifPro-Regular.ttf</file>
<file>qml/fonts/SourceSerifPro-Semibold.ttf</file> <file>qml/fonts/SourceSerifPro-Semibold.ttf</file>
<file>qml/img/available_updates.png</file>
<file>qml/img/b64.png</file>
<file>qml/img/broom.png</file>
<file>qml/img/bugiconactive.png</file>
<file>qml/img/bugiconinactive.png</file>
<file>qml/img/closedtriangleindicator.png</file>
<file>qml/img/closedtriangleindicator_filesproject.png</file> <file>qml/img/closedtriangleindicator_filesproject.png</file>
<file>qml/img/opentriangleindicator_filesproject.png</file> <file>qml/img/console.png</file>
<file>qml/img/projecticon.png</file> <file>qml/img/copy.png</file>
<file>qml/SourceSansProRegular.qml</file> <file>qml/img/dappProjectIcon.png</file>
<file>qml/SourceSansProBold.qml</file>
<file>qml/SourceSansProLight.qml</file>
<file>qml/StateDialogStyle.qml</file>
<file>qml/ProjectFilesStyle.qml</file>
<file>qml/DebuggerPaneStyle.qml</file>
<file>qml/CodeEditorStyle.qml</file>
<file>qml/StatusPaneStyle.qml</file>
<file>qml/StateStyle.qml</file>
<file>qml/img/plus.png</file>
<file>qml/img/delete_sign.png</file> <file>qml/img/delete_sign.png</file>
<file>qml/img/edit.png</file> <file>qml/img/edit.png</file>
<file>qml/DefaultLabel.qml</file> <file>qml/img/exit.png</file>
<file>qml/DefaultTextField.qml</file>
<file>qml/CommonSeparator.qml</file>
<file>qml/Style.qml</file>
<file>qml/WebPreviewStyle.qml</file>
<file>qml/img/available_updates.png</file>
<file>qml/DeploymentDialog.qml</file>
<file>qml/img/search_filled.png</file>
<file>qml/StorageView.qml</file>
<file>qml/CallStack.qml</file>
<file>qml/img/help.png</file> <file>qml/img/help.png</file>
<file>qml/img/jumpintoback.png</file>
<file>qml/img/jumpintobackdisabled.png</file>
<file>qml/img/jumpintoforward.png</file>
<file>qml/img/jumpintoforwarddisabled.png</file>
<file>qml/img/jumpoutback.png</file>
<file>qml/img/jumpoutbackdisabled.png</file>
<file>qml/img/jumpoutforward.png</file>
<file>qml/img/jumpoutforwarddisabled.png</file>
<file>qml/img/jumpoverback.png</file>
<file>qml/img/jumpoverbackdisabled.png</file>
<file>qml/img/jumpoverforward.png</file>
<file>qml/img/jumpoverforwarddisabled.png</file>
<file>qml/img/note.png</file>
<file>qml/img/openedfolder.png</file> <file>qml/img/openedfolder.png</file>
<file>qml/img/b64.png</file> <file>qml/img/opentriangleindicator.png</file>
<file>qml/img/exit.png</file> <file>qml/img/opentriangleindicator_filesproject.png</file>
<file>qml/img/plus.png</file>
<file>qml/img/projecticon.png</file>
<file>qml/img/run.png</file> <file>qml/img/run.png</file>
<file>qml/img/note.png</file> <file>qml/img/search_filled.png</file>
<file>qml/LogsPane.qml</file> <file>res/mix_256x256x32.png</file>
<file>qml/img/copy.png</file> <file>stdc/config.sol</file>
<file>qml/img/broom.png</file> <file>stdc/namereg.sol</file>
<file>qml/LogsPaneStyle.qml</file> <file>stdc/std.sol</file>
<file>qml/StructView.qml</file>
<file>qml/img/console.png</file>
<file>qml/VariablesView.qml</file>
</qresource> </qresource>
</RCC> </RCC>

11
mix/web.qrc

@ -28,5 +28,16 @@
<file>qml/html/cm/solidityToken.js</file> <file>qml/html/cm/solidityToken.js</file>
<file>qml/html/cm/javascript-hint.js</file> <file>qml/html/cm/javascript-hint.js</file>
<file>qml/html/cm/errorannotation.js</file> <file>qml/html/cm/errorannotation.js</file>
<file>qml/html/cm/tern.js</file>
<file>qml/html/cm/ecma5spec.js</file>
<file>qml/html/cm/comment.js</file>
<file>qml/html/cm/def.js</file>
<file>qml/html/cm/doc_comment.js</file>
<file>qml/html/cm/infer.js</file>
<file>qml/html/cm/signal.js</file>
<file>qml/html/cm/ternserver.js</file>
<file>qml/html/cm/acorn.js</file>
<file>qml/html/cm/acorn_loose.js</file>
<file>qml/html/cm/walk.js</file>
</qresource> </qresource>
</RCC> </RCC>

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

Loading…
Cancel
Save