Browse Source

Merge remote-tracking branch 'upstream/develop' into evmjit

cl-refactor
Paweł Bylica 10 years ago
parent
commit
30f614531b
  1. 13
      alethzero/MainWin.cpp
  2. 10
      alethzero/MainWin.h
  3. 4
      alethzero/Transact.cpp
  4. 12
      alethzero/Transact.h
  5. 1
      evmjit/libevmjit/ExecStats.h
  6. 2
      evmjit/libevmjit/preprocessor/llvm_includes_end.h
  7. 4
      evmjit/libevmjit/preprocessor/llvm_includes_start.h
  8. 49
      libsolidity/AST.cpp
  9. 33
      libsolidity/AST.h
  10. 1
      libsolidity/ASTForward.h
  11. 12
      libsolidity/ASTPrinter.cpp
  12. 2
      libsolidity/ASTPrinter.h
  13. 4
      libsolidity/ASTVisitor.h
  14. 28
      libsolidity/AST_accept.h
  15. 2
      libsolidity/CompilerStack.h
  16. 32
      libsolidity/CompilerUtils.cpp
  17. 4
      libsolidity/CompilerUtils.h
  18. 26
      libsolidity/ExpressionCompiler.cpp
  19. 4
      libsolidity/ExpressionCompiler.h
  20. 6
      libsolidity/NameAndTypeResolver.cpp
  21. 200
      libsolidity/Parser.cpp
  22. 44
      libsolidity/Parser.h
  23. 64
      libsolidity/Types.cpp
  24. 44
      libsolidity/Types.h
  25. 5
      libsolidity/grammar.txt
  26. 99
      mix/FileIo.cpp
  27. 25
      mix/FileIo.h
  28. 2
      mix/QVariableDefinition.h
  29. 232
      mix/qml/DeploymentDialog.qml
  30. 39
      mix/qml/ProjectModel.qml
  31. 1
      mix/qml/StatusPane.qml
  32. 1
      mix/qml/Style.qml
  33. 2
      mix/qml/TransactionDialog.qml
  34. 244
      mix/qml/js/ProjectModel.js
  35. 16
      mix/qml/js/QEtherHelper.js
  36. 1
      mix/res.qrc
  37. 4
      test/CMakeLists.txt
  38. 9
      test/SolidityNameAndTypeResolution.cpp
  39. 51
      test/SolidityParser.cpp
  40. 50
      test/stSolidityTestFiller.json
  41. 72
      test/stTransactionTestFiller.json
  42. 15
      test/ttTransactionTestFiller.json

13
alethzero/MainWin.cpp

@ -1458,7 +1458,7 @@ void Main::on_debugCurrent_triggered()
} }
} }
void Main::on_debugDumpState_triggered(int _add) void Main::debugDumpState(int _add)
{ {
if (auto item = ui->blocks->currentItem()) if (auto item = ui->blocks->currentItem())
{ {
@ -1479,11 +1479,6 @@ void Main::on_debugDumpState_triggered(int _add)
} }
} }
void Main::on_debugDumpStatePre_triggered()
{
on_debugDumpState_triggered(0);
}
void Main::on_contracts_currentItemChanged() void Main::on_contracts_currentItemChanged()
{ {
ui->contractInfo->clear(); ui->contractInfo->clear();
@ -1511,7 +1506,7 @@ void Main::on_contracts_currentItemChanged()
} }
} }
void Main::on_idealPeers_valueChanged() void Main::on_idealPeers_valueChanged(int)
{ {
m_webThree->setIdealPeerCount(ui->idealPeers->value()); m_webThree->setIdealPeerCount(ui->idealPeers->value());
} }
@ -1523,11 +1518,11 @@ void Main::on_ourAccounts_doubleClicked()
qApp->clipboard()->setText(QString::fromStdString(toHex(h.asArray()))); qApp->clipboard()->setText(QString::fromStdString(toHex(h.asArray())));
} }
void Main::on_log_doubleClicked() /*void Main::on_log_doubleClicked()
{ {
ui->log->setPlainText(""); ui->log->setPlainText("");
m_logHistory.clear(); m_logHistory.clear();
} }*/
void Main::on_accounts_doubleClicked() void Main::on_accounts_doubleClicked()
{ {

10
alethzero/MainWin.h

@ -109,7 +109,7 @@ private slots:
void on_go_triggered(); void on_go_triggered();
void on_net_triggered(); void on_net_triggered();
void on_connect_triggered(); void on_connect_triggered();
void on_idealPeers_valueChanged(); void on_idealPeers_valueChanged(int);
// Mining // Mining
void on_mine_triggered(); void on_mine_triggered();
@ -142,7 +142,7 @@ private slots:
void on_blocks_currentItemChanged(); void on_blocks_currentItemChanged();
// Logging // Logging
void on_log_doubleClicked(); // void on_log_doubleClicked();
void on_verbosity_valueChanged(); void on_verbosity_valueChanged();
// Misc // Misc
@ -162,8 +162,8 @@ private slots:
// Debugger // Debugger
void on_debugCurrent_triggered(); void on_debugCurrent_triggered();
void on_debugDumpState_triggered(int _add = 1); void on_debugDumpState_triggered() { debugDumpState(1); }
void on_debugDumpStatePre_triggered(); void on_debugDumpStatePre_triggered() { debugDumpState(0); }
// Whisper // Whisper
void on_newIdentity_triggered(); void on_newIdentity_triggered();
@ -177,6 +177,8 @@ signals:
void poll(); void poll();
private: private:
void debugDumpState(int _add);
dev::p2p::NetworkPreferences netPrefs() const; dev::p2p::NetworkPreferences netPrefs() const;
QString lookup(QString const& _n) const; QString lookup(QString const& _n) const;

4
alethzero/Transact.cpp

@ -55,7 +55,7 @@ Transact::Transact(Context* _c, QWidget* _parent):
ui->valueUnits->setCurrentIndex(6); ui->valueUnits->setCurrentIndex(6);
ui->gasPriceUnits->setCurrentIndex(4); ui->gasPriceUnits->setCurrentIndex(4);
ui->gasPrice->setValue(10); ui->gasPrice->setValue(10);
on_destination_currentTextChanged(); on_destination_currentTextChanged(QString());
} }
Transact::~Transact() Transact::~Transact()
@ -147,7 +147,7 @@ string Transact::getFunctionHashes(dev::solidity::CompilerStack const& _compiler
return ret; return ret;
} }
void Transact::on_destination_currentTextChanged() void Transact::on_destination_currentTextChanged(QString)
{ {
if (ui->destination->currentText().size() && ui->destination->currentText() != "(Create Contract)") if (ui->destination->currentText().size() && ui->destination->currentText() != "(Create Contract)")
if (Address a = m_context->fromString(ui->destination->currentText())) if (Address a = m_context->fromString(ui->destination->currentText()))

12
alethzero/Transact.h

@ -44,12 +44,12 @@ public:
void setEnvironment(QList<dev::KeyPair> _myKeys, dev::eth::Client* _eth, NatSpecFace* _natSpecDB); void setEnvironment(QList<dev::KeyPair> _myKeys, dev::eth::Client* _eth, NatSpecFace* _natSpecDB);
private slots: private slots:
void on_destination_currentTextChanged(); void on_destination_currentTextChanged(QString);
void on_value_valueChanged() { updateFee(); } void on_value_valueChanged(int) { updateFee(); }
void on_gas_valueChanged() { updateFee(); } void on_gas_valueChanged(int) { updateFee(); }
void on_valueUnits_currentIndexChanged() { updateFee(); } void on_valueUnits_currentIndexChanged(int) { updateFee(); }
void on_gasPriceUnits_currentIndexChanged() { updateFee(); } void on_gasPriceUnits_currentIndexChanged(int) { updateFee(); }
void on_gasPrice_valueChanged() { updateFee(); } void on_gasPrice_valueChanged(int) { updateFee(); }
void on_data_textChanged() { rejigData(); } void on_data_textChanged() { rejigData(); }
void on_optimize_clicked() { rejigData(); } void on_optimize_clicked() { rejigData(); }
void on_send_clicked(); void on_send_clicked();

1
evmjit/libevmjit/ExecStats.h

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <string>
#include <chrono> #include <chrono>
#include "ExecutionEngine.h" #include "ExecutionEngine.h"

2
evmjit/libevmjit/preprocessor/llvm_includes_end.h

@ -1,5 +1,7 @@
#if defined(_MSC_VER) #if defined(_MSC_VER)
#pragma warning(pop) #pragma warning(pop)
#elif defined(__clang__)
#pragma clang diagnostic pop
#else #else
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif

4
evmjit/libevmjit/preprocessor/llvm_includes_start.h

@ -1,6 +1,10 @@
#if defined(_MSC_VER) #if defined(_MSC_VER)
#pragma warning(push) #pragma warning(push)
#pragma warning(disable: 4267 4244 4800) #pragma warning(disable: 4267 4244 4800)
#elif defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#pragma clang diagnostic ignored "-Wconversion"
#else #else
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wunused-parameter"

49
libsolidity/AST.cpp

@ -609,13 +609,48 @@ void MemberAccess::checkTypeRequirements()
void IndexAccess::checkTypeRequirements() void IndexAccess::checkTypeRequirements()
{ {
m_base->checkTypeRequirements(); m_base->checkTypeRequirements();
if (m_base->getType()->getCategory() != Type::Category::Mapping) switch (m_base->getType()->getCategory())
BOOST_THROW_EXCEPTION(m_base->createTypeError("Indexed expression has to be a mapping (is " + {
m_base->getType()->toString() + ")")); case Type::Category::Array:
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType()); {
m_index->expectType(*type.getKeyType()); ArrayType const& type = dynamic_cast<ArrayType const&>(*m_base->getType());
m_type = type.getValueType(); if (!m_index)
m_isLValue = true; BOOST_THROW_EXCEPTION(createTypeError("Index expression cannot be omitted."));
m_index->expectType(IntegerType(256));
m_type = type.getBaseType();
m_isLValue = true;
break;
}
case Type::Category::Mapping:
{
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType());
if (!m_index)
BOOST_THROW_EXCEPTION(createTypeError("Index expression cannot be omitted."));
m_index->expectType(*type.getKeyType());
m_type = type.getValueType();
m_isLValue = true;
break;
}
case Type::Category::TypeType:
{
TypeType const& type = dynamic_cast<TypeType const&>(*m_base->getType());
if (!m_index)
m_type = make_shared<TypeType>(make_shared<ArrayType>(ArrayType::Location::Memory, type.getActualType()));
else
{
m_index->checkTypeRequirements();
auto length = dynamic_cast<IntegerConstantType const*>(m_index->getType().get());
if (!length)
BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected."));
m_type = make_shared<TypeType>(make_shared<ArrayType>(
ArrayType::Location::Memory, type.getActualType(), length->literalValue(nullptr)));
}
break;
}
default:
BOOST_THROW_EXCEPTION(m_base->createTypeError(
"Indexed expression has to be a type, mapping or array (is " + m_base->getType()->toString() + ")"));
}
} }
void Identifier::checkTypeRequirements() void Identifier::checkTypeRequirements()

33
libsolidity/AST.h

