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 RLP: " << RLP(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;
ui->configDock->close();
@ -162,7 +162,7 @@ Main::Main(QWidget *parent) :
QSettings s("ethereum", "alethzero");
m_networkConfig = s.value("peers").toByteArray();
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_server.reset(new OurWebThreeStubServer(*m_httpConnector, *web3(), keysAsVector(m_myKeys), this));
@ -312,7 +312,7 @@ void Main::installBalancesWatch()
Address coinsAddr = getCurrencies();
// 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)));
for (auto i: m_myKeys)
for (auto c: altCoins)
@ -583,7 +583,7 @@ Address Main::fromString(QString const& _n) const
{
try
{
return Address(fromHex(_n.toStdString(), ThrowType::Throw));
return Address(fromHex(_n.toStdString(), WhenError::Throw));
}
catch (BadHexCharacter& _e)
{
@ -616,10 +616,10 @@ QString Main::lookup(QString const& _a) const
h256 ret;
// 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);
/* 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);
*/
if (ret && !((u256)ret >> 32))
@ -937,7 +937,7 @@ void Main::refreshBalances()
u256 totalBalance = 0;
/* map<Address, tuple<QString, u256, u256>> altCoins;
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 addr = right160(ethereum()->stateAt(coinsAddr, n));
@ -1059,7 +1059,7 @@ void Main::refreshBlockCount()
{
cwatch << "refreshBlockCount()";
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()
@ -1357,7 +1357,7 @@ void Main::on_transactionQueue_currentItemChanged()
auto r = receipt.rlp();
s << "<div>Receipt: " << toString(RLP(r)) << "</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 << "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");
try
{
bytes b = fromHex(s.toStdString(), ThrowType::Throw);
bytes b = fromHex(s.toStdString(), WhenError::Throw);
ethereum()->inject(&b);
}
catch (BadHexCharacter& _e)
@ -1550,7 +1550,7 @@ void Main::on_debugCurrent_triggered()
unsigned txi = item->data(Qt::UserRole + 1).toInt();
bytes t = ethereum()->blockChain().transaction(h, txi);
State s(ethereum()->state(txi, h));
Executive e(s, ethereum()->blockChain(), 0);
Executive e(s, ethereum()->blockChain(), PendingBlock);
Debugger dw(this, this);
dw.populate(e, Transaction(t, CheckSignature::Sender));
dw.exec();

397
alethzero/Transact.cpp

@ -25,6 +25,7 @@
#include "Transact.h"
#include <fstream>
#include <boost/algorithm/string.hpp>
#include <QFileDialog>
#include <QMessageBox>
#include <liblll/Compiler.h>
@ -135,7 +136,39 @@ void Transact::updateFee()
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 = "";
auto const& contract = _compiler.getContractDefinition(_contractName);
@ -150,186 +183,256 @@ string Transact::getFunctionHashes(dev::solidity::CompilerStack const& _compiler
return ret;
}
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();
}
void Transact::rejigData()
static tuple<vector<string>, bytes, string> userInputToCode(string const& _user, bool _opt)
{
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();
vector<string> errors;
QString lll;
QString solidity;
if (src.find_first_not_of("1234567890abcdefABCDEF") == string::npos && src.size() % 2 == 0)
m_data = fromHex(src);
else if (sourceIsSolidity(src))
std::string u = _user;
boost::replace_all_copy(u, "\n", "");
boost::replace_all_copy(u, "\t", "");
boost::replace_all_copy(u, " ", "");
data = fromHex(u);
}
else if (sourceIsSolidity(_user))
{
dev::solidity::CompilerStack compiler(true);
try
{
dev::solidity::CompilerStack compiler(true);
try
{
// compiler.addSources(dev::solidity::StandardSources);
m_data = compiler.compile(src, ui->optimize->isChecked());
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>" + QString::fromStdString(compiler.getSolidityInterface()).toHtmlEscaped() + "</pre>";
solidity += "<pre>" + QString::fromStdString(getFunctionHashes(compiler)).toHtmlEscaped() + "</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>";
}
data = compiler.compile(_user, _opt);
solidity = "<h4>Solidity</h4>";
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().toStdString() + "</pre>";
solidity += "<pre>" + QString::fromStdString(getFunctionHashes(compiler, "")).toHtmlEscaped().toStdString() + "</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
else if (sourceIsSerpent(src))
else if (sourceIsSerpent(_user))
{
try
{
try
{
m_data = dev::asBytes(::compile(src));
for (auto& i: errors)
i = "(LLL " + i + ")";
}
catch (string const& err)
{
errors.push_back("Serpent " + err);
}
data = dev::asBytes(::compile(_user));
}
catch (string const& err)
{
errors.push_back("Serpent " + err);
}
}
#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
{
m_data = compileLLL(src, ui->optimize->isChecked(), &errors);
if (errors.empty())
{
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;
}
}
NatspecExpressionEvaluator evaluator;
return evaluator.evalExpression(QString::fromStdString(userNotice)).toStdString();
}
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())
{
errs = "<h4>Errors</h4>";
// Errors determining transaction data (i.e. init code). Bail.
QString htmlErrors;
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>");
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));
htmlInfo = QString::fromStdString(info) + "<h4>Code</h4>" + QString::fromStdString(disassemble(m_data)).toHtmlEscaped();
}
else
{
m_data = parseData(ui->data->toPlainText().toStdString());
auto to = m_context->fromString(ui->destination->currentText());
QString natspec;
if (ethereum()->codeAt(to, 0).size())
{
string userNotice = m_natSpecDB->getUserNotice(ethereum()->postState().codeHash(to), m_data);
if (userNotice.empty())
natspec = "Destination contract unknown.";
else
{
NatspecExpressionEvaluator evaluator;
natspec = evaluator.evalExpression(QString::fromStdString(userNotice));
}
ui->gas->setMinimum((qint64)Interface::txGas(m_data, 1));
if (!ui->gas->isEnabled())
ui->gas->setValue(m_backupGas);
ui->gas->setEnabled(true);
}
else
{
natspec += "Destination not a contract.";
if (ui->gas->isEnabled())
m_backupGas = ui->gas->value();
ui->gas->setValue((qint64)Interface::txGas(m_data));
ui->gas->setEnabled(false);
}
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>");
htmlInfo = "<h4>Dump</h4>" + QString::fromStdString(dev::memDump(m_data, 8, true));
}
htmlInfo += "<h4>Hex</h4>" + QString(Div(Mono)) + QString::fromStdString(toHex(m_data)) + "</div>";
// Determine the minimum amount of gas we need to play...
qint64 baseGas = (qint64)Interface::txGas(m_data, 0);
qint64 gasNeeded = 0;
if (b < value() + baseGas * gasPrice())
{
// Not enough - bail.
bail("<div class=\"error\"><span class=\"icon\">ERROR</span> No single account contains enough for paying even the basic amount of gas required.</div>");
return;
}
else
gasNeeded = min<qint64>((qint64)ethereum()->gasLimitRemaining(), (qint64)((b - value()) / gasPrice()));
// Dry-run execution to determine gas requirement and any execution errors
Address to;
ExecutionResult er;
if (isCreation())
er = ethereum()->create(s, value(), m_data, gasNeeded, gasPrice());
else
{
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();
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)
if (ethereum()->balanceAt(i.address(), 0) >= totalReq)
{
Secret s = i.secret();
if (isCreation())
{
auto b = ethereum()->balanceAt(i.address(), PendingBlock);
if (b >= _totalReq)
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
// a local Natspec LEVELDB
ethereum()->submitTransaction(s, value(), m_data, ui->gas->value(), gasPrice());
string src = ui->data->toPlainText().toStdString();
if (sourceIsSolidity(src))
try
{
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;
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));
}
}
else
ethereum()->submitTransaction(s, value(), m_context->fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice());
return;
}
QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount.");
catch (...) {}
}
else
ethereum()->submitTransaction(s, value(), m_context->fromString(ui->destination->currentText()), m_data, ui->gas->value(), gasPrice());
close();
}
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
{
u256 totalReq = value() + fee();
for (auto i: m_myKeys)
if (ethereum()->balanceAt(i.address()) >= totalReq)
{
State st(ethereum()->postState());
Secret s = i.secret();
Transaction t = isCreation() ?
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);
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.");
State st(ethereum()->postState());
Transaction t = isCreation() ?
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);
Debugger dw(m_context, this);
Executive e(st, ethereum()->blockChain(), 0);
dw.populate(e, t);
dw.exec();
}
catch (dev::Exception const& _e)
{

16
alethzero/Transact.h

@ -57,7 +57,7 @@ private slots:
void on_cancel_clicked() { close(); }
private:
dev::eth::Client* ethereum() { return m_ethereum; }
dev::eth::Client* ethereum() const { return m_ethereum; }
void rejigData();
void updateDestination();
@ -68,15 +68,17 @@ private:
dev::u256 value() 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;
QList<dev::KeyPair> m_myKeys;
dev::eth::Client* m_ethereum;
Context* m_context;
NatSpecFace* m_natSpecDB;
dev::eth::Client* m_ethereum = nullptr;
Context* m_context = nullptr;
NatSpecFace* m_natSpecDB = nullptr;
bool m_allGood = false;
};

48
eth/main.cpp

@ -103,7 +103,7 @@ void interactiveHelp()
void help()
{
cout
<< "Usage eth [OPTIONS] <remote-host>" << endl
<< "Usage eth [OPTIONS]" << endl
<< "Options:" << endl
<< " -a,--address <addr> Set the coinbase (mining payout) address to addr (default: auto)." << 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
<< " --json-rpc-port Specify JSON-RPC server port (implies '-j', default: 8080)." << endl
#endif
<< " -K,--kill-blockchain First kill the blockchain." << 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
<< " -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()
{
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 << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl;
exit(0);
@ -214,6 +215,7 @@ int main(int argc, char** argv)
bool upnp = true;
bool useLocal = false;
bool forceMining = false;
bool killChain = false;
bool jit = false;
bool structuredLogging = false;
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")
useLocal = true;
else if (arg == "-K" || arg == "--kill-blockchain")
killChain = true;
else if ((arg == "-c" || arg == "--client-name") && i + 1 < argc)
clientName = argv[++i];
else if ((arg == "-a" || arg == "--address" || arg == "--coinbase-address") && i + 1 < argc)
{
try
{
coinbase = h160(fromHex(argv[++i], ThrowType::Throw));
coinbase = h160(fromHex(argv[++i], WhenError::Throw));
}
catch (BadHexCharacter& _e)
{
@ -289,7 +292,6 @@ int main(int argc, char** argv)
cwarn << "coinbase rejected";
break;
}
}
else if ((arg == "-s" || arg == "--secret") && i + 1 < argc)
us = KeyPair(h256(fromHex(argv[++i])));
else if (arg == "--structured-logging-format" && i + 1 < argc)
@ -300,20 +302,24 @@ int main(int argc, char** argv)
dbPath = argv[++i];
else if ((arg == "-B" || arg == "--block-fees") && i + 1 < argc)
{
try {
try
{
blockFees = stof(argv[++i]);
}
catch (...) {
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[++i] << endl;
return -1;
}
}
else if ((arg == "-e" || arg == "--ether-price") && i + 1 < argc)
{
try {
try
{
etherPrice = stof(argv[++i]);
}
catch (...) {
catch (...)
{
cerr << "Bad " << arg << " option: " << argv[++i] << endl;
return -1;
}
@ -401,7 +407,10 @@ int main(int argc, char** argv)
else if (arg == "-V" || arg == "--version")
version();
else
remoteHost = argv[i];
{
cerr << "Invalid argument: " << arg << endl;
exit(-1);
}
}
if (!clientName.empty())
@ -417,7 +426,7 @@ int main(int argc, char** argv)
dev::WebThreeDirect web3(
clientImplString,
dbPath,
false,
killChain,
mode == NodeMode::Full ? set<string>{"eth", "shh"} : set<string>(),
netPrefs,
&nodesState,
@ -434,7 +443,8 @@ int main(int argc, char** argv)
c->setAddress(coinbase);
}
cout << "Address: " << endl << toHex(us.address().asArray()) << endl;
cout << "Transaction Signer: " << us.address() << endl;
cout << "Mining Benefactor: " << coinbase << endl;
web3.startNetwork();
if (bootstrap)
@ -680,7 +690,7 @@ int main(int argc, char** argv)
auto acs =c->addresses();
string ss;
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)) + "]";
cout << ss << endl;
@ -691,7 +701,7 @@ int main(int argc, char** argv)
auto acs =c->addresses();
string ss;
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)) + "]";
cout << ss << endl;
@ -720,7 +730,7 @@ int main(int argc, char** argv)
u256 minGas = (u256)Client::txGas(bytes(), 0);
try
{
Address dest = h160(fromHex(hexAddr, ThrowType::Throw));
Address dest = h160(fromHex(hexAddr, WhenError::Throw));
c->submitTransaction(us.secret(), amount, dest, bytes(), minGas);
}
catch (BadHexCharacter& _e)
@ -764,7 +774,7 @@ int main(int argc, char** argv)
stringstream ssc;
try
{
init = fromHex(sinit, ThrowType::Throw);
init = fromHex(sinit, WhenError::Throw);
}
catch (BadHexCharacter& _e)
{
@ -883,10 +893,10 @@ int main(int argc, char** argv)
try
{
auto storage =c->storageAt(h, 0);
auto storage =c->storageAt(h, PendingBlock);
for (auto const& i: storage)
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";
ofstream ofs;
@ -925,7 +935,7 @@ int main(int argc, char** argv)
{
try
{
coinbase = h160(fromHex(hexAddr, ThrowType::Throw));
coinbase = h160(fromHex(hexAddr, WhenError::Throw));
}
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)
{
if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_arg1))
{
if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2))
{
if (!c2->getValue())
return std::make_pair(Constant::get(0), Constant::get(0));
auto div = Constant::get(c1->getValue().udiv(c2->getValue()));
auto mod = Constant::get(c1->getValue().urem(c2->getValue()));
return std::make_pair(div, mod);
}
}
// 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 (!c2->getValue())
// return std::make_pair(Constant::get(0), Constant::get(0));
// auto div = Constant::get(c1->getValue().udiv(c2->getValue()));
// auto mod = Constant::get(c1->getValue().urem(c2->getValue()));
// return std::make_pair(div, mod);
// }
// }
auto r = createCall(getDivFunc(Type::Word), {_arg1, _arg2});
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)
{
if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_x))
{
if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_y))
{
if (!c2->getValue())
return std::make_pair(Constant::get(0), Constant::get(0));
auto div = Constant::get(c1->getValue().sdiv(c2->getValue()));
auto mod = Constant::get(c1->getValue().srem(c2->getValue()));
return std::make_pair(div, mod);
}
}
// 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 (!c2->getValue())
// return std::make_pair(Constant::get(0), Constant::get(0));
// auto div = Constant::get(c1->getValue().sdiv(c2->getValue()));
// 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 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));
}
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;
std::vector<uint8_t> ret;
@ -96,7 +96,7 @@ bytes dev::fromHex(std::string const& _s, ThrowType _throw)
#ifndef BOOST_NO_EXCEPTIONS
cwarn << boost::current_exception_diagnostic_information();
#endif
if (_throw == ThrowType::Throw)
if (_throw == WhenError::Throw)
throw;
}
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
cwarn << boost::current_exception_diagnostic_information();
#endif
if (_throw == ThrowType::Throw)
if (_throw == WhenError::Throw)
throw;
}
return ret;

8
libdevcore/CommonData.h

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

9
libdevcrypto/FileSystem.cpp

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

2
libdevcrypto/FileSystem.h

@ -30,6 +30,6 @@ namespace dev
{
/// @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 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());
}
try {
boost::filesystem::create_directories(getDataDir() + "/ethashcache");
boost::filesystem::create_directories(getDataDir("ethash"));
} catch (...) {}
std::string memoFile = getDataDir() + "/ethashcache/full";
std::string memoFile = getDataDir("ethash") + "/full";
auto info = rlpList(c_ethashRevision, _header.seedHash());
if (boost::filesystem::exists(memoFile) && contents(memoFile + ".info") != info)
boost::filesystem::remove(memoFile);

1
libethcore/Exceptions.h

@ -59,7 +59,6 @@ struct InvalidGasUsed: virtual dev::Exception {};
class InvalidTransactionsHash: virtual public dev::Exception {};
struct InvalidTransaction: virtual dev::Exception {};
struct InvalidDifficulty: virtual dev::Exception {};
struct InvalidSeedHash: virtual dev::Exception {};
class InvalidGasLimit: virtual public dev::Exception {};
struct InvalidTransactionGasUsed: 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_difficultyBoundDivisor = 2048;
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_expGas = 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_difficultyBoundDivisor;
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_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 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()
@ -55,7 +55,7 @@ void VersionChecker::setOk()
{
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));
}
}
@ -357,11 +357,11 @@ LocalisedLogEntries Client::checkWatch(unsigned _watchId)
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);
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.
auto m = i.second.filter.matches(_receipt);
@ -369,7 +369,7 @@ void Client::appendFromNewPending(TransactionReceipt const& _receipt, h256Set& i
{
// filter catches them
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);
}
}
@ -383,7 +383,7 @@ void Client::appendFromNewBlock(h256 const& _block, h256Set& io_changed)
Guard l(m_filterLock);
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.
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);
if (m.size())
{
auto sha3 = transaction(d.hash, j).sha3();
auto transactionHash = transaction(d.hash, j).sha3();
// filter catches them
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);
}
}
@ -491,7 +491,7 @@ void Client::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes
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;
try
@ -514,7 +514,7 @@ ExecutionResult Client::call(Secret _secret, u256 _value, Address _dest, bytes c
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;
try
@ -739,12 +739,12 @@ unsigned Client::numberOf(int _n) const
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);
if (_h == 0)
if (_h == PendingBlock)
return m_postMine;
else if (_h == -1)
else if (_h == LatestBlock)
return m_preMine;
else
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);
}
StateDiff Client::diff(unsigned _txi, int _block) const
StateDiff Client::diff(unsigned _txi, BlockNumber _block) const
{
State st = asOf(_block);
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));
}
std::vector<Address> Client::addresses(int _block) const
std::vector<Address> Client::addresses(BlockNumber _block) const
{
vector<Address> ret;
for (auto const& i: asOf(_block).addresses())
@ -788,27 +788,27 @@ std::vector<Address> Client::addresses(int _block) const
return ret;
}
u256 Client::balanceAt(Address _a, int _block) const
u256 Client::balanceAt(Address _a, BlockNumber _block) const
{
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);
}
u256 Client::countAt(Address _a, int _block) const
u256 Client::countAt(Address _a, BlockNumber _block) const
{
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);
}
bytes Client::codeAt(Address _a, int _block) const
bytes Client::codeAt(Address _a, BlockNumber _block) const
{
return asOf(_block).code(_a);
}

21
libethereum/Client.h

@ -225,11 +225,11 @@ public:
virtual void flushTransactions() override;
/// 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.
/// @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.
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::storageAt;
virtual u256 balanceAt(Address _a, int _block) const;
virtual u256 countAt(Address _a, int _block) const;
virtual u256 stateAt(Address _a, u256 _l, int _block) const;
virtual bytes codeAt(Address _a, int _block) const;
virtual std::map<u256, u256> storageAt(Address _a, int _block) const;
virtual u256 balanceAt(Address _a, BlockNumber _block) const;
virtual u256 countAt(Address _a, BlockNumber _block) const;
virtual u256 stateAt(Address _a, u256 _l, BlockNumber _block) const;
virtual bytes codeAt(Address _a, BlockNumber _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(h256 _filterId, Reaping _r = Reaping::Automatic) override;
@ -282,11 +282,11 @@ public:
/// Differences between transactions.
using Interface::diff;
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.
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.
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.
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;
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()
{
clogS(NetMessageSummary) << "Aborting Sync :-(";
abortSync();
}

22
libethereum/Interface.h

@ -73,16 +73,16 @@ public:
virtual void flushTransactions() = 0;
/// 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.
/// @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]
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 countAt(Address _a) const { return countAt(_a, m_default); }
@ -90,11 +90,11 @@ public:
bytes codeAt(Address _a) const { return codeAt(_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 countAt(Address _a, int _block) const = 0;
virtual u256 stateAt(Address _a, u256 _l, int _block) const = 0;
virtual bytes codeAt(Address _a, int _block) const = 0;
virtual std::map<u256, u256> storageAt(Address _a, int _block) const = 0;
virtual u256 balanceAt(Address _a, BlockNumber _block) const = 0;
virtual u256 countAt(Address _a, BlockNumber _block) const = 0;
virtual u256 stateAt(Address _a, u256 _l, BlockNumber _block) const = 0;
virtual bytes codeAt(Address _a, BlockNumber _block) const = 0;
virtual std::map<u256, u256> storageAt(Address _a, BlockNumber _block) const = 0;
// [LOGS API]
@ -135,11 +135,11 @@ public:
/// Differences between transactions.
StateDiff diff(unsigned _txi) const { return diff(_txi, m_default); }
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.
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.
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;
protected:
int m_default = -1;
int m_default = PendingBlock;
};
class Watch;

27
libethereum/LogFilter.cpp

@ -46,6 +46,33 @@ h256 LogFilter::sha3() const
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
{
if (m_addresses.size())

11
libethereum/LogFilter.h

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

2
libethereum/State.cpp

@ -1040,7 +1040,7 @@ LastHashes State::getLastHashes(BlockChain const& _bc, unsigned _n) const
{
LastHashes ret;
ret.resize(256);
if (c_protocolVersion > 49)
if (eth::c_protocolVersion > 49)
{
ret[0] = _bc.numberHash(_n);
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;
if (!!dynamic_cast<OutOfGas const*>(&_e))
return TransactionException::OutOfGas;
if (!!dynamic_cast<OutOfStack const*>(&_e))
return TransactionException::OutOfStack;
if (!!dynamic_cast<StackUnderflow const*>(&_e))
return TransactionException::StackUnderflow;
return TransactionException::Unknown;

3
libethereum/Transaction.h

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

4
libevm/ExtVMFace.h

@ -63,10 +63,10 @@ using LogEntries = std::vector<LogEntry>;
struct LocalisedLogEntry: public LogEntry
{
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;
h256 sha3;
h256 transactionHash;
};
using LocalisedLogEntries = std::vector<LocalisedLogEntry>;

6
libevm/VM.cpp

@ -36,6 +36,7 @@ struct InstructionMetric
{
int gasPriceTier;
int args;
int ret;
};
static array<InstructionMetric, 256> metrics()
@ -46,12 +47,15 @@ static array<InstructionMetric, 256> metrics()
InstructionInfo inst = instructionInfo((Instruction)i);
s_ret[i].gasPriceTier = inst.gasPriceTier;
s_ret[i].args = inst.args;
s_ret[i].ret = inst.ret;
}
return s_ret;
}
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();
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.
//m_onFail = std::function<void()>(onOperation);
require(metric.args);
require(metric.args, metric.ret);
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;
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); } }
u256 curPC() const { return m_curPC; }

1
libevm/VMFace.h

@ -31,6 +31,7 @@ struct BreakPointHit: virtual VMException {};
struct BadInstruction: virtual VMException {};
struct BadJumpDestination: virtual VMException {};
struct OutOfGas: virtual VMException {};
struct OutOfStack: virtual VMException {};
struct StackUnderflow: virtual VMException {};
/// 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);
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
@ -2005,16 +2005,6 @@ var getOptions = function (options) {
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
if(options.topics instanceof Array) {
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
return {
fromBlock: utils.toHex(options.fromBlock),
toBlock: utils.toHex(options.toBlock),
limit: utils.toHex(options.limit),
offset: utils.toHex(options.offset),
fromBlock: asBlockNumber(options.fromBlock),
toBlock: asBlockNumber(options.toBlock),
to: options.to,
address: options.address,
topics: options.topics
@ -2053,9 +2048,9 @@ var filter = function(options, implementation, formatter) {
// call the callbacks
var onMessages = function (messages) {
messages.forEach(function (message) {
message = formatter ? formatter(message) : message;
message = formatter ? formatter(message) : message;
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"])
//# 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);
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

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

@ -64,16 +64,6 @@ var getOptions = function (options) {
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
if(options.topics instanceof Array) {
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
return {
fromBlock: utils.toHex(options.fromBlock),
toBlock: utils.toHex(options.toBlock),
limit: utils.toHex(options.limit),
offset: utils.toHex(options.offset),
fromBlock: asBlockNumber(options.fromBlock),
toBlock: asBlockNumber(options.toBlock),
to: options.to,
address: options.address,
topics: options.topics
@ -112,9 +107,9 @@ var filter = function(options, implementation, formatter) {
// call the callbacks
var onMessages = function (messages) {
messages.forEach(function (message) {
message = formatter ? formatter(message) : message;
message = formatter ? formatter(message) : message;
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::p2p;
const unsigned dev::p2p::c_protocolVersion = 3;
// Helper function to determine if an address falls within one of the reserved ranges
// For V4:
// Class A "10.*", Class B "172.[16->31].*", Class C "192.168.*"

3
libp2p/Common.h

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

32
libp2p/Host.cpp

@ -42,6 +42,12 @@ using namespace std;
using namespace dev;
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) {}
void HostNodeTableHandler::processEvent(NodeId const& _n, NodeTableEventType const& _e)
@ -170,11 +176,6 @@ void Host::doneWorking()
m_sessions.clear();
}
unsigned Host::protocolVersion() const
{
return 3;
}
void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint)
{
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
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);
return;
@ -260,7 +261,7 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e)
p->required = n.required;
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;
}
@ -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() << ")";
_p->m_lastDisconnect = TCPError;
_p->m_lastAttempted = std::chrono::system_clock::now();
_p->m_failedAttempts++;
}
else
{
@ -609,8 +611,7 @@ void Host::startedWorking()
else
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, m_listenPort > 0 ? m_listenPort : 30303));
m_nodeTable.reset(new NodeTable(m_ioService, m_alias, bi::address::from_string(listenAddress()), listenPort() > 0 ? listenPort() : 30303));
m_nodeTable->setEventHandler(new HostNodeTableHandler(*this));
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: if/how to save private addresses
// 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);
if (p.peerEndpoint().address().is_v4())
@ -708,7 +709,7 @@ bytes Host::saveNetwork() const
}
RLPStream ret(3);
ret << 1 << m_alias.secret();
ret << dev::p2p::c_protocolVersion << m_alias.secret();
ret.appendList(count).appendRaw(network.out(), count);
return ret.out();
}
@ -721,7 +722,7 @@ void Host::restoreNetwork(bytesConstRef _b)
RecursiveGuard l(x_sessions);
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[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>());
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];
if (i.itemCount() == 3)
m_nodeTable->addNode(id, udp, tcp);

9
libp2p/Host.h

@ -94,18 +94,9 @@ public:
/// Will block on network process events.
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.
static std::string pocHost();
/// Basic peer network protocol version.
unsigned protocolVersion() const;
/// 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; }

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, 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):
m_node(Node(_alias.pub(), bi::udp::endpoint())),
NodeTable::NodeTable(ba::io_service& _io, KeyPair _alias, bi::address const& _udpAddress, uint16_t _udp):
m_node(Node(_alias.pub(), bi::udp::endpoint(_udpAddress, _udp))),
m_secret(_alias.sec()),
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_bucketRefreshTimer(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)
{
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
if (!_node.id)
{
@ -326,7 +340,7 @@ void NodeTable::noteActiveNode(Public const& _pubk, bi::udp::endpoint const& _en
s.nodes.push_back(node);
s.touch();
if (!removed)
if (!removed && m_nodeEventHandler)
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.touch();
if (!removed)
if (!removed && m_nodeEventHandler)
m_nodeEventHandler->appendEvent(node->id, NodeEntryAdded);
}
}
@ -463,6 +477,13 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
case PingNode::type:
{
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));
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.
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();
/// 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;
unsigned version = 1;
unsigned version = dev::p2p::c_protocolVersion;
std::string ipAddress;
unsigned port;
unsigned expiration;

14
libp2p/RLPxHandshake.cpp

@ -139,7 +139,7 @@ void RLPXHandshake::error()
void RLPXHandshake::transition(boost::system::error_code _ech)
{
if (_ech || m_nextState == Error)
if (_ech || m_nextState == Error || m_cancel)
return error();
auto self(shared_from_this());
@ -172,7 +172,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
// 5 arguments, HelloPacket
RLPStream s;
s.append((unsigned)0).appendList(5)
<< m_host->protocolVersion()
<< dev::p2p::c_protocolVersion
<< m_host->m_clientVersion
<< m_host->caps()
<< 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:
/// 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.
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() {}
/// Start handshake.
void start() { transition(); }
void cancel() { m_nextState = Error; }
/// Cancels handshake preventing
void cancel() { m_cancel = true; }
protected:
/// Write Auth message to socket and transitions to AckAuth.
@ -93,7 +94,11 @@ protected:
/// Performs transition for m_nextState.
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.
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().
@ -119,7 +124,8 @@ protected:
/// Passed onto Host which will take ownership.
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()
{
clogS(NetMessageSummary) << "Closing Peer Session :-(";
m_peer->m_lastConnected = m_peer->m_lastAttempted - chrono::seconds(1);
// Read-chain finished for one reason or another.

4
libp2p/UDP.h

@ -113,6 +113,10 @@ public:
enum { maxDatagramSize = MaxDatagramSize };
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); };
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
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
// 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)
m_context << eth::swapInstruction(i);
// stack: target_ref source_ref [source_byte_off] [source_length]
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]
// retrieve source length
if (_sourceType.getLocation() != ArrayType::Location::CallData || !_sourceType.isDynamicallySized())
@ -81,7 +87,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
m_context
<< eth::Instruction::POP << eth::Instruction::POP
<< eth::Instruction::POP << eth::Instruction::POP;
m_context << u256(0); //@todo
m_context << u256(0);
return;
}
// 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
// skip copying if source length is zero
m_context << eth::Instruction::DUP3 << eth::Instruction::ISZERO;
eth::AssemblyItem copyLoopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(copyLoopEnd);
eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag();
m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset);
if (_sourceType.getLocation() == ArrayType::Location::Storage && _sourceType.isDynamicallySized())
CompilerUtils(m_context).computeHashStatic();
@ -107,18 +113,24 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
convertLengthToSize(_sourceType);
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
// 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();
m_context << copyLoopStart;
// check for loop condition
m_context
<< eth::Instruction::DUP3 << eth::Instruction::DUP2
<< eth::dupInstruction(3 + byteOffsetSize) << eth::dupInstruction(2 + byteOffsetSize)
<< eth::Instruction::GT << eth::Instruction::ISZERO;
eth::AssemblyItem copyLoopEnd = m_context.newTag();
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
if (sourceBaseType->getCategory() == Type::Category::Array)
{
//@todo
solAssert(byteOffsetSize == 0, "Byte offset for array as base type.");
m_context << eth::Instruction::DUP3;
if (sourceIsStorage)
m_context << u256(0);
@ -129,36 +141,80 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
);
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
{
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)
{
m_context << u256(0);
if (haveByteOffsetSource)
m_context << eth::Instruction::DUP2;
else
m_context << u256(0);
StorageItem(m_context, *sourceBaseType).retrieveValue(SourceLocation(), true);
}
else if (sourceBaseType->isValueType())
CompilerUtils(m_context).loadFromMemoryDynamic(*sourceBaseType, true, true, false);
else
solAssert(false, "Copying of unknown type requested: " + sourceBaseType->toString());
solAssert(2 + sourceBaseType->getSizeOnStack() <= 16, "Stack too deep.");
m_context << eth::dupInstruction(2 + sourceBaseType->getSizeOnStack()) << u256(0);
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] <source_value>...
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);
}
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
// increment source
m_context
<< eth::Instruction::SWAP2
<< (sourceIsStorage ? sourceBaseType->getStorageSize() : sourceBaseType->getCalldataEncodedSize())
<< eth::Instruction::ADD
<< eth::Instruction::SWAP2;
if (haveByteOffsetSource)
incrementByteOffset(sourceBaseType->getStorageBytes(), 1, haveByteOffsetTarget ? 5 : 4);
else
m_context
<< eth::swapInstruction(2 + byteOffsetSize)
<< (sourceIsStorage ? sourceBaseType->getStorageSize() : sourceBaseType->getCalldataEncodedSize())
<< eth::Instruction::ADD
<< eth::swapInstruction(2 + byteOffsetSize);
// increment target
m_context
<< eth::Instruction::SWAP1
<< targetBaseType->getStorageSize()
<< eth::Instruction::ADD
<< eth::Instruction::SWAP1;
if (haveByteOffsetTarget)
incrementByteOffset(targetBaseType->getStorageBytes(), byteOffsetSize, byteOffsetSize + 2);
else
m_context
<< eth::swapInstruction(1 + byteOffsetSize)
<< targetBaseType->getStorageSize()
<< eth::Instruction::ADD
<< eth::swapInstruction(1 + byteOffsetSize);
m_context.appendJumpTo(copyLoopStart);
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
// 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
clearStorageLoop(*targetBaseType);
m_context << eth::Instruction::POP;
m_context << u256(0); //@todo
m_context << u256(0);
}
void ArrayUtils::clearArray(ArrayType const& _type) const
{
unsigned stackHeightStart = m_context.getStackHeight();
solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
if (_type.isDynamicallySized())
if (_type.getBaseType()->getStorageBytes() < 32)
{
m_context << eth::Instruction::POP; // remove byte offset
clearDynamicArray(_type);
solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value 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)
m_context << eth::Instruction::POP << eth::Instruction::POP;
else if (_type.getLength() < 5) // unroll loop for small arrays @todo choose a good value
m_context << eth::Instruction::POP;
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)
{
m_context << u256(0);
StorageItem(m_context, *_type.getBaseType()).setToZero(SourceLocation(), false);
m_context << eth::Instruction::SWAP1;
m_context << u256(_type.getBaseType()->getStorageSize()) << eth::Instruction::ADD;
m_context << eth::Instruction::SWAP1;
m_context
<< eth::Instruction::POP
<< u256(_type.getBaseType()->getStorageSize()) << eth::Instruction::ADD;
}
m_context << u256(0);
StorageItem(m_context, *_type.getBaseType()).setToZero(SourceLocation(), true);
}
else
{
solAssert(!_type.isByteArray(), "");
m_context << eth::Instruction::SWAP1;
m_context << eth::Instruction::DUP1 << _type.getLength();
convertLengthToSize(_type);
m_context << eth::Instruction::ADD << eth::Instruction::SWAP1;
clearStorageLoop(*_type.getBaseType());
m_context << eth::Instruction::POP << eth::Instruction::POP;
if (_type.getBaseType()->getStorageBytes() < 32)
clearStorageLoop(IntegerType(256));
else
clearStorageLoop(*_type.getBaseType());
m_context << eth::Instruction::POP;
}
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
<< eth::Instruction::SWAP1;
// stack: data_pos_end data_pos
if (_type.isByteArray())
if (_type.isByteArray() || _type.getBaseType()->getStorageBytes() < 32)
clearStorageLoop(IntegerType(256));
else
clearStorageLoop(*_type.getBaseType());
@ -237,6 +313,8 @@ void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const
{
solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
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();
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
m_context << eth::Instruction::SWAP2 << eth::Instruction::ADD;
// stack: ref new_length delete_end delete_start
if (_type.isByteArray())
if (_type.isByteArray() || _type.getBaseType()->getStorageBytes() < 32)
clearStorageLoop(IntegerType(256));
else
clearStorageLoop(*_type.getBaseType());
@ -294,7 +372,7 @@ void ArrayUtils::clearStorageLoop(Type const& _type) const
eth::AssemblyItem zeroLoopEnd = m_context.newTag();
m_context.appendConditionalJumpTo(zeroLoopEnd);
// delete
m_context << u256(0); //@todo
m_context << u256(0);
StorageItem(m_context, _type).setToZero(SourceLocation(), false);
m_context << eth::Instruction::POP;
// increment
@ -313,7 +391,20 @@ void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) con
if (_arrayType.isByteArray())
m_context << u256(31) << eth::Instruction::ADD
<< 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;
}
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;
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;
};

17
libsolidity/Compiler.cpp

@ -20,12 +20,12 @@
* Solidity compiler.
*/
#include <libsolidity/Compiler.h>
#include <algorithm>
#include <boost/range/adaptor/reversed.hpp>
#include <libevmcore/Instruction.h>
#include <libevmcore/Assembly.h>
#include <libsolidity/AST.h>
#include <libsolidity/Compiler.h>
#include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerUtils.h>
@ -274,19 +274,8 @@ void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
void Compiler::registerStateVariables(ContractDefinition const& _contract)
{
vector<VariableDeclaration const*> variables;
for (ContractDefinition const* contract: boost::adaptors::reverse(_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);
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);
for (auto const& var: ContractType(_contract).getStateVariables())
m_context.addStateVariable(*get<0>(var), get<1>(var), get<2>(var));
}
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)
{
// stack layout: <base_ref> [storage_byte_offset] [<length>] <index>
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(baseType);
solAssert(_indexAccess.getIndexExpression(), "Index expression expected.");
ArrayType::Location location = arrayType.getLocation();
@ -762,6 +761,11 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
location == ArrayType::Location::Memory ? eth::Instruction::MLOAD :
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);
// retrieve length
if (!arrayType.isDynamicallySized())
@ -769,11 +773,9 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
else if (location == ArrayType::Location::CallData)
// length is stored on the stack
m_context << eth::Instruction::SWAP1;
else if (location == ArrayType::Location::Storage)
m_context << eth::Instruction::DUP3 << load;
else
m_context << eth::Instruction::DUP2 << load;
// stack: <base_ref> [storage_byte_offset] <index> <length>
// stack: <base_ref> <index> <length>
// check out-of-bounds access
m_context << eth::Instruction::DUP2 << eth::Instruction::LT;
eth::AssemblyItem legalAccess = m_context.appendConditionalJump();
@ -781,23 +783,22 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
m_context << eth::Instruction::STOP;
m_context << legalAccess;
// stack: <base_ref> [storage_byte_offset] <index>
// stack: <base_ref> <index>
if (arrayType.isByteArray())
// byte array is packed differently, especially in storage
switch (location)
{
case ArrayType::Location::Storage:
// byte array index storage lvalue on stack (goal):
// <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();
// stack: 32 storage_byte_offset index data_ref
// stack: 32 index data_ref
m_context
<< eth::Instruction::DUP4 << eth::Instruction::DUP3
<< eth::Instruction::DUP3 << eth::Instruction::DUP3
<< eth::Instruction::DIV << eth::Instruction::ADD
// stack: 32 storage_byte_offset index (data_ref + index / 32)
<< eth::Instruction::SWAP3 << eth::Instruction::SWAP2
<< eth::Instruction::POP << eth::Instruction::MOD;
// stack: 32 index (data_ref + index / 32)
<< eth::Instruction::SWAP2 << eth::Instruction::SWAP1
<< eth::Instruction::MOD;
setLValue<StorageByteArrayElement>(_indexAccess);
break;
case ArrayType::Location::CallData:
@ -811,36 +812,51 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
}
else
{
// stack: <base_ref> [storage_byte_offset] <index>
if (location == ArrayType::Location::Storage)
//@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;
// stack: <base_ref> <index>
m_context << eth::Instruction::SWAP1;
if (arrayType.isDynamicallySized())
{
if (location == ArrayType::Location::Storage)
{
m_context << eth::Instruction::SWAP1;
CompilerUtils(m_context).computeHashStatic();
}
else if (location == ArrayType::Location::Memory)
m_context << u256(32) << eth::Instruction::ADD;
}
m_context << eth::Instruction::ADD;
// stack: <index> <data_ref>
switch (location)
{
case ArrayType::Location::CallData:
m_context
<< eth::Instruction::SWAP1 << arrayType.getBaseType()->getCalldataEncodedSize()
<< eth::Instruction::MUL << eth::Instruction::ADD;
if (arrayType.getBaseType()->isValueType())
CompilerUtils(m_context).loadFromMemoryDynamic(*arrayType.getBaseType(), true, true, false);
break;
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);
break;
case ArrayType::Location::Memory:

46
libsolidity/Types.cpp

@ -20,14 +20,14 @@
* Solidity data types
*/
#include <libsolidity/Types.h>
#include <limits>
#include <boost/range/adaptor/reversed.hpp>
#include <libdevcore/CommonIO.h>
#include <libdevcore/CommonData.h>
#include <libsolidity/Utils.h>
#include <libsolidity/Types.h>
#include <libsolidity/AST.h>
#include <limits>
using namespace std;
namespace dev
@ -184,6 +184,8 @@ TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length
TypePointer baseType = _baseTypeName.toType();
if (!baseType)
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->getType())
@ -700,13 +702,21 @@ u256 ArrayType::getStorageSize() const
{
if (isDynamicallySized())
return 1;
else
bigint size;
unsigned baseBytes = getBaseType()->getStorageBytes();
if (baseBytes == 0)
size = 1;
else if (baseBytes < 32)
{
bigint 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 itemsPerSlot = 32 / baseBytes;
size = (bigint(getLength()) + (itemsPerSlot - 1)) / itemsPerSlot;
}
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
@ -806,6 +816,26 @@ u256 ContractType::getFunctionIdentifier(string const& _functionName) const
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
{
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();

19
libsolidity/Types.h

@ -335,13 +335,22 @@ public:
/// Constructor for a byte array ("bytes")
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[]")
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]")
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 TypePointer unaryOperatorResult(Token::Value _operator) const override;
@ -403,6 +412,10 @@ public:
/// not exist.
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:
ContractDefinition const& m_contract;
/// 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)
{
Json::Value res;
res["data"] = toJS(_e.data);
res["address"] = toJS(_e.address);
res["topics"] = Json::Value(Json::arrayValue);
for (auto const& t: _e.topics)
res["topics"].append(toJS(t));
res["number"] = _e.number;
res["hash"] = toJS(_e.sha3);
if (_e.transactionHash)
{
res["data"] = toJS(_e.data);
res["address"] = toJS(_e.address);
res["topics"] = Json::Value(Json::arrayValue);
for (auto const& t: _e.topics)
res["topics"].append(toJS(t));
res["number"] = _e.number;
res["hash"] = toJS(_e.transactionHash);
}
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);
for (dev::eth::LocalisedLogEntry const& e: _es)
@ -139,6 +141,18 @@ static Json::Value toJson(map<u256, u256> const& _storage)
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.
{
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
if (!_json["fromBlock"].empty())
filter.withEarliest(jsToInt(_json["fromBlock"].asString()));
filter.withEarliest(toBlockNumber(_json["fromBlock"].asString()));
if (!_json["toBlock"].empty())
filter.withLatest(jsToInt(_json["toBlock"].asString()));
filter.withLatest(toBlockNumber(_json["toBlock"].asString()));
if (!_json["address"].empty())
{
if (_json["address"].isArray())
@ -227,15 +241,6 @@ static Json::Value toJson(h256 const& _h, shh::Envelope const& _e, shh::Message
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):
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;
if (!t.gas)
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;
}

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 ..)
find_package (Qt5WebEngine QUIET)
qt5_add_resources(UI_RESOURCES res.qrc)
qt5_add_resources(UI_RESOURCES res.qrc qml.qrc)
file(GLOB HEADERS "*.h")

26
mix/ClientModel.cpp

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

10
mix/ClientModel.h

@ -34,13 +34,13 @@ namespace dev
namespace mix
{
class AppContext;
class Web3Server;
class RpcConnector;
class QEther;
class QDebugData;
class MixClient;
class QVariableDefinition;
class CodeModel;
struct SolidityType;
/// Backend transaction config class
@ -127,7 +127,7 @@ class ClientModel: public QObject
Q_OBJECT
public:
ClientModel(AppContext* _context);
ClientModel();
~ClientModel();
/// @returns true if currently executing contract code
Q_PROPERTY(bool running MEMBER m_running NOTIFY runStateChanged)
@ -143,6 +143,8 @@ public:
Q_INVOKABLE QString apiCall(QString const& _message);
/// Simulate mining. Creates a new block
Q_INVOKABLE void mine();
/// Get/set code model. Should be set from qml
Q_PROPERTY(CodeModel* codeModel MEMBER m_codeModel)
public slots:
/// Setup state, run transaction sequence, show debugger for the last transaction
@ -157,8 +159,6 @@ public slots:
private slots:
/// Update UI with machine states result. Display a modal dialog.
void showDebugger();
/// Update UI with transaction run error.
void showDebugError(QString const& _error);
signals:
/// Transaction execution started
@ -201,7 +201,6 @@ private:
void showDebuggerForTransaction(ExecutionResult const& _t);
QVariant formatValue(SolidityType const& _type, dev::u256 const& _value);
AppContext* m_context;
std::atomic<bool> m_running;
std::atomic<bool> m_mining;
std::unique_ptr<MixClient> m_client;
@ -211,6 +210,7 @@ private:
std::map<Address, QString> m_contractNames;
std::map<QString, Address> m_stdContractAddresses;
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.
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 ConstantCompilationControl.cpp
/** @file Clipboard.cpp
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
* @date 2015
*/
#include <QQmlContext>
#include <QQuickItem>
#include <QtCore/QFileInfo>
#include "Clipboard.h"
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtCore/QtCore>
#include <QDebug>
#include "StatusPane.h"
#include "QContractDefinition.h"
#include "AppContext.h"
#include "CodeModel.h"
#include <QClipboard>
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.
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 ConstantCompilationControl.h
/** @file Clipboard.h
* @author Yann yann@ethdev.com
* @date 2014
* Ethereum IDE client.
* @date 2015
*/
#pragma once
#include "Extension.h"
#include <QObject>
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_PROPERTY(QString text READ text WRITE setText NOTIFY clipboardChanged)
public:
StatusPane(AppContext* _appContext);
~StatusPane() {}
void start() const override;
QString title() const override;
QString contentUrl() const override;
Clipboard();
/// Copy text to clipboard
void setText(QString _text);
/// Get text from clipboard
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));
}
CodeModel::CodeModel(QObject* _parent):
QObject(_parent),
CodeModel::CodeModel():
m_compiling(false),
m_codeHighlighterSettings(new CodeHighlighterSettings()),
m_backgroundWorker(this),
@ -181,7 +180,10 @@ void CodeModel::registerCodeChange(QString const& _documentId, QString const& _c
{
CompiledContract* contract = contractByDocumentId(_documentId);
if (contract != nullptr && contract->m_sourceHash == qHash(_code))
{
emit compilationComplete();
return;
}
{
Guard pl(x_pendingContracts);
@ -259,11 +261,16 @@ void CodeModel::runCompilationJob(int _jobId)
CompiledContract* contract = new CompiledContract(cs, name, source);
QQmlEngine::setObjectOwnership(contract, QQmlEngine::CppOwnership);
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())
emit contractInterfaceChanged(name);
if (prevContract == nullptr)
emit newContractCompiled(name);
else if (prevContract->contract()->name() != name)
emit contractRenamed(contract->documentId(), prevContract->contract()->name(), name);
}
releaseContracts();
m_contractMap.swap(result);

4
mix/CodeModel.h

@ -125,7 +125,7 @@ class CodeModel: public QObject
Q_OBJECT
public:
CodeModel(QObject* _parent);
CodeModel();
~CodeModel();
Q_PROPERTY(QVariantMap contracts READ contracts NOTIFY codeChanged)
@ -165,6 +165,8 @@ signals:
void contractInterfaceChanged(QString _documentId);
/// Emitted if there is a new contract compiled for the first time
void newContractCompiled(QString _documentId);
/// Emitted if a contract name has been changed
void contractRenamed(QString _documentId, QString _oldName, QString _newName);
public slots:
/// 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
*/
#include <QDebug>
#include "MixApplication.h"
#include <QQmlApplicationEngine>
#include <QUrl>
#include <QIcon>
#ifdef ETH_HAVE_WEBENGINE
#include <QtWebEngine/QtWebEngine>
#endif
#include "MixApplication.h"
#include "AppContext.h"
#include <QMenuBar>
#include "CodeModel.h"
#include "ClientModel.h"
#include "FileIo.h"
#include "QEther.h"
#include "QVariableDeclaration.h"
#include "SortFilterProxyModel.h"
#include "Clipboard.h"
#include "HttpServer.h"
using namespace dev::mix;
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"));
setOrganizationDomain(tr("ethereum.org"));
@ -43,8 +47,26 @@ MixApplication::MixApplication(int& _argc, char* _argv[]):
#ifdef ETH_HAVE_WEBENGINE
QtWebEngine::initialize();
#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()

4
mix/MixApplication.h

@ -33,8 +33,6 @@ namespace dev
namespace mix
{
class AppContext;
class MixApplication: public QApplication
{
Q_OBJECT
@ -42,12 +40,10 @@ class MixApplication: public QApplication
public:
MixApplication(int& _argc, char* _argv[]);
virtual ~MixApplication();
AppContext* context() { return m_appContext.get(); }
QQmlApplicationEngine* engine() { return m_engine.get(); }
private:
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;
State temp;
@ -299,7 +299,7 @@ dev::eth::ExecutionResult MixClient::call(Secret _secret, u256 _value, Address _
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;
State temp;
@ -315,27 +315,27 @@ dev::eth::ExecutionResult MixClient::create(Secret _secret, u256 _value, bytes c
return lastExecution().result;
}
u256 MixClient::balanceAt(Address _a, int _block) const
u256 MixClient::balanceAt(Address _a, BlockNumber _block) const
{
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);
}
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);
}
bytes MixClient::codeAt(Address _a, int _block) const
bytes MixClient::codeAt(Address _a, BlockNumber _block) const
{
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);
}
@ -567,13 +567,13 @@ eth::StateDiff MixClient::diff(unsigned _txi, h256 _block) const
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);
return st.fromPending(_txi).diff(st.fromPending(_txi + 1));
}
Addresses MixClient::addresses(int _block) const
Addresses MixClient::addresses(BlockNumber _block) const
{
Addresses ret;
for (auto const& i: asOf(_block).addresses())

19
mix/MixClient.h

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

3
mix/QContractDefinition.cpp

@ -21,14 +21,13 @@
#include <QObject>
#include "QContractDefinition.h"
#include <libsolidity/CompilerStack.h>
#include <libsolidity/AST.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/NameAndTypeResolver.h>
#include "AppContext.h"
#include "QContractDefinition.h"
using namespace dev::solidity;
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 editor = editors.itemAt(i).item;
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;
Connections {
target: appContext
Component.onCompleted: {
target: mainApplication
onLoaded: {
//TODO: load a list, dependencies, ets, from external files
contractListModel.append({

2
mix/qml/DebugInfoList.qml

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

10
mix/qml/Debugger.qml

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

2
mix/qml/DeploymentDialog.qml

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

4
mix/qml/LogsPane.qml

@ -67,7 +67,7 @@ Rectangle
var log = logsModel.get(k);
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);
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 {

18
mix/qml/MainContent.qml

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

19
mix/qml/ProjectList.qml

@ -111,6 +111,25 @@ Item {
Connections {
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: {
if (modelData === "Contracts") {
var ci = 0;

6
mix/qml/ProjectModel.qml

@ -51,6 +51,7 @@ Item {
function createProject() { ProjectModelCode.createProject(); }
function closeProject(callBack) { ProjectModelCode.closeProject(callBack); }
function saveProject() { ProjectModelCode.saveProject(); }
function saveProjectFile() { ProjectModelCode.saveProjectFile(); }
function loadProject(path) { ProjectModelCode.loadProject(path); }
function newHtmlFile() { ProjectModelCode.newHtmlFile(); }
function newJsFile() { ProjectModelCode.newJsFile(); }
@ -69,8 +70,8 @@ Item {
function formatAppUrl() { ProjectModelCode.formatAppUrl(url); }
Connections {
target: appContext
onAppLoaded: {
target: mainApplication
onLoaded: {
if (projectSettings.lastProjectPath && projectSettings.lastProjectPath !== "")
projectModel.loadProject(projectSettings.lastProjectPath)
}
@ -173,7 +174,6 @@ Item {
{
target: projectModel
onProjectClosed: {
projectSettings.lastProjectPath = "";
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: {
stateListModel.addNewContracts();
}
onContractRenamed: {
stateListModel.renameContracts(_oldName, _newName);
}
}
StateDialog {
@ -206,11 +209,35 @@ 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() {
//add new contracts for all states
var changed = false;
for(var c in codeModel.contracts) {
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++) {
var transaction = state.transactions[t];
if (transaction.functionId === c && transaction.contractId === c)
@ -223,13 +250,14 @@ Item {
ctorTr.contractId = c;
ctorTr.sender = state.accounts[0].secret;
state.transactions.push(ctorTr);
var item = state;//fromPlainStateItem(state);
stateListModel.set(s, item);
stateList[s] = item;
changed = true;
stateListModel.set(s, state);
stateList[s] = state;
}
}
}
save();
if (changed)
save();
}
function addState() {

6
mix/qml/TransactionLog.qml

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

4
mix/qml/WebCodeEditor.qml

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

13
mix/qml/WebPreview.qml

@ -58,16 +58,11 @@ Item {
function changePage() {
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 {
target: appContext
onAppLoaded: {
target: mainApplication
onLoaded: {
//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");
webView.loadHtml(containerPage, httpServer.url + "/WebContainer.html")
@ -292,7 +287,7 @@ Item {
color: WebPreviewStyle.general.separatorColor
}
SplitView
Splitter
{
Layout.preferredWidth: parent.width
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";
var HINT_ELEMENT_CLASS = "CodeMirror-hint";
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) {
// We want a single cursor position.
if (cm.somethingSelected()) return;
if (getHints == null) {
if (options && options.async) return;
else getHints = CodeMirror.hint.auto;
if (!getHints) return cm.showHint(options);
if (options && options.async) getHints.async = true;
var newOpts = {hint: getHints};
if (options) for (var prop in options) newOpts[prop] = options[prop];
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 || {});
CodeMirror.signal(cm, "startCompletion", cm);
if (completion.options.async)
getHints(cm, function(hints) { completion.showHints(hints); }, completion.options);
else
return completion.showHints(getHints(cm, completion.options));
};
if (this.state.completionActive) this.state.completionActive.close();
var completion = this.state.completionActive = new Completion(this, options);
var getHints = completion.options.hint;
if (!getHints) return;
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.getHints = getHints;
this.options = options;
this.options = this.buildOptions(options);
this.widget = this.onClose = null;
}
@ -46,7 +72,8 @@
pick: function(data, i) {
var completion = data.list[i];
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);
this.close();
},
@ -54,7 +81,7 @@
showHints: function(data) {
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);
else
this.showWidget(data);
@ -65,7 +92,7 @@
CodeMirror.signal(data, "shown");
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 requestAnimationFrame = window.requestAnimationFrame || function(fn) {
@ -84,10 +111,7 @@
function update() {
if (finished) return;
CodeMirror.signal(data, "update");
if (completion.options.async)
completion.getHints(completion.cm, finishUpdate, completion.options);
else
finishUpdate(completion.getHints(completion.cm, completion.options));
retrieveHints(completion.options.hint, completion.cm, completion.options, finishUpdate);
}
function finishUpdate(data_) {
data = data_;
@ -118,6 +142,17 @@
}
this.cm.on("cursorActivity", activity);
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;
}
function buildKeyMap(options, handle) {
function buildKeyMap(completion, handle) {
var baseMap = {
Up: function() {handle.moveFocus(-1);},
Down: function() {handle.moveFocus(1);},
@ -138,7 +173,8 @@
Tab: handle.pick,
Esc: handle.close
};
var ourMap = options.customKeys ? {} : baseMap;
var custom = completion.options.customKeys;
var ourMap = custom ? {} : baseMap;
function addBinding(key, val) {
var bound;
if (typeof val != "string")
@ -150,12 +186,13 @@
bound = val;
ourMap[key] = bound;
}
if (options.customKeys)
for (var key in options.customKeys) if (options.customKeys.hasOwnProperty(key))
addBinding(key, options.customKeys[key]);
if (options.extraKeys)
for (var key in options.extraKeys) if (options.extraKeys.hasOwnProperty(key))
addBinding(key, options.extraKeys[key]);
if (custom)
for (var key in custom) if (custom.hasOwnProperty(key))
addBinding(key, custom[key]);
var extra = completion.options.extraKeys;
if (extra)
for (var key in extra) if (extra.hasOwnProperty(key))
addBinding(key, extra[key]);
return ourMap;
}
@ -169,11 +206,11 @@
function Widget(completion, data) {
this.completion = completion;
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");
hints.className = "CodeMirror-hints";
this.selectedHint = options.getDefaultSelection ? options.getDefaultSelection(cm,options,data) : 0;
this.selectedHint = data.selectedHint || 0;
var completions = data.list;
for (var i = 0; i < completions.length; ++i) {
@ -186,19 +223,19 @@
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;
hints.style.left = left + "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.
var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
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;
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
hints.style.top = (top = curTop - height) + "px";
hints.style.top = (top = pos.top - height) + "px";
below = false;
} else if (height > winH) {
hints.style.height = (winH - 5) + "px";
@ -211,7 +248,7 @@
}
}
}
var overlapX = box.left - winW;
var overlapX = box.right - winW;
if (overlapX > 0) {
if (box.right - box.left > winW) {
hints.style.width = (winW - 5) + "px";
@ -220,7 +257,7 @@
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); },
setFocus: function(n) { widget.changeActive(n); },
menuSize: function() { return widget.screenAmount(); },
@ -230,7 +267,7 @@
data: data
}));
if (options.closeOnUnfocus !== false) {
if (completion.options.closeOnUnfocus) {
var closingOnBlur;
cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
@ -256,7 +293,7 @@
var t = getHintElement(hints, e.target || e.srcElement);
if (t && t.hintId != null) {
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);
var cm = this.completion.cm;
if (this.completion.options.closeOnUnfocus !== false) {
if (this.completion.options.closeOnUnfocus) {
cm.off("blur", this.onBlur);
cm.off("focus", this.onFocus);
}
@ -340,4 +377,18 @@
});
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/closebrackets.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>
<script src="codeeditor.js"></script>

20
mix/qml/html/codeeditor.js

@ -4,9 +4,9 @@ var editor = CodeMirror(document.body, {
matchBrackets: true,
autofocus: true,
gutters: ["CodeMirror-linenumbers", "breakpoints"],
extraKeys: { "Ctrl-Space": "autocomplete" },
autoCloseBrackets: true
});
var ternServer;
editor.setOption("theme", "solarized dark");
editor.setOption("indentUnit", 4);
@ -100,15 +100,27 @@ setMode = function(mode) {
if (mode === "javascript")
{
CodeMirror.commands.autocomplete = function(cm) {
CodeMirror.showHint(cm, CodeMirror.hint.anyword); // TODO change to a proper JavaScript language completion
}
ternServer = new CodeMirror.TernServer({defs: [ ecma5Spec() ]});
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")
{
CodeMirror.commands.autocomplete = function(cm) {
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) {
if (!isEmpty) {
if (unsavedFiles.length > 0)
{
saveMessageDialog.callBack = callBack;
saveMessageDialog.open();
}
else
{
doCloseProject();
if (callBack)
callBack();
}
if (!isEmpty && unsavedFiles.length > 0)
{
saveMessageDialog.callBack = callBack;
saveMessageDialog.open();
}
else
{
doCloseProject();
if (callBack)
callBack();
}
}
@ -97,46 +95,47 @@ function saveProjectFile()
}
function loadProject(path) {
closeProject();
console.log("Loading project at " + path);
var projectFile = path + projectFileName;
var json = fileIo.readFile(projectFile);
var projectData = JSON.parse(json);
if (projectData.deploymentDir)
projectModel.deploymentDir = projectData.deploymentDir
if (projectData.packageHash)
deploymentDialog.packageHash = projectData.packageHash
if (projectData.packageBase64)
deploymentDialog.packageBase64 = projectData.packageBase64
if (projectData.applicationUrlEth)
deploymentDialog.applicationUrlEth = projectData.applicationUrlEth
if (projectData.applicationUrlHttp)
deploymentDialog.applicationUrlHttp = projectData.applicationUrlHttp
if (!projectData.title) {
var parts = path.split("/");
projectData.title = parts[parts.length - 2];
}
deploymentAddresses = projectData.deploymentAddresses ? projectData.deploymentAddresses : [];
projectTitle = projectData.title;
projectPath = path;
if (!projectData.files)
projectData.files = [];
for(var i = 0; i < projectData.files.length; i++) {
addFile(projectData.files[i]);
}
projectSettings.lastProjectPath = path;
projectLoading(projectData);
projectLoaded()
//TODO: move this to codemodel
var contractSources = {};
for (var d = 0; d < listModel.count; d++) {
var doc = listModel.get(d);
if (doc.isContract)
contractSources[doc.documentId] = fileIo.readFile(doc.path);
}
codeModel.reset(contractSources);
closeProject(function() {
console.log("Loading project at " + path);
var projectFile = path + projectFileName;
var json = fileIo.readFile(projectFile);
var projectData = JSON.parse(json);
if (projectData.deploymentDir)
projectModel.deploymentDir = projectData.deploymentDir
if (projectData.packageHash)
deploymentDialog.packageHash = projectData.packageHash
if (projectData.packageBase64)
deploymentDialog.packageBase64 = projectData.packageBase64
if (projectData.applicationUrlEth)
deploymentDialog.applicationUrlEth = projectData.applicationUrlEth
if (projectData.applicationUrlHttp)
deploymentDialog.applicationUrlHttp = projectData.applicationUrlHttp
if (!projectData.title) {
var parts = path.split("/");
projectData.title = parts[parts.length - 2];
}
deploymentAddresses = projectData.deploymentAddresses ? projectData.deploymentAddresses : [];
projectTitle = projectData.title;
projectPath = path;
if (!projectData.files)
projectData.files = [];
for(var i = 0; i < projectData.files.length; i++) {
addFile(projectData.files[i]);
}
projectSettings.lastProjectPath = path;
projectLoading(projectData);
projectLoaded()
//TODO: move this to codemodel
var contractSources = {};
for (var d = 0; d < listModel.count; d++) {
var doc = listModel.get(d);
if (doc.isContract)
contractSources[doc.documentId] = fileIo.readFile(doc.path);
}
codeModel.reset(contractSources);
});
}
function addFile(fileName) {
@ -163,7 +162,6 @@ function addFile(fileName) {
};
projectListModel.append(docData);
saveProjectFile();
fileIo.watchFileChanged(p);
return docData.documentId;
}
@ -232,27 +230,28 @@ function doCloseProject() {
}
function doCreateProject(title, path) {
closeProject();
console.log("Creating project " + title + " at " + path);
if (path[path.length - 1] !== "/")
path += "/";
var dirPath = path + title + "/";
fileIo.makeDir(dirPath);
var projectFile = dirPath + projectFileName;
var indexFile = "index.html";
var contractsFile = "contract.sol";
var projectData = {
title: title,
files: [ contractsFile, indexFile ]
};
//TODO: copy from template
fileIo.writeFile(dirPath + indexFile, htmlTemplate);
fileIo.writeFile(dirPath + contractsFile, contractTemplate);
newProject(projectData);
var json = JSON.stringify(projectData, null, "\t");
fileIo.writeFile(projectFile, json);
loadProject(dirPath);
closeProject(function() {
console.log("Creating project " + title + " at " + path);
if (path[path.length - 1] !== "/")
path += "/";
var dirPath = path + title + "/";
fileIo.makeDir(dirPath);
var projectFile = dirPath + projectFileName;
var indexFile = "index.html";
var contractsFile = "contract.sol";
var projectData = {
title: title,
files: [ contractsFile, indexFile ]
};
//TODO: copy from template
fileIo.writeFile(dirPath + indexFile, htmlTemplate);
fileIo.writeFile(dirPath + contractsFile, contractTemplate);
newProject(projectData);
var json = JSON.stringify(projectData, null, "\t");
fileIo.writeFile(projectFile, json);
loadProject(dirPath);
});
}
function doAddExistingFiles(files) {
@ -263,6 +262,7 @@ function doAddExistingFiles(files) {
if (sourcePath !== destPath)
fileIo.copyFile(sourcePath, destPath);
var id = addFile(sourceFileName);
saveProjectFile();
documentAdded(id)
}
}
@ -321,6 +321,7 @@ function createAndAddFile(name, extension, content) {
var filePath = projectPath + fileName;
fileIo.writeFile(filePath, content);
var id = addFile(fileName);
saveProjectFile();
documentAdded(id);
}

37
mix/qml/main.qml

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

124
mix/res.qrc

@ -1,65 +1,5 @@
<RCC>
<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-BlackIt.ttf</file>
<file>qml/fonts/SourceSansPro-Bold.ttf</file>
@ -75,43 +15,43 @@
<file>qml/fonts/SourceSerifPro-Bold.ttf</file>
<file>qml/fonts/SourceSerifPro-Regular.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/opentriangleindicator_filesproject.png</file>
<file>qml/img/projecticon.png</file>
<file>qml/SourceSansProRegular.qml</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/console.png</file>
<file>qml/img/copy.png</file>
<file>qml/img/dappProjectIcon.png</file>
<file>qml/img/delete_sign.png</file>
<file>qml/img/edit.png</file>
<file>qml/DefaultLabel.qml</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/exit.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/b64.png</file>
<file>qml/img/exit.png</file>
<file>qml/img/opentriangleindicator.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/note.png</file>
<file>qml/LogsPane.qml</file>
<file>qml/img/copy.png</file>
<file>qml/img/broom.png</file>
<file>qml/LogsPaneStyle.qml</file>
<file>qml/StructView.qml</file>
<file>qml/img/console.png</file>
<file>qml/VariablesView.qml</file>
<file>qml/img/search_filled.png</file>
<file>res/mix_256x256x32.png</file>
<file>stdc/config.sol</file>
<file>stdc/namereg.sol</file>
<file>stdc/std.sol</file>
</qresource>
</RCC>

11
mix/web.qrc

@ -28,5 +28,16 @@
<file>qml/html/cm/solidityToken.js</file>
<file>qml/html/cm/javascript-hint.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>
</RCC>

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

Loading…
Cancel
Save