@ -441,7 +441,7 @@ public:
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
TypeName const* getTypeName() const { return m_typeName.get(); } TypeName* getTypeName() { return m_typeName.get(); }
ASTPointer<Expression> const& getValue() const { return m_value; } ASTPointer<Expression> const& getValue() const { return m_value; }
/// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly /// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly
@ -588,7 +588,7 @@ public:
/// Retrieve the element of the type hierarchy this node refers to. Can return an empty shared /// Retrieve the element of the type hierarchy this node refers to. Can return an empty shared
/// pointer until the types have been resolved using the @ref NameAndTypeResolver. /// pointer until the types have been resolved using the @ref NameAndTypeResolver.
/// If it returns an empty shared pointer after that, this indicates that the type was not found. /// If it returns an empty shared pointer after that, this indicates that the type was not found.
virtual std::shared_ptr<Type const> toType() const = 0; virtual std::shared_ptr<Type const> toType() = 0;
}; };
/** /**
@ -605,7 +605,7 @@ public:
} }
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual std::shared_ptr<Type const> toType() const override { return Type::fromElementaryTypeName(m_type); } virtual std::shared_ptr<Type const> toType() override { return Type::fromElementaryTypeName(m_type); }
Token::Value getTypeName() const { return m_type; } Token::Value getTypeName() const { return m_type; }
@ -623,7 +623,7 @@ public:
TypeName(_location), m_name(_name), m_referencedDeclaration(nullptr) {} TypeName(_location), m_name(_name), m_referencedDeclaration(nullptr) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual std::shared_ptr<Type const> toType() const override { return Type::fromUserDefinedTypeName(*this); } virtual std::shared_ptr<Type const> toType() override { return Type::fromUserDefinedTypeName(*this); }
ASTString const& getName() const { return *m_name; } ASTString const& getName() const { return *m_name; }
void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
@ -646,7 +646,7 @@ public:
TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual std::shared_ptr<Type const> toType() const override { return Type::fromMapping(*this); } virtual TypePointer toType() override { return Type::fromMapping(*m_keyType, *m_valueType); }
ElementaryTypeName const& getKeyType() const { return *m_keyType; } ElementaryTypeName const& getKeyType() const { return *m_keyType; }
TypeName const& getValueType() const { return *m_valueType; } TypeName const& getValueType() const { return *m_valueType; }
@ -656,6 +656,27 @@ private:
ASTPointer<TypeName> m_valueType; ASTPointer<TypeName> m_valueType;
}; };
/**
* An array type, can be "typename[]" or "typename[<expression>]".
*/
class ArrayTypeName: public TypeName
{
public:
ArrayTypeName(Location const& _location, ASTPointer<TypeName> const& _baseType,
ASTPointer<Expression> const& _length):
TypeName(_location), m_baseType(_baseType), m_length(_length) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual std::shared_ptr<Type const> toType() override { return Type::fromArrayTypeName(*m_baseType, m_length.get()); }
TypeName const& getBaseType() const { return *m_baseType; }
Expression const* getLength() const { return m_length.get(); }
private:
ASTPointer<TypeName> m_baseType;
ASTPointer<Expression> m_length; ///< Length of the array, might be empty.
};
/// @} /// @}
/// Statements /// Statements
@ -1081,7 +1102,7 @@ public:
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Expression const& getBaseExpression() const { return *m_base; } Expression const& getBaseExpression() const { return *m_base; }
Expression const& getIndexExpression() const { return *m_index; } Expression const* getIndexExpression() const { return m_index.get(); }
private: private:
ASTPointer<Expression> m_base; ASTPointer<Expression> m_base;

1
libsolidity/ASTForward.h

@ -53,6 +53,7 @@ class TypeName;
class ElementaryTypeName; class ElementaryTypeName;
class UserDefinedTypeName; class UserDefinedTypeName;
class Mapping; class Mapping;
class ArrayTypeName;
class Statement; class Statement;
class Block; class Block;
class PlaceholderStatement; class PlaceholderStatement;

12
libsolidity/ASTPrinter.cpp

@ -155,6 +155,13 @@ bool ASTPrinter::visit(Mapping const& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(ArrayTypeName const& _node)
{
writeLine("ArrayTypeName");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Statement const& _node) bool ASTPrinter::visit(Statement const& _node)
{ {
writeLine("Statement"); writeLine("Statement");
@ -419,6 +426,11 @@ void ASTPrinter::endVisit(Mapping const&)
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(ArrayTypeName const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Statement const&) void ASTPrinter::endVisit(Statement const&)
{ {
m_indentation--; m_indentation--;

2
libsolidity/ASTPrinter.h

@ -58,6 +58,7 @@ public:
bool visit(ElementaryTypeName const& _node) override; bool visit(ElementaryTypeName const& _node) override;
bool visit(UserDefinedTypeName const& _node) override; bool visit(UserDefinedTypeName const& _node) override;
bool visit(Mapping const& _node) override; bool visit(Mapping const& _node) override;
bool visit(ArrayTypeName const& _node) override;
bool visit(Statement const& _node) override; bool visit(Statement const& _node) override;
bool visit(Block const& _node) override; bool visit(Block const& _node) override;
bool visit(PlaceholderStatement const& _node) override; bool visit(PlaceholderStatement const& _node) override;
@ -99,6 +100,7 @@ public:
void endVisit(ElementaryTypeName const&) override; void endVisit(ElementaryTypeName const&) override;
void endVisit(UserDefinedTypeName const&) override; void endVisit(UserDefinedTypeName const&) override;
void endVisit(Mapping const&) override; void endVisit(Mapping const&) override;
void endVisit(ArrayTypeName const&) override;
void endVisit(Statement const&) override; void endVisit(Statement const&) override;
void endVisit(Block const&) override; void endVisit(Block const&) override;
void endVisit(PlaceholderStatement const&) override; void endVisit(PlaceholderStatement const&) override;

4
libsolidity/ASTVisitor.h

@ -59,6 +59,7 @@ public:
virtual bool visit(ElementaryTypeName&) { return true; } virtual bool visit(ElementaryTypeName&) { return true; }
virtual bool visit(UserDefinedTypeName&) { return true; } virtual bool visit(UserDefinedTypeName&) { return true; }
virtual bool visit(Mapping&) { return true; } virtual bool visit(Mapping&) { return true; }
virtual bool visit(ArrayTypeName&) { return true; }
virtual bool visit(Statement&) { return true; } virtual bool visit(Statement&) { return true; }
virtual bool visit(Block&) { return true; } virtual bool visit(Block&) { return true; }
virtual bool visit(PlaceholderStatement&) { return true; } virtual bool visit(PlaceholderStatement&) { return true; }
@ -102,6 +103,7 @@ public:
virtual void endVisit(ElementaryTypeName&) { } virtual void endVisit(ElementaryTypeName&) { }
virtual void endVisit(UserDefinedTypeName&) { } virtual void endVisit(UserDefinedTypeName&) { }
virtual void endVisit(Mapping&) { } virtual void endVisit(Mapping&) { }
virtual void endVisit(ArrayTypeName&) { }
virtual void endVisit(Statement&) { } virtual void endVisit(Statement&) { }
virtual void endVisit(Block&) { } virtual void endVisit(Block&) { }
virtual void endVisit(PlaceholderStatement&) { } virtual void endVisit(PlaceholderStatement&) { }
@ -149,6 +151,7 @@ public:
virtual bool visit(ElementaryTypeName const&) { return true; } virtual bool visit(ElementaryTypeName const&) { return true; }
virtual bool visit(UserDefinedTypeName const&) { return true; } virtual bool visit(UserDefinedTypeName const&) { return true; }
virtual bool visit(Mapping const&) { return true; } virtual bool visit(Mapping const&) { return true; }
virtual bool visit(ArrayTypeName const&) { return true; }
virtual bool visit(Statement const&) { return true; } virtual bool visit(Statement const&) { return true; }
virtual bool visit(Block const&) { return true; } virtual bool visit(Block const&) { return true; }
virtual bool visit(PlaceholderStatement const&) { return true; } virtual bool visit(PlaceholderStatement const&) { return true; }
@ -192,6 +195,7 @@ public:
virtual void endVisit(ElementaryTypeName const&) { } virtual void endVisit(ElementaryTypeName const&) { }
virtual void endVisit(UserDefinedTypeName const&) { } virtual void endVisit(UserDefinedTypeName const&) { }
virtual void endVisit(Mapping const&) { } virtual void endVisit(Mapping const&) { }
virtual void endVisit(ArrayTypeName const&) { }
virtual void endVisit(Statement const&) { } virtual void endVisit(Statement const&) { }
virtual void endVisit(Block const&) { } virtual void endVisit(Block const&) { }
virtual void endVisit(PlaceholderStatement const&) { } virtual void endVisit(PlaceholderStatement const&) { }

28
libsolidity/AST_accept.h

@ -327,6 +327,28 @@ void Mapping::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
void ArrayTypeName::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_baseType->accept(_visitor);
if (m_length)
m_length->accept(_visitor);
}
_visitor.endVisit(*this);
}
void ArrayTypeName::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_baseType->accept(_visitor);
if (m_length)
m_length->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Block::accept(ASTVisitor& _visitor) void Block::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
@ -604,7 +626,8 @@ void IndexAccess::accept(ASTVisitor& _visitor)
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{ {
m_base->accept(_visitor); m_base->accept(_visitor);
m_index->accept(_visitor); if (m_index)
m_index->accept(_visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
@ -614,7 +637,8 @@ void IndexAccess::accept(ASTConstVisitor& _visitor) const
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{ {
m_base->accept(_visitor); m_base->accept(_visitor);
m_index->accept(_visitor); if (m_index)
m_index->accept(_visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }

2
libsolidity/CompilerStack.h

@ -60,7 +60,7 @@ class CompilerStack: boost::noncopyable
{ {
public: public:
/// Creates a new compiler stack. Adds standard sources if @a _addStandardSources. /// Creates a new compiler stack. Adds standard sources if @a _addStandardSources.
explicit CompilerStack(bool _addStandardSources = false); explicit CompilerStack(bool _addStandardSources = true);
/// Adds a source object (e.g. file) to the parser. After this, parse has to be called again. /// Adds a source object (e.g. file) to the parser. After this, parse has to be called again.
/// @returns true if a source object by the name already existed and was replaced. /// @returns true if a source object by the name already existed and was replaced.

32
libsolidity/CompilerUtils.cpp

@ -36,14 +36,14 @@ const unsigned int CompilerUtils::dataStartOffset = 4;
unsigned CompilerUtils::loadFromMemory(unsigned _offset, Type const& _type, unsigned CompilerUtils::loadFromMemory(unsigned _offset, Type const& _type,
bool _fromCalldata, bool _padToWordBoundaries) bool _fromCalldata, bool _padToWordBoundaries)
{ {
solAssert(_type.getCategory() != Type::Category::ByteArray, "Unable to statically load dynamic type."); solAssert(_type.getCategory() != Type::Category::Array, "Unable to statically load dynamic type.");
m_context << u256(_offset); m_context << u256(_offset);
return loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries); return loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries);
} }
void CompilerUtils::loadFromMemoryDynamic(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries) void CompilerUtils::loadFromMemoryDynamic(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries)
{ {
solAssert(_type.getCategory() != Type::Category::ByteArray, "Byte arrays not yet implemented."); solAssert(_type.getCategory() != Type::Category::Array, "Arrays not yet implemented.");
m_context << eth::Instruction::DUP1; m_context << eth::Instruction::DUP1;
unsigned numBytes = loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries); unsigned numBytes = loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries);
// update memory counter // update memory counter
@ -55,7 +55,7 @@ void CompilerUtils::loadFromMemoryDynamic(Type const& _type, bool _fromCalldata,
unsigned CompilerUtils::storeInMemory(unsigned _offset, Type const& _type, bool _padToWordBoundaries) unsigned CompilerUtils::storeInMemory(unsigned _offset, Type const& _type, bool _padToWordBoundaries)
{ {
solAssert(_type.getCategory() != Type::Category::ByteArray, "Unable to statically store dynamic type."); solAssert(_type.getCategory() != Type::Category::Array, "Unable to statically store dynamic type.");
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries); unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
if (numBytes > 0) if (numBytes > 0)
m_context << u256(_offset) << eth::Instruction::MSTORE; m_context << u256(_offset) << eth::Instruction::MSTORE;
@ -64,11 +64,12 @@ unsigned CompilerUtils::storeInMemory(unsigned _offset, Type const& _type, bool
void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries) void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries)
{ {
if (_type.getCategory() == Type::Category::ByteArray) if (_type.getCategory() == Type::Category::Array)
{ {
auto const& type = dynamic_cast<ByteArrayType const&>(_type); auto const& type = dynamic_cast<ArrayType const&>(_type);
solAssert(type.isByteArray(), "Non byte arrays not yet implemented here.");
if (type.getLocation() == ByteArrayType::Location::CallData) if (type.getLocation() == ArrayType::Location::CallData)
{ {
// stack: target source_offset source_len // stack: target source_offset source_len
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5 m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5
@ -79,7 +80,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
} }
else else
{ {
solAssert(type.getLocation() == ByteArrayType::Location::Storage, "Memory byte arrays not yet implemented."); solAssert(type.getLocation() == ArrayType::Location::Storage, "Memory byte arrays not yet implemented.");
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
// stack here: memory_offset storage_offset length_bytes // stack here: memory_offset storage_offset length_bytes
// jump to end if length is zero // jump to end if length is zero
@ -163,16 +164,18 @@ void CompilerUtils::computeHashStatic(Type const& _type, bool _padToWordBoundari
m_context << u256(length) << u256(0) << eth::Instruction::SHA3; m_context << u256(length) << u256(0) << eth::Instruction::SHA3;
} }
void CompilerUtils::copyByteArrayToStorage(ByteArrayType const& _targetType, void CompilerUtils::copyByteArrayToStorage(
ByteArrayType const& _sourceType) const ArrayType const& _targetType, ArrayType const& _sourceType) const
{ {
// stack layout: [source_ref] target_ref (top) // stack layout: [source_ref] target_ref (top)
// need to leave target_ref on the stack at the end // need to leave target_ref on the stack at the end
solAssert(_targetType.getLocation() == ByteArrayType::Location::Storage, ""); solAssert(_targetType.getLocation() == ArrayType::Location::Storage, "");
solAssert(_targetType.isByteArray(), "Non byte arrays not yet implemented here.");
solAssert(_sourceType.isByteArray(), "Non byte arrays not yet implemented here.");
switch (_sourceType.getLocation()) switch (_sourceType.getLocation())
{ {
case ByteArrayType::Location::CallData: case ArrayType::Location::CallData:
{ {
// This also assumes that after "length" we only have zeros, i.e. it cannot be used to // This also assumes that after "length" we only have zeros, i.e. it cannot be used to
// slice a byte array from calldata. // slice a byte array from calldata.
@ -224,7 +227,7 @@ void CompilerUtils::copyByteArrayToStorage(ByteArrayType const& _targetType,
<< eth::Instruction::POP << eth::Instruction::POP; << eth::Instruction::POP << eth::Instruction::POP;
break; break;
} }
case ByteArrayType::Location::Storage: case ArrayType::Location::Storage:
{ {
// this copies source to target and also clears target if it was larger // this copies source to target and also clears target if it was larger
@ -313,9 +316,10 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda
return numBytes; return numBytes;
} }
void CompilerUtils::clearByteArray(ByteArrayType const& _type) const void CompilerUtils::clearByteArray(ArrayType const& _type) const
{ {
solAssert(_type.getLocation() == ByteArrayType::Location::Storage, ""); solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
solAssert(_type.isByteArray(), "Non byte arrays not yet implemented here.");
// fetch length // fetch length
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD; m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;

4
libsolidity/CompilerUtils.h

@ -82,11 +82,11 @@ public:
/// Copies a byte array to a byte array in storage. /// Copies a byte array to a byte array in storage.
/// Stack pre: [source_reference] target_reference /// Stack pre: [source_reference] target_reference
/// Stack post: target_reference /// Stack post: target_reference
void copyByteArrayToStorage(ByteArrayType const& _targetType, ByteArrayType const& _sourceType) const; void copyByteArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const;
/// Clears the length and data elements of the byte array referenced on the stack. /// Clears the length and data elements of the byte array referenced on the stack.
/// Stack pre: reference /// Stack pre: reference
/// Stack post: /// Stack post:
void clearByteArray(ByteArrayType const& _type) const; void clearByteArray(ArrayType const& _type) const;
/// Bytes we need to the start of call data. /// Bytes we need to the start of call data.
/// - The size in bytes of the function (hash) identifier. /// - The size in bytes of the function (hash) identifier.

26
libsolidity/ExpressionCompiler.cpp

@ -530,20 +530,21 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
m_context << enumType->getMemberValue(_memberAccess.getMemberName()); m_context << enumType->getMemberValue(_memberAccess.getMemberName());
break; break;
} }
case Type::Category::ByteArray: case Type::Category::Array:
{ {
solAssert(member == "length", "Illegal bytearray member."); solAssert(member == "length", "Illegal array member.");
auto const& type = dynamic_cast<ByteArrayType const&>(*_memberAccess.getExpression().getType()); auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.getExpression().getType());
solAssert(type.isByteArray(), "Non byte arrays not yet implemented here.");
switch (type.getLocation()) switch (type.getLocation())
{ {
case ByteArrayType::Location::CallData: case ArrayType::Location::CallData:
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
break; break;
case ByteArrayType::Location::Storage: case ArrayType::Location::Storage:
m_context << eth::Instruction::SLOAD; m_context << eth::Instruction::SLOAD;
break; break;
default: default:
solAssert(false, "Unsupported byte array location."); solAssert(false, "Unsupported array location.");
break; break;
} }
break; break;
@ -561,7 +562,8 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
solAssert(baseType.getCategory() == Type::Category::Mapping, ""); solAssert(baseType.getCategory() == Type::Category::Mapping, "");
Type const& keyType = *dynamic_cast<MappingType const&>(baseType).getKeyType(); Type const& keyType = *dynamic_cast<MappingType const&>(baseType).getKeyType();
m_context << u256(0); m_context << u256(0);
appendExpressionCopyToMemory(keyType, _indexAccess.getIndexExpression()); solAssert(_indexAccess.getIndexExpression(), "Index expression expected.");
appendExpressionCopyToMemory(keyType, *_indexAccess.getIndexExpression());
solAssert(baseType.getSizeOnStack() == 1, solAssert(baseType.getSizeOnStack() == 1,
"Unexpected: Not exactly one stack slot taken by subscriptable expression."); "Unexpected: Not exactly one stack slot taken by subscriptable expression.");
m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::SWAP1;
@ -1135,11 +1137,11 @@ void ExpressionCompiler::LValue::storeValue(Type const& _sourceType, Location co
else else
{ {
solAssert(_sourceType.getCategory() == m_dataType->getCategory(), "Wrong type conversation for assignment."); solAssert(_sourceType.getCategory() == m_dataType->getCategory(), "Wrong type conversation for assignment.");
if (m_dataType->getCategory() == Type::Category::ByteArray) if (m_dataType->getCategory() == Type::Category::Array)
{ {
CompilerUtils(*m_context).copyByteArrayToStorage( CompilerUtils(*m_context).copyByteArrayToStorage(
dynamic_cast<ByteArrayType const&>(*m_dataType), dynamic_cast<ArrayType const&>(*m_dataType),
dynamic_cast<ByteArrayType const&>(_sourceType)); dynamic_cast<ArrayType const&>(_sourceType));
if (_move) if (_move)
*m_context << eth::Instruction::POP; *m_context << eth::Instruction::POP;
} }
@ -1210,8 +1212,8 @@ void ExpressionCompiler::LValue::setToZero(Location const& _location) const
break; break;
} }
case LValueType::Storage: case LValueType::Storage:
if (m_dataType->getCategory() == Type::Category::ByteArray) if (m_dataType->getCategory() == Type::Category::Array)
CompilerUtils(*m_context).clearByteArray(dynamic_cast<ByteArrayType const&>(*m_dataType)); CompilerUtils(*m_context).clearByteArray(dynamic_cast<ArrayType const&>(*m_dataType));
else if (m_dataType->getCategory() == Type::Category::Struct) else if (m_dataType->getCategory() == Type::Category::Struct)
{ {
// stack layout: ref // stack layout: ref

4
libsolidity/ExpressionCompiler.h

@ -39,7 +39,7 @@ namespace solidity {
class CompilerContext; class CompilerContext;
class Type; class Type;
class IntegerType; class IntegerType;
class ByteArrayType; class ArrayType;
class StaticStringType; class StaticStringType;
/** /**
@ -165,7 +165,7 @@ private:
/// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue /// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue
void retrieveValueFromStorage(bool _remove = false) const; void retrieveValueFromStorage(bool _remove = false) const;
/// Copies from a byte array to a byte array in storage, both references on the stack. /// Copies from a byte array to a byte array in storage, both references on the stack.
void copyByteArrayToStorage(ByteArrayType const& _targetType, ByteArrayType const& _sourceType) const; void copyByteArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const;
CompilerContext* m_context; CompilerContext* m_context;
LValueType m_type = LValueType::None; LValueType m_type = LValueType::None;

6
libsolidity/NameAndTypeResolver.cpp

@ -334,10 +334,10 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
if (_variable.getTypeName()) if (_variable.getTypeName())
{ {
TypePointer type = _variable.getTypeName()->toType(); TypePointer type = _variable.getTypeName()->toType();
// All byte array parameter types should point to call data // All array parameter types should point to call data
if (_variable.isExternalFunctionParameter()) if (_variable.isExternalFunctionParameter())
if (auto const* byteArrayType = dynamic_cast<ByteArrayType const*>(type.get())) if (auto const* arrayType = dynamic_cast<ArrayType const*>(type.get()))
type = byteArrayType->copyForLocation(ByteArrayType::Location::CallData); type = arrayType->copyForLocation(ArrayType::Location::CallData);
_variable.setType(type); _variable.setType(type);
if (!_variable.getType()) if (!_variable.getType())

200
libsolidity/Parser.cpp

@ -41,8 +41,11 @@ class Parser::ASTNodeFactory
public: public:
ASTNodeFactory(Parser const& _parser): ASTNodeFactory(Parser const& _parser):
m_parser(_parser), m_location(_parser.getPosition(), -1, _parser.getSourceName()) {} m_parser(_parser), m_location(_parser.getPosition(), -1, _parser.getSourceName()) {}
ASTNodeFactory(Parser const& _parser, ASTPointer<ASTNode> const& _childNode):
m_parser(_parser), m_location(_childNode->getLocation()) {}
void markEndPosition() { m_location.end = m_parser.getEndPosition(); } void markEndPosition() { m_location.end = m_parser.getEndPosition(); }
void setLocation(Location const& _location) { m_location = _location; }
void setLocationEmpty() { m_location.end = m_location.start; } void setLocationEmpty() { m_location.end = m_location.start; }
/// Set the end position to the one of the given node. /// Set the end position to the one of the given node.
void setEndPositionFromNode(ASTPointer<ASTNode> const& _node) { m_location.end = _node->getLocation().end; } void setEndPositionFromNode(ASTPointer<ASTNode> const& _node) { m_location.end = _node->getLocation().end; }
@ -299,12 +302,20 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
return nodeFactory.createNode<EnumDefinition>(name, members); return nodeFactory.createNode<EnumDefinition>(name, members);
} }
ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(VarDeclParserOptions const& _options) ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
VarDeclParserOptions const& _options, ASTPointer<TypeName> const& _lookAheadArrayType)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory = _lookAheadArrayType ?
ASTPointer<TypeName> type = parseTypeName(_options.allowVar); ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this);
if (type != nullptr) ASTPointer<TypeName> type;
nodeFactory.setEndPositionFromNode(type); if (_lookAheadArrayType)
type = _lookAheadArrayType;
else
{
type = parseTypeName(_options.allowVar);
if (type != nullptr)
nodeFactory.setEndPositionFromNode(type);
}
bool isIndexed = false; bool isIndexed = false;
ASTPointer<ASTString> identifier; ASTPointer<ASTString> identifier;
Token::Value token = m_scanner->getCurrentToken(); Token::Value token = m_scanner->getCurrentToken();
@ -407,6 +418,7 @@ ASTPointer<Identifier> Parser::parseIdentifier()
ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
{ {
ASTNodeFactory nodeFactory(*this);
ASTPointer<TypeName> type; ASTPointer<TypeName> type;
Token::Value token = m_scanner->getCurrentToken(); Token::Value token = m_scanner->getCurrentToken();
if (Token::isElementaryTypeName(token)) if (Token::isElementaryTypeName(token))
@ -421,9 +433,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
m_scanner->next(); m_scanner->next();
} }
else if (token == Token::Mapping) else if (token == Token::Mapping)
{
type = parseMapping(); type = parseMapping();
}
else if (token == Token::Identifier) else if (token == Token::Identifier)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
@ -432,6 +442,18 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
} }
else else
BOOST_THROW_EXCEPTION(createParserError("Expected type name")); BOOST_THROW_EXCEPTION(createParserError("Expected type name"));
// Parse "[...]" postfixes for arrays.
while (m_scanner->getCurrentToken() == Token::LBrack)
{
m_scanner->next();
ASTPointer<Expression> length;
if (m_scanner->getCurrentToken() != Token::RBrack)
length = parseExpression();
nodeFactory.markEndPosition();
expectToken(Token::RBrack);
type = nodeFactory.createNode<ArrayTypeName>(type, length);
}
return type; return type;
} }
@ -530,7 +552,7 @@ ASTPointer<Statement> Parser::parseStatement()
} }
// fall-through // fall-through
default: default:
statement = parseVarDeclOrExprStmt(); statement = parseSimpleStatement();
} }
expectToken(Token::Semicolon); expectToken(Token::Semicolon);
return statement; return statement;
@ -579,7 +601,7 @@ ASTPointer<ForStatement> Parser::parseForStatement()
// LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RParen? // LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RParen?
if (m_scanner->getCurrentToken() != Token::Semicolon) if (m_scanner->getCurrentToken() != Token::Semicolon)
initExpression = parseVarDeclOrExprStmt(); initExpression = parseSimpleStatement();
expectToken(Token::Semicolon); expectToken(Token::Semicolon);
if (m_scanner->getCurrentToken() != Token::Semicolon) if (m_scanner->getCurrentToken() != Token::Semicolon)
@ -598,48 +620,89 @@ ASTPointer<ForStatement> Parser::parseForStatement()
body); body);
} }
ASTPointer<Statement> Parser::parseVarDeclOrExprStmt() ASTPointer<Statement> Parser::parseSimpleStatement()
{ {
if (peekVariableDeclarationStatement()) // These two cases are very hard to distinguish:
// x[7 * 20 + 3] a; - x[7 * 20 + 3] = 9;
// In the first case, x is a type name, in the second it is the name of a variable.
switch (peekStatementType())
{
case LookAheadInfo::VariableDeclarationStatement:
return parseVariableDeclarationStatement(); return parseVariableDeclarationStatement();
else case LookAheadInfo::ExpressionStatement:
return parseExpressionStatement(); return parseExpressionStatement();
default:
break;
}
// At this point, we have '(Identifier|ElementaryTypeName) "["'.
// We parse '(Identifier|ElementaryTypeName) ( "[" Expression "]" )+' and then decide whether to hand this over
// to ExpressionStatement or create a VariableDeclarationStatement out of it.
ASTPointer<PrimaryExpression> primary;
if (m_scanner->getCurrentToken() == Token::Identifier)
primary = parseIdentifier();
else
{
primary = ASTNodeFactory(*this).createNode<ElementaryTypeNameExpression>(m_scanner->getCurrentToken());
m_scanner->next();
}
vector<pair<ASTPointer<Expression>, Location>> indices;
solAssert(m_scanner->getCurrentToken() == Token::LBrack, "");
Location indexLocation = primary->getLocation();
do
{
expectToken(Token::LBrack);
ASTPointer<Expression> index;
if (m_scanner->getCurrentToken() != Token::RBrack)
index = parseExpression();
indexLocation.end = getEndPosition();
indices.push_back(make_pair(index, indexLocation));
expectToken(Token::RBrack);
}
while (m_scanner->getCurrentToken() == Token::LBrack);
if (m_scanner->getCurrentToken() == Token::Identifier)
return parseVariableDeclarationStatement(typeNameIndexAccessStructure(primary, indices));
else
return parseExpressionStatement(expressionFromIndexAccessStructure(primary, indices));
} }
ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStatement() ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStatement(
ASTPointer<TypeName> const& _lookAheadArrayType)
{ {
ASTNodeFactory nodeFactory(*this);
VarDeclParserOptions options; VarDeclParserOptions options;
options.allowVar = true; options.allowVar = true;
options.allowInitialValue = true; options.allowInitialValue = true;
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options); ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options, _lookAheadArrayType);
ASTNodeFactory nodeFactory(*this, variable);
return nodeFactory.createNode<VariableDeclarationStatement>(variable); return nodeFactory.createNode<VariableDeclarationStatement>(variable);
} }
ASTPointer<ExpressionStatement> Parser::parseExpressionStatement() ASTPointer<ExpressionStatement> Parser::parseExpressionStatement(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure)
{ {
ASTNodeFactory nodeFactory(*this); ASTPointer<Expression> expression = parseExpression(_lookAheadIndexAccessStructure);
ASTPointer<Expression> expression = parseExpression(); return ASTNodeFactory(*this, expression).createNode<ExpressionStatement>(expression);
nodeFactory.setEndPositionFromNode(expression);
return nodeFactory.createNode<ExpressionStatement>(expression);
} }
ASTPointer<Expression> Parser::parseExpression() ASTPointer<Expression> Parser::parseExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure)
{ {
ASTNodeFactory nodeFactory(*this); ASTPointer<Expression> expression = parseBinaryExpression(4, _lookAheadIndexAccessStructure);
ASTPointer<Expression> expression = parseBinaryExpression();
if (!Token::isAssignmentOp(m_scanner->getCurrentToken())) if (!Token::isAssignmentOp(m_scanner->getCurrentToken()))
return expression; return expression;
Token::Value assignmentOperator = expectAssignmentOperator(); Token::Value assignmentOperator = expectAssignmentOperator();
ASTPointer<Expression> rightHandSide = parseExpression(); ASTPointer<Expression> rightHandSide = parseExpression();
ASTNodeFactory nodeFactory(*this, expression);
nodeFactory.setEndPositionFromNode(rightHandSide); nodeFactory.setEndPositionFromNode(rightHandSide);
return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide); return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide);
} }
ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence) ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence,
ASTPointer<Expression> const& _lookAheadIndexAccessStructure)
{ {
ASTNodeFactory nodeFactory(*this); ASTPointer<Expression> expression = parseUnaryExpression(_lookAheadIndexAccessStructure);
ASTPointer<Expression> expression = parseUnaryExpression(); ASTNodeFactory nodeFactory(*this, expression);
int precedence = Token::precedence(m_scanner->getCurrentToken()); int precedence = Token::precedence(m_scanner->getCurrentToken());
for (; precedence >= _minPrecedence; --precedence) for (; precedence >= _minPrecedence; --precedence)
while (Token::precedence(m_scanner->getCurrentToken()) == precedence) while (Token::precedence(m_scanner->getCurrentToken()) == precedence)
@ -653,11 +716,13 @@ ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence)
return expression; return expression;
} }
ASTPointer<Expression> Parser::parseUnaryExpression() ASTPointer<Expression> Parser::parseUnaryExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ?
ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this);
Token::Value token = m_scanner->getCurrentToken(); Token::Value token = m_scanner->getCurrentToken();
if (Token::isUnaryOp(token) || Token::isCountOp(token)) if (!_lookAheadIndexAccessStructure && (Token::isUnaryOp(token) || Token::isCountOp(token)))
{ {
// prefix expression // prefix expression
m_scanner->next(); m_scanner->next();
@ -668,7 +733,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
else else
{ {
// potential postfix expression // potential postfix expression
ASTPointer<Expression> subExpression = parseLeftHandSideExpression(); ASTPointer<Expression> subExpression = parseLeftHandSideExpression(_lookAheadIndexAccessStructure);
token = m_scanner->getCurrentToken(); token = m_scanner->getCurrentToken();
if (!Token::isCountOp(token)) if (!Token::isCountOp(token))
return subExpression; return subExpression;
@ -678,11 +743,16 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
} }
} }
ASTPointer<Expression> Parser::parseLeftHandSideExpression() ASTPointer<Expression> Parser::parseLeftHandSideExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ?
ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this);
ASTPointer<Expression> expression; ASTPointer<Expression> expression;
if (m_scanner->getCurrentToken() == Token::New) if (_lookAheadIndexAccessStructure)
expression = _lookAheadIndexAccessStructure;
else if (m_scanner->getCurrentToken() == Token::New)
{ {
expectToken(Token::New); expectToken(Token::New);
ASTPointer<Identifier> contractName(parseIdentifier()); ASTPointer<Identifier> contractName(parseIdentifier());
@ -699,7 +769,9 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression()
case Token::LBrack: case Token::LBrack:
{ {
m_scanner->next(); m_scanner->next();
ASTPointer<Expression> index = parseExpression(); ASTPointer<Expression> index;
if (m_scanner->getCurrentToken() != Token::RBrack)
index = parseExpression();
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RBrack); expectToken(Token::RBrack);
expression = nodeFactory.createNode<IndexAccess>(expression, index); expression = nodeFactory.createNode<IndexAccess>(expression, index);
@ -774,10 +846,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
m_scanner->next(); m_scanner->next();
} }
else else
{
BOOST_THROW_EXCEPTION(createParserError("Expected primary expression.")); BOOST_THROW_EXCEPTION(createParserError("Expected primary expression."));
return ASTPointer<Expression>(); // this is not reached
}
break; break;
} }
return expression; return expression;
@ -824,18 +893,55 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars
return ret; return ret;
} }
Parser::LookAheadInfo Parser::peekStatementType() const
{
// Distinguish between variable declaration (and potentially assignment) and expression statement
// (which include assignments to other expressions and pre-declared variables).
// We have a variable declaration if we get a keyword that specifies a type name.
// If it is an identifier or an elementary type name followed by an identifier, we also have
// a variable declaration.
// If we get an identifier followed by a "[", it can be both ("type[9] a;" or "arr[9] = 7;").
// In all other cases, we have an expression statement.
Token::Value token(m_scanner->getCurrentToken());
bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier);
if (token == Token::Mapping || token == Token::Var ||
(mightBeTypeName && m_scanner->peekNextToken() == Token::Identifier))
return LookAheadInfo::VariableDeclarationStatement;
if (mightBeTypeName && m_scanner->peekNextToken() == Token::LBrack)
return LookAheadInfo::IndexAccessStructure;
return LookAheadInfo::ExpressionStatement;
}
ASTPointer<TypeName> Parser::typeNameIndexAccessStructure(
ASTPointer<PrimaryExpression> const& _primary, vector<pair<ASTPointer<Expression>, Location>> const& _indices)
{
ASTNodeFactory nodeFactory(*this, _primary);
ASTPointer<TypeName> type;
if (auto identifier = dynamic_cast<Identifier const*>(_primary.get()))
type = nodeFactory.createNode<UserDefinedTypeName>(make_shared<ASTString>(identifier->getName()));
else if (auto typeName = dynamic_cast<ElementaryTypeNameExpression const*>(_primary.get()))
type = nodeFactory.createNode<ElementaryTypeName>(typeName->getTypeToken());
else
solAssert(false, "Invalid type name for array look-ahead.");
for (auto const& lengthExpression: _indices)
{
nodeFactory.setLocation(lengthExpression.second);
type = nodeFactory.createNode<ArrayTypeName>(type, lengthExpression.first);
}
return type;
}
bool Parser::peekVariableDeclarationStatement() ASTPointer<Expression> Parser::expressionFromIndexAccessStructure(
ASTPointer<PrimaryExpression> const& _primary, vector<pair<ASTPointer<Expression>, Location>> const& _indices)
{ {
// distinguish between variable declaration (and potentially assignment) and expression statement ASTNodeFactory nodeFactory(*this, _primary);
// (which include assignments to other expressions and pre-declared variables) ASTPointer<Expression> expression(_primary);
// We have a variable declaration if we get a keyword that specifies a type name, or for (auto const& index: _indices)
// in the case of a user-defined type, we have two identifiers following each other. {
return (m_scanner->getCurrentToken() == Token::Mapping || nodeFactory.setLocation(index.second);
m_scanner->getCurrentToken() == Token::Var || expression = nodeFactory.createNode<IndexAccess>(expression, index.first);
((Token::isElementaryTypeName(m_scanner->getCurrentToken()) || }
m_scanner->getCurrentToken() == Token::Identifier) && return expression;
m_scanner->peekNextToken() == Token::Identifier));
} }
void Parser::expectToken(Token::Value _value) void Parser::expectToken(Token::Value _value)

44
libsolidity/Parser.h

@ -64,7 +64,9 @@ private:
ASTPointer<StructDefinition> parseStructDefinition(); ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<EnumDefinition> parseEnumDefinition(); ASTPointer<EnumDefinition> parseEnumDefinition();
ASTPointer<EnumValue> parseEnumValue(); ASTPointer<EnumValue> parseEnumValue();
ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions()); ASTPointer<VariableDeclaration> parseVariableDeclaration(
VarDeclParserOptions const& _options = VarDeclParserOptions(),
ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>());
ASTPointer<ModifierDefinition> parseModifierDefinition(); ASTPointer<ModifierDefinition> parseModifierDefinition();
ASTPointer<EventDefinition> parseEventDefinition(); ASTPointer<EventDefinition> parseEventDefinition();
ASTPointer<ModifierInvocation> parseModifierInvocation(); ASTPointer<ModifierInvocation> parseModifierInvocation();
@ -77,13 +79,20 @@ private:
ASTPointer<IfStatement> parseIfStatement(); ASTPointer<IfStatement> parseIfStatement();
ASTPointer<WhileStatement> parseWhileStatement(); ASTPointer<WhileStatement> parseWhileStatement();
ASTPointer<ForStatement> parseForStatement(); ASTPointer<ForStatement> parseForStatement();
ASTPointer<Statement> parseVarDeclOrExprStmt(); /// A "simple statement" can be a variable declaration statement or an expression statement.
ASTPointer<VariableDeclarationStatement> parseVariableDeclarationStatement(); ASTPointer<Statement> parseSimpleStatement();
ASTPointer<ExpressionStatement> parseExpressionStatement(); ASTPointer<VariableDeclarationStatement> parseVariableDeclarationStatement(
ASTPointer<Expression> parseExpression(); ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>());
ASTPointer<Expression> parseBinaryExpression(int _minPrecedence = 4); ASTPointer<ExpressionStatement> parseExpressionStatement(
ASTPointer<Expression> parseUnaryExpression(); ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>());
ASTPointer<Expression> parseLeftHandSideExpression(); ASTPointer<Expression> parseExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>());
ASTPointer<Expression> parseBinaryExpression(int _minPrecedence = 4,
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>());
ASTPointer<Expression> parseUnaryExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>());
ASTPointer<Expression> parseLeftHandSideExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>());
ASTPointer<Expression> parsePrimaryExpression(); ASTPointer<Expression> parsePrimaryExpression();
std::vector<ASTPointer<Expression>> parseFunctionCallListArguments(); std::vector<ASTPointer<Expression>> parseFunctionCallListArguments();
std::pair<std::vector<ASTPointer<Expression>>, std::vector<ASTPointer<ASTString>>> parseFunctionCallArguments(); std::pair<std::vector<ASTPointer<Expression>>, std::vector<ASTPointer<ASTString>>> parseFunctionCallArguments();
@ -92,9 +101,24 @@ private:
///@{ ///@{
///@name Helper functions ///@name Helper functions
/// Peeks ahead in the scanner to determine if a variable declaration statement is going to follow /// Used as return value of @see peekStatementType.
bool peekVariableDeclarationStatement(); enum class LookAheadInfo
{
IndexAccessStructure, VariableDeclarationStatement, ExpressionStatement
};
/// Performs limited look-ahead to distinguish between variable declaration and expression statement.
/// For source code of the form "a[][8]" ("IndexAccessStructure"), this is not possible to
/// decide with constant look-ahead.
LookAheadInfo peekStatementType() const;
/// Returns a typename parsed in look-ahead fashion from something like "a[8][2**70]".
ASTPointer<TypeName> typeNameIndexAccessStructure(
ASTPointer<PrimaryExpression> const& _primary,
std::vector<std::pair<ASTPointer<Expression>, Location>> const& _indices);
/// Returns an expression parsed in look-ahead fashion from something like "a[8][2**70]".
ASTPointer<Expression> expressionFromIndexAccessStructure(
ASTPointer<PrimaryExpression> const& _primary,
std::vector<std::pair<ASTPointer<Expression>, Location>> const& _indices);
/// If current token value is not _value, throw exception otherwise advance token. /// If current token value is not _value, throw exception otherwise advance token.
void expectToken(Token::Value _value); void expectToken(Token::Value _value);
Token::Value expectAssignmentOperator(); Token::Value expectAssignmentOperator();

64
libsolidity/Types.cpp

@ -58,7 +58,7 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
else if (Token::String0 <= _typeToken && _typeToken <= Token::String32) else if (Token::String0 <= _typeToken && _typeToken <= Token::String32)
return make_shared<StaticStringType>(int(_typeToken) - int(Token::String0)); return make_shared<StaticStringType>(int(_typeToken) - int(Token::String0));
else if (_typeToken == Token::Bytes) else if (_typeToken == Token::Bytes)
return make_shared<ByteArrayType>(ByteArrayType::Location::Storage); return make_shared<ArrayType>(ArrayType::Location::Storage);
else else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type.")); std::string(Token::toString(_typeToken)) + " to type."));
@ -83,17 +83,35 @@ TypePointer Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
return TypePointer(); return TypePointer();
} }
TypePointer Type::fromMapping(Mapping const& _typeName) TypePointer Type::fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType)
{ {
TypePointer keyType = _typeName.getKeyType().toType(); TypePointer keyType = _keyType.toType();
if (!keyType) if (!keyType)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name."));
TypePointer valueType = _typeName.getValueType().toType(); TypePointer valueType = _valueType.toType();
if (!valueType) if (!valueType)
BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name")); BOOST_THROW_EXCEPTION(_valueType.createTypeError("Invalid type name."));
return make_shared<MappingType>(keyType, valueType); return make_shared<MappingType>(keyType, valueType);
} }
TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length)
{
TypePointer baseType = _baseTypeName.toType();
if (!baseType)
BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Invalid type name."));
if (_length)
{
if (!_length->getType())
_length->checkTypeRequirements();
auto const* length = dynamic_cast<IntegerConstantType const*>(_length->getType().get());
if (!length)
BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array length."));
return make_shared<ArrayType>(ArrayType::Location::Storage, baseType, length->literalValue(nullptr));
}
else
return make_shared<ArrayType>(ArrayType::Location::Storage, baseType);
}
TypePointer Type::forLiteral(Literal const& _literal) TypePointer Type::forLiteral(Literal const& _literal)
{ {
switch (_literal.getToken()) switch (_literal.getToken())
@ -517,27 +535,27 @@ TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer(); return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
} }
bool ByteArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
{ {
return _convertTo.getCategory() == getCategory(); return _convertTo.getCategory() == getCategory();
} }
TypePointer ByteArrayType::unaryOperatorResult(Token::Value _operator) const TypePointer ArrayType::unaryOperatorResult(Token::Value _operator) const
{ {
if (_operator == Token::Delete) if (_operator == Token::Delete)
return make_shared<VoidType>(); return make_shared<VoidType>();
return TypePointer(); return TypePointer();
} }
bool ByteArrayType::operator==(Type const& _other) const bool ArrayType::operator==(Type const& _other) const
{ {
if (_other.getCategory() != getCategory()) if (_other.getCategory() != getCategory())
return false; return false;
ByteArrayType const& other = dynamic_cast<ByteArrayType const&>(_other); ArrayType const& other = dynamic_cast<ArrayType const&>(_other);
return other.m_location == m_location; return other.m_location == m_location;
} }
unsigned ByteArrayType::getSizeOnStack() const unsigned ArrayType::getSizeOnStack() const
{ {
if (m_location == Location::CallData) if (m_location == Location::CallData)
// offset, length (stack top) // offset, length (stack top)
@ -547,12 +565,30 @@ unsigned ByteArrayType::getSizeOnStack() const
return 1; return 1;
} }
shared_ptr<ByteArrayType> ByteArrayType::copyForLocation(ByteArrayType::Location _location) const string ArrayType::toString() const
{ {
return make_shared<ByteArrayType>(_location); if (isByteArray())
return "bytes";
string ret = getBaseType()->toString() + "[";
if (!isDynamicallySized())
ret += getLength().str();
return ret + "]";
}
shared_ptr<ArrayType> ArrayType::copyForLocation(ArrayType::Location _location) const
{
auto copy = make_shared<ArrayType>(_location);
copy->m_isByteArray = m_isByteArray;
if (m_baseType->getCategory() == Type::Category::Array)
copy->m_baseType = dynamic_cast<ArrayType const&>(*m_baseType).copyForLocation(_location);
else
copy->m_baseType = m_baseType;
copy->m_hasDynamicLength = m_hasDynamicLength;
copy->m_length = m_length;
return copy;
} }
const MemberList ByteArrayType::s_byteArrayMemberList = MemberList({{"length", make_shared<IntegerType>(256)}}); const MemberList ArrayType::s_arrayTypeMemberList = MemberList({{"length", make_shared<IntegerType>(256)}});
bool ContractType::operator==(Type const& _other) const bool ContractType::operator==(Type const& _other) const
{ {
@ -1040,7 +1076,7 @@ MagicType::MagicType(MagicType::Kind _kind):
m_members = MemberList({{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, m_members = MemberList({{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
{"gas", make_shared<IntegerType>(256)}, {"gas", make_shared<IntegerType>(256)},
{"value", make_shared<IntegerType>(256)}, {"value", make_shared<IntegerType>(256)},
{"data", make_shared<ByteArrayType>(ByteArrayType::Location::CallData)}}); {"data", make_shared<ArrayType>(ArrayType::Location::CallData)}});
break; break;
case Kind::Transaction: case Kind::Transaction:
m_members = MemberList({{"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, m_members = MemberList({{"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},

44
libsolidity/Types.h

@ -36,8 +36,6 @@ namespace dev
namespace solidity namespace solidity
{ {
// @todo realMxN, dynamic strings, text, arrays
class Type; // forward class Type; // forward
class FunctionType; // forward class FunctionType; // forward
using TypePointer = std::shared_ptr<Type const>; using TypePointer = std::shared_ptr<Type const>;
@ -78,7 +76,7 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this<Type
public: public:
enum class Category enum class Category
{ {
Integer, IntegerConstant, Bool, Real, ByteArray, Integer, IntegerConstant, Bool, Real, Array,
String, Contract, Struct, Function, Enum, String, Contract, Struct, Function, Enum,
Mapping, Void, TypeType, Modifier, Magic Mapping, Void, TypeType, Modifier, Magic
}; };
@ -89,8 +87,8 @@ public:
static TypePointer fromElementaryTypeName(Token::Value _typeToken); static TypePointer fromElementaryTypeName(Token::Value _typeToken);
static TypePointer fromElementaryTypeName(std::string const& _name); static TypePointer fromElementaryTypeName(std::string const& _name);
static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
static TypePointer fromMapping(Mapping const& _typeName); static TypePointer fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType);
static TypePointer fromFunction(FunctionDefinition const& _function); static TypePointer fromArrayTypeName(TypeName& _baseTypeName, Expression* _length);
/// @} /// @}
/// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does /// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does
@ -281,32 +279,50 @@ public:
}; };
/** /**
* The type of a byte array, prototype for a general array. * The type of an array. The flavours are byte array (bytes), statically- (<type>[<length>])
* and dynamically-sized array (<type>[]).
*/ */
class ByteArrayType: public Type class ArrayType: public Type
{ {
public: public:
enum class Location { Storage, CallData, Memory }; enum class Location { Storage, CallData, Memory };
virtual Category getCategory() const override { return Category::ByteArray; } virtual Category getCategory() const override { return Category::Array; }
explicit ByteArrayType(Location _location): m_location(_location) {}
/// Constructor for a byte array ("bytes")
explicit ArrayType(Location _location):
m_location(_location), m_isByteArray(true), m_baseType(std::make_shared<IntegerType>(8)) {}
/// Constructor for a dynamically sized array type ("type[]")
ArrayType(Location _location, const TypePointer &_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) {}
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual bool operator==(const Type& _other) const override; virtual bool operator==(const Type& _other) const override;
virtual bool isDynamicallySized() const { return true; } virtual bool isDynamicallySized() const { return m_hasDynamicLength; }
virtual unsigned getSizeOnStack() const override; virtual unsigned getSizeOnStack() const override;
virtual std::string toString() const override { return "bytes"; } virtual std::string toString() const override;
virtual MemberList const& getMembers() const override { return s_byteArrayMemberList; } virtual MemberList const& getMembers() const override { return s_arrayTypeMemberList; }
Location getLocation() const { return m_location; } Location getLocation() const { return m_location; }
bool isByteArray() const { return m_isByteArray; }
TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;}
u256 const& getLength() const { return m_length; }
/// @returns a copy of this type with location changed to @a _location /// @returns a copy of this type with location changed to @a _location
/// @todo this might move as far up as Type later /// @todo this might move as far up as Type later
std::shared_ptr<ByteArrayType> copyForLocation(Location _location) const; std::shared_ptr<ArrayType> copyForLocation(Location _location) const;
private: private:
Location m_location; Location m_location;
static const MemberList s_byteArrayMemberList; bool m_isByteArray = false; ///< Byte arrays ("bytes") have different semantics from ordinary arrays.
TypePointer m_baseType;
bool m_hasDynamicLength = true;
u256 m_length;
static const MemberList s_arrayTypeMemberList;
}; };
/** /**

5
libsolidity/grammar.txt

@ -18,8 +18,9 @@ ParameterList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')'
// semantic restriction: mappings and structs (recursively) containing mappings // semantic restriction: mappings and structs (recursively) containing mappings
// are not allowed in argument lists // are not allowed in argument lists
VariableDeclaration = TypeName Identifier VariableDeclaration = TypeName Identifier
TypeName = ElementaryTypeName | Identifier | Mapping TypeName = ElementaryTypeName | Identifier | Mapping | ArrayTypeName
Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')' Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')'
ArrayTypeName = TypeName '[' (Expression)? ']'
Block = '{' Statement* '}' Block = '{' Statement* '}'
Statement = IfStatement | WhileStatement | Block | Statement = IfStatement | WhileStatement | Block |
@ -42,5 +43,5 @@ Assignment = Expression (AssignmentOp Expression)
FunctionCall = Expression '(' Expression ( ',' Expression )* ')' FunctionCall = Expression '(' Expression ( ',' Expression )* ')'
NewExpression = 'new' Identifier NewExpression = 'new' Identifier
MemberAccess = Expression '.' Identifier MemberAccess = Expression '.' Identifier
IndexAccess = Expression '[' Expresison ']' IndexAccess = Expression '[' (Expresison)? ']'
PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')' PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')'

99
mix/FileIo.cpp

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file FileIo.cpp /** @file FileIo.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com * @author Arkadiy Paronyan arkadiy@ethdev.com
@ -20,13 +20,21 @@
* Ethereum IDE client. * Ethereum IDE client.
*/ */
#include <QDirIterator>
#include <QDir> #include <QDir>
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QTextStream> #include <QTextStream>
#include <QUrl> #include <QUrl>
#include <json/json.h>
#include <libdevcrypto/CryptoPP.h>
#include <libdevcrypto/Common.h>
#include <libdevcore/RLP.h>
#include <libdevcrypto/SHA3.h>
#include "FileIo.h" #include "FileIo.h"
using namespace dev;
using namespace dev::crypto;
using namespace dev::mix; using namespace dev::mix;
void FileIo::makeDir(QString const& _url) void FileIo::makeDir(QString const& _url)
@ -101,3 +109,72 @@ bool FileIo::fileExists(QString const& _url)
QFile file(url.path()); QFile file(url.path());
return file.exists(); return file.exists();
} }
QStringList FileIo::makePackage(QString const& _deploymentFolder)
{
Json::Value manifest;
Json::Value entries(Json::arrayValue);
QUrl folder(_deploymentFolder);
QString path(folder.path());
QDir deployDir = QDir(path);
dev::RLPStream rlpStr;
int k = 1;
std::vector<bytes> files;
for (auto item: deployDir.entryInfoList(QDir::Files))
{
QFile qFile(item.filePath());
if (qFile.open(QIODevice::ReadOnly))
{
k++;
QFileInfo fileInfo = QFileInfo(qFile.fileName());
Json::Value jsonValue;
std::string path = fileInfo.fileName() == "index.html" ? "/" : fileInfo.fileName().toStdString();
jsonValue["path"] = path; //TODO: Manage relative sub folder
jsonValue["file"] = "/" + fileInfo.fileName().toStdString();
jsonValue["contentType"] = "text/html"; //TODO: manage multiple content type
QByteArray a = qFile.readAll();
bytes data = bytes(a.begin(), a.end());
files.push_back(data);
jsonValue["hash"] = toHex(dev::sha3(data).ref());
entries.append(jsonValue);
}
qFile.close();
}
rlpStr.appendList(k);
std::stringstream jsonStr;
jsonStr << manifest;
QByteArray b = QString::fromStdString(jsonStr.str()).toUtf8();
rlpStr.append(bytesConstRef((const unsigned char*)b.data(), b.size()));
for (unsigned int k = 0; k < files.size(); k++)
rlpStr.append(files.at(k));
manifest["entries"] = entries;
bytes dapp = rlpStr.out();
dev::h256 dappHash = dev::sha3(dapp);
//encrypt
KeyPair key(dappHash);
Secp256k1 enc;
enc.encrypt(key.pub(), dapp);
QUrl url(_deploymentFolder + "package.dapp");
QFile compressed(url.path());
QByteArray qFileBytes((char*)dapp.data(), dapp.size());
if (compressed.open(QIODevice::WriteOnly))
{
compressed.write(qFileBytes);
compressed.flush();
}
else
error(tr("Error creating package.dapp"));
compressed.close();
QStringList ret;
ret.append(QString::fromStdString(toHex(dappHash.ref())));
ret.append(qFileBytes.toBase64());
return ret;
}

25
mix/FileIo.h

@ -1,18 +1,18 @@
/* /*
This file is part of cpp-ethereum. This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful, cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file FileIo.h /** @file FileIo.h
* @author Arkadiy Paronyan arkadiy@ethdev.com * @author Arkadiy Paronyan arkadiy@ethdev.com
@ -22,6 +22,7 @@
#pragma once #pragma once
#include <libdevcore/CommonData.h>
#include <QObject> #include <QObject>
namespace dev namespace dev
@ -52,6 +53,8 @@ public:
Q_INVOKABLE void moveFile(QString const& _sourceUrl, QString const& _destUrl); Q_INVOKABLE void moveFile(QString const& _sourceUrl, QString const& _destUrl);
/// Check if file exists /// Check if file exists
Q_INVOKABLE bool fileExists(QString const& _url); Q_INVOKABLE bool fileExists(QString const& _url);
/// Compress a folder, @returns sha3 of the compressed file.
Q_INVOKABLE QStringList makePackage(QString const& _deploymentFolder);
private: private:
QString getHomePath() const; QString getHomePath() const;

2
mix/QVariableDefinition.h

@ -53,6 +53,8 @@ public:
virtual bytes encodeValue() = 0; virtual bytes encodeValue() = 0;
/// Decode the return value @a _rawValue. /// Decode the return value @a _rawValue.
virtual void decodeValue(dev::bytes const& _rawValue) = 0; virtual void decodeValue(dev::bytes const& _rawValue) = 0;
/// returns String representation of the encoded value.
Q_INVOKABLE QString encodeValueAsString() { return QString::fromStdString(dev::toHex(encodeValue())); }
protected: protected:
QString m_value; QString m_value;

232
mix/qml/DeploymentDialog.qml

@ -0,0 +1,232 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.0
import QtQuick.Dialogs 1.1
import QtQuick.Controls.Styles 1.3
import org.ethereum.qml.QEther 1.0
import "js/TransactionHelper.js" as TransactionHelper
import "js/ProjectModel.js" as ProjectModelCode
import "js/QEtherHelper.js" as QEtherHelper
import "."
Window {
id: modalDeploymentDialog
modality: Qt.ApplicationModal
width: 600
height: 350
visible: false
property alias applicationUrlEth: applicationUrlEth.text
property alias applicationUrlHttp: applicationUrlHttp.text
property string urlHintContract: "c83d3e22645fb015d02043a744921cc2f828c64d"
property string packageHash
property alias packageBase64: base64Value.text
property string eth: "afb7cdbd076674fd2c67f8a66518e3145b184ae4";
property string wallet: "c83d3e22645fb015d02043a744921cc2f828c64d";
color: Style.generic.layout.backgroundColor
function close()
{
visible = false;
}
function open()
{
modalDeploymentDialog.setX((Screen.width - width) / 2);
modalDeploymentDialog.setY((Screen.height - height) / 2);
visible = true;
}
function pad(h)
{
// TODO move this to QHashType class
while (h.length < 64)
{
h = '0' + h;
}
return h;
}
Rectangle
{
anchors.fill : parent
anchors.margins: 10
color: Style.generic.layout.backgroundColor
GridLayout
{
columns: 2
anchors.top: parent.top
anchors.left: parent.left
width: parent.width
DefaultLabel
{
text: qsTr("Ethereum Application URL: ")
}
DefaultTextField
{
Layout.fillWidth: true
id: applicationUrlEth
}
DefaultLabel
{
text: qsTr("Web Application Ressources URL: ")
}
DefaultTextField
{
Layout.fillWidth: true
id: applicationUrlHttp
}
DefaultLabel
{
text: qsTr("Package (Base64): ")
}
TextArea
{
Layout.fillWidth: true
readOnly: true
id: base64Value
height: 60
enabled: base64Value.text != ""
}
}
MessageDialog {
id: deployDialog
standardButtons: StandardButton.Ok
icon: StandardIcon.Warning
}
RowLayout
{
anchors.bottom: parent.bottom
anchors.right: parent.right;
anchors.bottomMargin: 10
Button {
text: qsTr("Deploy to Ethereum");
tooltip: qsTr("Deploy contract and package resources files.")
onClicked: {
deployWarningDialog.open();
}
}
Button {
text: qsTr("Register Web Application");
tooltip: qsTr("Register hosted Web Application.")
onClicked: {
if (applicationUrlHttp.text === "" || deploymentDialog.packageHash === "")
{
deployDialog.title = text;
deployDialog.text = qsTr("Please provide the link where the resources are stored and ensure the package is aleary built using the deployment step. ")
deployDialog.open();
}
else
ProjectModelCode.registerToUrlHint();
}
}
Button {
text: qsTr("Close");
onClicked: close();
}
Button {
text: qsTr("Check Ownership");
visible : false
onClicked: {
var requests = [];
var ethStr = QEtherHelper.createString("wallet");
var ethHash = QEtherHelper.createHash(eth);
requests.push({ //owner
jsonrpc: "2.0",
method: "eth_call",
params: [ { "to": '0x' + modalDeploymentDialog.eth, "data": "0xec7b9200" + ethStr.encodeValueAsString() } ],
id: 3
});
requests.push({ //register
jsonrpc: "2.0",
method: "eth_call",
params: [ { "to": '0x' + modalDeploymentDialog.eth, "data": "0x6be16bed" + ethStr.encodeValueAsString() } ],
id: 4
});
var jsonRpcUrl = "http://localhost:8080";
var rpcRequest = JSON.stringify(requests);
var httpRequest = new XMLHttpRequest();
httpRequest.open("POST", jsonRpcUrl, true);
httpRequest.setRequestHeader("Content-type", "application/json");
httpRequest.setRequestHeader("Content-length", rpcRequest.length);
httpRequest.setRequestHeader("Connection", "close");
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
console.log(httpRequest.responseText);
} else {
var errorText = qsTr("path registration failed ") + httpRequest.status;
console.log(errorText);
}
}
}
httpRequest.send(rpcRequest);
}
}
Button {
text: qsTr("Generate registrar init");
visible: false
onClicked: {
console.log("registering eth/wallet")
var jsonRpcRequestId = 0;
var requests = [];
var walletStr = QEtherHelper.createString("wallet");
requests.push({ //reserve
jsonrpc: "2.0",
method: "eth_transact",
params: [ { "to": '0x' + modalDeploymentDialog.eth, "data": "0x1c83171b" + walletStr.encodeValueAsString() } ],
id: jsonRpcRequestId++
});
requests.push({ //setRegister
jsonrpc: "2.0",
method: "eth_transact",
params: [ { "to": '0x' + modalDeploymentDialog.eth, "data": "0x96077307" + walletStr.encodeValueAsString() + pad(wallet) } ],
id: jsonRpcRequestId++
});
var jsonRpcUrl = "http://localhost:8080";
var rpcRequest = JSON.stringify(requests);
var httpRequest = new XMLHttpRequest();
httpRequest.open("POST", jsonRpcUrl, true);
httpRequest.setRequestHeader("Content-type", "application/json");
httpRequest.setRequestHeader("Content-length", rpcRequest.length);
httpRequest.setRequestHeader("Connection", "close");
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
console.log(httpRequest.responseText);
} else {
var errorText = qsTr("path registration failed ") + httpRequest.status;
console.log(errorText);
}
}
}
httpRequest.send(rpcRequest);
}
}
}
}
}

39
mix/qml/ProjectModel.qml

@ -21,6 +21,7 @@ Item {
signal newProject(var projectData) signal newProject(var projectData)
signal documentSaved(var documentId) signal documentSaved(var documentId)
signal deploymentStarted() signal deploymentStarted()
signal deploymentStepChanged(string message)
signal deploymentComplete() signal deploymentComplete()
signal deploymentError(string error) signal deploymentError(string error)
@ -31,7 +32,7 @@ Item {
property string projectPath: "" property string projectPath: ""
property string projectTitle: "" property string projectTitle: ""
property string currentDocumentId: "" property string currentDocumentId: ""
property string deploymentAddress: "" property var deploymentAddresses: []
property var listModel: projectListModel property var listModel: projectListModel
property var stateListModel: projectStateListModel.model property var stateListModel: projectStateListModel.model
property CodeEditorView codeEditor: null property CodeEditorView codeEditor: null
@ -55,6 +56,7 @@ Item {
function getDocumentIndex(documentId) { return ProjectModelCode.getDocumentIndex(documentId); } function getDocumentIndex(documentId) { return ProjectModelCode.getDocumentIndex(documentId); }
function addExistingFiles(paths) { ProjectModelCode.doAddExistingFiles(paths); } function addExistingFiles(paths) { ProjectModelCode.doAddExistingFiles(paths); }
function deployProject() { ProjectModelCode.deployProject(false); } function deployProject() { ProjectModelCode.deployProject(false); }
function registerToUrlHint() { ProjectModelCode.registerToUrlHint(); }
Connections { Connections {
target: appContext target: appContext
@ -91,15 +93,44 @@ Item {
MessageDialog { MessageDialog {
id: deployWarningDialog id: deployWarningDialog
property bool redeploy
title: qsTr("Project") title: qsTr("Project")
text: qsTr("This project has been already deployed to the network. Do you want to re-deploy it?") text:
standardButtons: StandardButton.Ok | StandardButton.Cancel {
if (Object.keys(projectModel.deploymentAddresses).length > 0)
{
redeploy = true
standardButtons = StandardButton.Ok | StandardButton.Reset | StandardButton.Abort;
return qsTr("This project has been already deployed to the network. Do you want to repackage the resources only, or also reset the deployed contract to its initial state?")
}
else
{
redeploy = false;
standardButtons = StandardButton.Ok | StandardButton.Abort;
return qsTr("This action will deploy to the network. Do you want to deploy it?")
}
}
icon: StandardIcon.Question icon: StandardIcon.Question
onAccepted: { onAccepted: {
ProjectModelCode.deployProject(true); ProjectModelCode.startDeployProject(!redeploy);
}
onReset: {
ProjectModelCode.startDeployProject(true);
} }
} }
MessageDialog {
id: deployRessourcesDialog
title: qsTr("Project")
standardButtons: StandardButton.Ok
icon: StandardIcon.Info
}
DeploymentDialog
{
id: deploymentDialog
}
ListModel { ListModel {
id: projectListModel id: projectListModel
} }

1
mix/qml/StatusPane.qml

@ -47,6 +47,7 @@ Rectangle {
onDeploymentStarted: infoMessage(qsTr("Running deployment...")); onDeploymentStarted: infoMessage(qsTr("Running deployment..."));
onDeploymentError: infoMessage(error); onDeploymentError: infoMessage(error);
onDeploymentComplete: infoMessage(qsTr("Deployment complete")); onDeploymentComplete: infoMessage(qsTr("Deployment complete"));
onDeploymentStepChanged: infoMessage(message);
} }
Connections { Connections {
target: codeModel target: codeModel

1
mix/qml/Style.qml

@ -11,6 +11,7 @@ QtObject {
property QtObject generic: QtObject { property QtObject generic: QtObject {
property QtObject layout: QtObject { property QtObject layout: QtObject {
property string separatorColor: "#808080" property string separatorColor: "#808080"
property string backgroundColor: "#ededed"
} }
property QtObject size: QtObject { property QtObject size: QtObject {
property string titlePointSize: absoluteSize(0) property string titlePointSize: absoluteSize(0)

2
mix/qml/TransactionDialog.qml

@ -405,7 +405,7 @@ Window {
return boolViewComp; return boolViewComp;
else if (type.indexOf("string") !== -1) else if (type.indexOf("string") !== -1)
return stringViewComp; return stringViewComp;
else if (type.indexOf("hash") !== -1) else if (type.indexOf("hash") !== -1 || type.indexOf("address") !== -1)
return hashViewComp; return hashViewComp;
else else
return null; return null;

244
mix/qml/js/ProjectModel.js

@ -19,6 +19,7 @@
* @date 2015 * @date 2015
* Ethereum IDE client. * Ethereum IDE client.
*/ */
Qt.include("QEtherHelper.js")
var htmlTemplate = "<html>\n<head>\n<script>\n</script>\n</head>\n<body>\n<script>\n</script>\n</body>\n</html>"; var htmlTemplate = "<html>\n<head>\n<script>\n</script>\n</head>\n<body>\n<script>\n</script>\n</body>\n</html>";
var contractTemplate = "contract Contract {\n}\n"; var contractTemplate = "contract Contract {\n}\n";
@ -45,7 +46,11 @@ function saveProject() {
var projectData = { var projectData = {
files: [], files: [],
title: projectTitle, title: projectTitle,
deploymentAddress: deploymentAddress deploymentAddresses: deploymentAddresses,
applicationUrlEth: deploymentDialog.applicationUrlEth,
applicationUrlHttp: deploymentDialog.applicationUrlHttp,
packageHash: deploymentDialog.packageHash,
packageBase64: deploymentDialog.packageBase64
}; };
for (var i = 0; i < projectListModel.count; i++) for (var i = 0; i < projectListModel.count; i++)
projectData.files.push(projectListModel.get(i).fileName) projectData.files.push(projectListModel.get(i).fileName)
@ -63,11 +68,19 @@ function loadProject(path) {
var projectFile = path + projectFileName; var projectFile = path + projectFileName;
var json = fileIo.readFile(projectFile); var json = fileIo.readFile(projectFile);
var projectData = JSON.parse(json); var projectData = JSON.parse(json);
if (projectData.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) { if (!projectData.title) {
var parts = path.split("/"); var parts = path.split("/");
projectData.title = parts[parts.length - 2]; projectData.title = parts[parts.length - 2];
} }
deploymentAddress = projectData.deploymentAddress ? projectData.deploymentAddress : ""; deploymentAddresses = projectData.deploymentAddresses ? projectData.deploymentAddresses : [];
projectTitle = projectData.title; projectTitle = projectData.title;
projectPath = path; projectPath = path;
if (!projectData.files) if (!projectData.files)
@ -272,22 +285,27 @@ function generateFileName(name, extension) {
var jsonRpcRequestId = 1; var jsonRpcRequestId = 1;
function deployProject(force) { function deployProject(force) {
saveAll(); //TODO: ask user saveAll(); //TODO: ask user
deploymentDialog.open();
}
if (!force && deploymentAddress !== "") { function startDeployProject(erasePrevious)
deployWarningDialog.visible = true; {
var date = new Date();
var deploymentId = date.toLocaleString(Qt.locale(), "ddMMyyHHmmsszzz");
if (!erasePrevious)
{
finalizeDeployment(deploymentId, projectModel.deploymentAddresses);
return; return;
} }
var date = new Date(); var jsonRpcUrl = "http://127.0.0.1:8080";
var deploymentId = date.toLocaleString(Qt.locale(), "ddMMyyHHmmsszzz");
var jsonRpcUrl = "http://127.0.0.1:8080";
console.log("Deploying " + deploymentId + " to " + jsonRpcUrl); console.log("Deploying " + deploymentId + " to " + jsonRpcUrl);
deploymentStarted(); deploymentStarted();
var requests = []; var requests = [];
var requestNames = []; var requestNames = [];
for (var c in codeModel.contracts) { //TODO: order based on dependencies for (var c in codeModel.contracts) { //TODO: order based on dependencies
var code = codeModel.contracts[c].codeHex; var code = codeModel.contracts[c].codeHex;
requests.push({ requests.push({
@ -299,8 +317,8 @@ function deployProject(force) {
requestNames.push(c); requestNames.push(c);
} }
var rpcRequest = JSON.stringify(requests);; var rpcRequest = JSON.stringify(requests);
var httpRequest = new XMLHttpRequest() var httpRequest = new XMLHttpRequest();
httpRequest.open("POST", jsonRpcUrl, true); httpRequest.open("POST", jsonRpcUrl, true);
httpRequest.setRequestHeader("Content-type", "application/json"); httpRequest.setRequestHeader("Content-type", "application/json");
httpRequest.setRequestHeader("Content-length", rpcRequest.length); httpRequest.setRequestHeader("Content-length", rpcRequest.length);
@ -311,8 +329,8 @@ function deployProject(force) {
var rpcResponse = JSON.parse(httpRequest.responseText); var rpcResponse = JSON.parse(httpRequest.responseText);
if (rpcResponse.length === requestNames.length) { if (rpcResponse.length === requestNames.length) {
var contractAddresses = {}; var contractAddresses = {};
for (var r = 0; r < rpcResponse.lenght; r++) for (var r = 0; r < rpcResponse.length; r++)
contractAddresses[requestNames[r]] = rpcResponse.result; contractAddresses[requestNames[r]] = rpcResponse[r].result;
finalizeDeployment(deploymentId, contractAddresses); finalizeDeployment(deploymentId, contractAddresses);
} }
} else { } else {
@ -326,7 +344,7 @@ function deployProject(force) {
} }
function finalizeDeployment(deploymentId, addresses) { function finalizeDeployment(deploymentId, addresses) {
//create a dir for frontend files and copy them deploymentStepChanged(qsTr("Packaging application ..."));
var deploymentDir = projectPath + deploymentId + "/"; var deploymentDir = projectPath + deploymentId + "/";
fileIo.makeDir(deploymentDir); fileIo.makeDir(deploymentDir);
for (var i = 0; i < projectListModel.count; i++) { for (var i = 0; i < projectListModel.count; i++) {
@ -369,7 +387,203 @@ function finalizeDeployment(deploymentId, addresses) {
//copy scripts //copy scripts
fileIo.copyFile("qrc:///js/bignumber.min.js", deploymentDir + "bignumber.min.js"); fileIo.copyFile("qrc:///js/bignumber.min.js", deploymentDir + "bignumber.min.js");
fileIo.copyFile("qrc:///js/webthree.js", deploymentDir + "ethereum.js"); fileIo.copyFile("qrc:///js/webthree.js", deploymentDir + "ethereum.js");
deploymentAddress = address; deploymentAddresses = addresses;
saveProject(); saveProject();
deploymentComplete();
var packageRet = fileIo.makePackage(deploymentDir);
deploymentDialog.packageHash = packageRet[0];
deploymentDialog.packageBase64 = packageRet[1];
var applicationUrlEth = deploymentDialog.applicationUrlEth;
applicationUrlEth = formatAppUrl(applicationUrlEth);
deploymentStepChanged(qsTr("Registering application on the Ethereum network ..."));
checkRegistration(applicationUrlEth, deploymentDialog.eth, function () {
deploymentComplete();
deployRessourcesDialog.text = qsTr("Register Web Application to finalize deployment.");
deployRessourcesDialog.open();
});
}
function rpcCall(requests, callBack)
{
var jsonRpcUrl = "http://localhost:8080";
var rpcRequest = JSON.stringify(requests);
var httpRequest = new XMLHttpRequest();
httpRequest.open("POST", jsonRpcUrl, true);
httpRequest.setRequestHeader("Content-type", "application/json");
httpRequest.setRequestHeader("Content-length", rpcRequest.length);
httpRequest.setRequestHeader("Connection", "close");
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status !== 200)
{
var errorText = qsTr("Deployment error: Error while registering Dapp ") + httpRequest.status;
console.log(errorText);
deploymentError(errorText);
return;
}
callBack(httpRequest.status, httpRequest.responseText)
}
}
httpRequest.send(rpcRequest);
}
function checkRegistration(dappUrl, addr, callBack)
{
var requests = [];
var data = "";
if (dappUrl.length > 0)
{
//checking path (register).
var str = createString(dappUrl[0]);
data = "0x6be16bed" + str.encodeValueAsString();
console.log("checking if path exists (register) => " + JSON.stringify(dappUrl));
requests.push({
jsonrpc: "2.0",
method: "eth_call",
params: [ { "to": '0x' + addr, "data": data } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
var address = JSON.parse(response)[0].result.replace('0x', '');
if (address === "")
{
var errorTxt = qsTr("Path does not exists " + JSON.stringify(dappUrl) + " cannot continue");
deploymentError(errorTxt);
console.log(errorTxt);
return;
}
dappUrl.splice(0, 1);
checkRegistration(dappUrl, address, callBack);
});
}
else
{
var paramTitle = createString(projectModel.projectTitle);
requests.push({
//owner()
jsonrpc: "2.0",
method: "eth_call",
params: [ { "to": '0x' + addr, "data": "0xec7b9200" + paramTitle.encodeValueAsString() } ],
id: jsonRpcRequestId++
});
requests.push({
//accounts
jsonrpc: "2.0",
method: "eth_accounts",
params: null,
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
requests = [];
var res = JSON.parse(response);
var currentOwner = res[0].result;
var noOwner = currentOwner.replace('0x', '').replace(/0/g, '') === '';
if (noOwner)
{
requests.push({
//reserve()
jsonrpc: "2.0",
method: "eth_transact",
params: [ { "to": '0x' + addr, "data": "0x1c83171b" + paramTitle.encodeValueAsString() } ],
id: jsonRpcRequestId++
});
}
else
{
var bOwner = false;
currentOwner = normalizeAddress(currentOwner);
for (var u in res[1].result)
{
if (normalizeAddress(res[1].result[u]) === currentOwner)
bOwner = true;
}
if (!bOwner)
{
var errorTxt = qsTr("Current user is not the owner of this path. Cannot continue")
deploymentError(errorTxt);
console.log(errorTxt);
return;
}
}
console.log("setContentHash");
requests.push({
//setContent()
jsonrpc: "2.0",
method: "eth_transact",
params: [ { "to": '0x' + addr, "data": "0x5d574e32" + paramTitle.encodeValueAsString() + deploymentDialog.packageHash } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
callBack();
});
});
}
} }
function registerToUrlHint()
{
deploymentStepChanged(qsTr("Registering application Resources (" + deploymentDialog.applicationUrlHttp) + ") ...");
var requests = [];
var paramUrlHttp = createString(deploymentDialog.applicationUrlHttp);
requests.push({
//urlHint => suggestUrl
jsonrpc: "2.0",
method: "eth_transact",
params: [ { "to": '0x' + deploymentDialog.urlHintContract, "data": "0x4983e19c" + deploymentDialog.packageHash + paramUrlHttp.encodeValueAsString() } ],
id: jsonRpcRequestId++
});
rpcCall(requests, function (httpRequest, response) {
deploymentComplete();
});
}
function normalizeAddress(addr)
{
addr = addr.replace('0x', '');
var i = 0;
for (var k in addr)
{
if (addr[k] !== "0")
break;
else
i++;
}
return addr.substring(i);
}
function formatAppUrl(url)
{
var slash = url.indexOf("/");
var dot = url.indexOf(".");
if (slash === -1 && dot === -1)
return url;
if ((slash !== -1 && slash < dot) || dot === -1)
return url.split("/");
else
{
var dotted;
var ret = [];
if (slash !== -1)
{
ret.push(url.split("/"));
dotted = ret[0].split(".");
}
else
dotted = url.split(".");
for (var k in dotted)
ret.unshift(dotted[k]);
return ret;
}
}

16
mix/qml/js/QEtherHelper.js

@ -15,3 +15,19 @@ function createBigInt(_value)
return bigint; return bigint;
} }
function createString(_value)
{
var stringComponent = Qt.createComponent("qrc:/qml/QStringType.qml");
var stringC = stringComponent.createObject();
stringC.setValue(_value);
return stringC;
}
function createHash(_value)
{
var hComponent = Qt.createComponent("qrc:/qml/QHashType.qml");
var hC = hComponent.createObject();
hC.setValue(_value);
return hC;
}

1
mix/res.qrc

@ -101,5 +101,6 @@
<file>qml/Style.qml</file> <file>qml/Style.qml</file>
<file>qml/WebPreviewStyle.qml</file> <file>qml/WebPreviewStyle.qml</file>
<file>qml/img/available_updates.png</file> <file>qml/img/available_updates.png</file>
<file>qml/DeploymentDialog.qml</file>
</qresource> </qresource>
</RCC> </RCC>

4
test/CMakeLists.txt

@ -4,6 +4,10 @@ aux_source_directory(. SRC_LIST)
list(REMOVE_ITEM SRC_LIST "./createRandomTest.cpp") list(REMOVE_ITEM SRC_LIST "./createRandomTest.cpp")
list(REMOVE_ITEM SRC_LIST "./checkRandomTest.cpp") list(REMOVE_ITEM SRC_LIST "./checkRandomTest.cpp")
if (NOT JSONRPC)
list(REMOVE_ITEM SRC_LIST "./AccountHolder.cpp")
endif()
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS}) include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(BEFORE ..) include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})

9
test/SolidityNameAndTypeResolution.cpp

@ -1176,6 +1176,15 @@ BOOST_AUTO_TEST_CASE(test_for_bug_override_function_with_bytearray_type)
BOOST_CHECK_NO_THROW(parseTextAndResolveNamesWithChecks(sourceCode)); BOOST_CHECK_NO_THROW(parseTextAndResolveNamesWithChecks(sourceCode));
} }
BOOST_AUTO_TEST_CASE(array_with_nonconstant_length)
{
char const* text = R"(
contract c {
function f(uint a) { uint8[a] x; }
})";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

51
test/SolidityParser.cpp

@ -480,6 +480,18 @@ BOOST_AUTO_TEST_CASE(statement_starting_with_type_conversion)
char const* text = "contract test {\n" char const* text = "contract test {\n"
" function fun() {\n" " function fun() {\n"
" uint64(2);\n" " uint64(2);\n"
" uint64[7](3);\n"
" uint64[](3);\n"
" }\n"
"}\n";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(type_conversion_to_dynamic_array)
{
char const* text = "contract test {\n"
" function fun() {\n"
" var x = uint64[](3);\n"
" }\n" " }\n"
"}\n"; "}\n";
BOOST_CHECK_NO_THROW(parseText(text)); BOOST_CHECK_NO_THROW(parseText(text));
@ -753,6 +765,45 @@ BOOST_AUTO_TEST_CASE(external_variable)
BOOST_CHECK_THROW(parseText(text), ParserError); BOOST_CHECK_THROW(parseText(text), ParserError);
} }
BOOST_AUTO_TEST_CASE(arrays_in_storage)
{
char const* text = R"(
contract c {
uint[10] a;
uint[] a2;
struct x { uint[2**20] b; y[0] c; }
struct y { uint d; mapping(uint=>x)[] e; }
})";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(arrays_in_events)
{
char const* text = R"(
contract c {
event e(uint[10] a, string7[8] indexed b, c[3] x);
})";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(arrays_in_expressions)
{
char const* text = R"(
contract c {
function f() { c[10] a = 7; uint8[10 * 2] x; }
})";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(multi_arrays)
{
char const* text = R"(
contract c {
mapping(uint => mapping(uint => int8)[8][][9])[] x;
})";
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

50
test/stSolidityTestFiller.json

@ -264,7 +264,7 @@
}, },
"pre" : "pre" :
{ {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "100000", "balance" : "100000",
"//": "contract subcaller ", "//": "contract subcaller ",
"//": "{ ", "//": "{ ",
@ -294,6 +294,12 @@
"nonce" : "0", "nonce" : "0",
"storage" : { "storage" : {
} }
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "100000",
"nonce" : "0",
"code" : "",
"storage": {}
} }
}, },
"transaction" : "transaction" :
@ -304,7 +310,7 @@
"gasPrice" : "1", "gasPrice" : "1",
"nonce" : "0", "nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "a94f5374fce5edbc8e2a8697c15331677e6ebf0b", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "1" "value" : "1"
} }
}, },
@ -320,7 +326,7 @@
}, },
"pre" : "pre" :
{ {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "100000", "balance" : "100000",
"//" : "contract recursiveMethods ", "//" : "contract recursiveMethods ",
"//" : "{ ", "//" : "{ ",
@ -343,6 +349,12 @@
"nonce" : "0", "nonce" : "0",
"storage" : { "storage" : {
} }
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "100000",
"nonce" : "0",
"code" : "",
"storage": {}
} }
}, },
"transaction" : "transaction" :
@ -353,7 +365,7 @@
"gasPrice" : "1", "gasPrice" : "1",
"nonce" : "0", "nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "a94f5374fce5edbc8e2a8697c15331677e6ebf0b", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "1" "value" : "1"
} }
}, },
@ -369,7 +381,7 @@
}, },
"pre" : "pre" :
{ {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "100000", "balance" : "100000",
"//" : "contract recursiveMethods ", "//" : "contract recursiveMethods ",
"//" : "{ ", "//" : "{ ",
@ -392,6 +404,12 @@
"nonce" : "0", "nonce" : "0",
"storage" : { "storage" : {
} }
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "500",
"nonce" : "0",
"code" : "",
"storage": {}
} }
}, },
"transaction" : "transaction" :
@ -402,7 +420,7 @@
"gasPrice" : "1", "gasPrice" : "1",
"nonce" : "0", "nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "a94f5374fce5edbc8e2a8697c15331677e6ebf0b", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "1" "value" : "1"
} }
}, },
@ -418,7 +436,7 @@
}, },
"pre" : "pre" :
{ {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "1000000", "balance" : "1000000",
"//" : "contract recursiveCreate1 ", "//" : "contract recursiveCreate1 ",
"//" : "{ ", "//" : "{ ",
@ -461,6 +479,12 @@
"nonce" : "0", "nonce" : "0",
"storage" : { "storage" : {
} }
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "500000",
"nonce" : "0",
"code" : "",
"storage": {}
} }
}, },
"transaction" : "transaction" :
@ -471,7 +495,7 @@
"gasPrice" : "1", "gasPrice" : "1",
"nonce" : "0", "nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "a94f5374fce5edbc8e2a8697c15331677e6ebf0b", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "1" "value" : "1"
} }
}, },
@ -487,12 +511,18 @@
}, },
"pre" : "pre" :
{ {
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "100000", "balance" : "100000",
"code" : "0x60003560e060020a90048063c040622614601557005b601b6021565b60006000f35b61014f60008190555056", "code" : "0x60003560e060020a90048063c040622614601557005b601b6021565b60006000f35b61014f60008190555056",
"nonce" : "0", "nonce" : "0",
"storage" : { "storage" : {
} }
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "500",
"nonce" : "0",
"code" : "",
"storage": {}
} }
}, },
"transaction" : "transaction" :
@ -503,7 +533,7 @@
"gasPrice" : "1", "gasPrice" : "1",
"nonce" : "0", "nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "a94f5374fce5edbc8e2a8697c15331677e6ebf0b", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "1" "value" : "1"
} }
} }

72
test/stTransactionTestFiller.json

@ -842,78 +842,6 @@
} }
}, },
"TransactionMakeAccountBalanceOverflow" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "45678256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" :
{
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "100000",
"code" : "",
"nonce" : "0",
"storage" : {
}
},
"b94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"code" : "",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" :
{
"data" : "",
"gasLimit" : "1000",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"value" : "100"
}
},
"TransactionMakeAccountNonceOverflow" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "1",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"pre" :
{
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "100000",
"code" : "",
"nonce" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"nonce" : "10000",
"storage" : {
}
}
},
"transaction" :
{
"data" : "",
"gasLimit" : "1000",
"gasPrice" : "1",
"nonce" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"nonce" : "10000",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"value" : "100"
}
},
"UserTransactionZeroCost" : { "UserTransactionZeroCost" : {
"env" : { "env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",

15
test/ttTransactionTestFiller.json

@ -294,7 +294,7 @@
"gasPrice" : "1", "gasPrice" : "1",
"nonce" : "0xffdc5", "nonce" : "0xffdc5",
"to" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", "to" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"value" : "0xfffdc12c", "value" : "4294820140",
"v" : "28", "v" : "28",
"r" : "0x98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a", "r" : "0x98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a",
"s" : "0x8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3" "s" : "0x8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3"
@ -314,5 +314,18 @@
"r" : "0x98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a", "r" : "0x98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a",
"s" : "0x8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3" "s" : "0x8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3"
} }
},
"unpadedRValue": {
"transaction": {
"nonce": "13",
"gasPrice": "0x09184e72a000",
"gasLimit": "0x2710",
"to": "7c47ef93268a311f4cad0c750724299e9b72c268",
"data": "0x379607f50000000000000000000000000000000000000000000000000000000000000005",
"r": "0x006ab6dda9f4df56ea45583af36660329147f1753f3724ea5eb9ed83e812ca77",
"s": "0x495701e230667832c8999e884e366a61028633ecf951e8cd66d119f381ae5718",
"v": "28",
"value": ""
}
} }
} }

Loading…
Cancel
Save