Browse Source

Merge branch 'develop'

cl-refactor
Gav Wood 10 years ago
parent
commit
12c5af13a3
  1. 2
      alethzero/CMakeLists.txt
  2. 32
      cmake/EthExecutableHelper.cmake
  3. 10
      cmake/FindMHD.cmake
  4. 4
      eth/CMakeLists.txt
  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. 2
      extdep/CMakeLists.txt
  9. 4
      libdevcore/RLP.h
  10. 2
      libethereum/Transaction.cpp
  11. 71
      libsolidity/AST.cpp
  12. 64
      libsolidity/AST.h
  13. 3
      libsolidity/ASTForward.h
  14. 4
      libsolidity/ASTJsonConverter.cpp
  15. 4
      libsolidity/ASTJsonConverter.h
  16. 18
      libsolidity/ASTPrinter.cpp
  17. 6
      libsolidity/ASTPrinter.h
  18. 12
      libsolidity/ASTVisitor.h
  19. 48
      libsolidity/AST_accept.h
  20. 51
      libsolidity/Compiler.cpp
  21. 3
      libsolidity/Compiler.h
  22. 2
      libsolidity/CompilerContext.h
  23. 63
      libsolidity/CompilerStack.cpp
  24. 13
      libsolidity/CompilerStack.h
  25. 32
      libsolidity/CompilerUtils.cpp
  26. 4
      libsolidity/CompilerUtils.h
  27. 59
      libsolidity/ExpressionCompiler.cpp
  28. 15
      libsolidity/ExpressionCompiler.h
  29. 12
      libsolidity/NameAndTypeResolver.cpp
  30. 2
      libsolidity/NameAndTypeResolver.h
  31. 235
      libsolidity/Parser.cpp
  32. 45
      libsolidity/Parser.h
  33. 4
      libsolidity/Token.h
  34. 77
      libsolidity/Types.cpp
  35. 44
      libsolidity/Types.h
  36. 5
      libsolidity/grammar.txt
  37. 99
      mix/FileIo.cpp
  38. 25
      mix/FileIo.h
  39. 2
      mix/QVariableDefinition.h
  40. 232
      mix/qml/DeploymentDialog.qml
  41. 39
      mix/qml/ProjectModel.qml
  42. 1
      mix/qml/StatusPane.qml
  43. 1
      mix/qml/Style.qml
  44. 2
      mix/qml/TransactionDialog.qml
  45. 244
      mix/qml/js/ProjectModel.js
  46. 16
      mix/qml/js/QEtherHelper.js
  47. 1
      mix/res.qrc
  48. 4
      test/CMakeLists.txt
  49. 56
      test/SolidityEndToEndTest.cpp
  50. 21
      test/SolidityNameAndTypeResolution.cpp
  51. 57
      test/SolidityParser.cpp
  52. 4
      test/TestHelper.cpp
  53. 50
      test/stSolidityTestFiller.json
  54. 72
      test/stTransactionTestFiller.json
  55. 7
      test/transaction.cpp
  56. 15
      test/ttTransactionTestFiller.json
  57. 2
      third/CMakeLists.txt

2
alethzero/CMakeLists.txt

@ -58,5 +58,5 @@ if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC"))
endif()
# eth_install_executable is defined in cmake/EthExecutableHelper.cmake
eth_install_executable(${EXECUTABLE})
eth_install_executable(${EXECUTABLE} DLLS ${MHD_DLL_RELEASE})

32
cmake/EthExecutableHelper.cmake

@ -56,7 +56,7 @@ macro(eth_install_executable EXECUTABLE)
set (extra_macro_args ${ARGN})
set (options)
set (one_value_args QMLDIR)
set (multi_value_args)
set (multi_value_args DLLS)
cmake_parse_arguments (ETH_INSTALL_EXECUTABLE "${options}" "${one_value_args}" "${multi_value_args}" "${extra_macro_args}")
if (ETH_INSTALL_EXECUTABLE_QMLDIR)
@ -90,15 +90,29 @@ macro(eth_install_executable EXECUTABLE)
" COMPONENT RUNTIME )
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND cmd /C "set PATH=${Qt5Core_DIR}/../../../bin;%PATH% && ${WINDEPLOYQT_APP} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.exe ${eth_qml_dir}"
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
)
#workaround for https://bugreports.qt.io/browse/QTBUG-42083
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND cmd /C "(echo [Paths] & echo.Prefix=.)" > "qt.conf"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} VERBATIM
get_target_property(TARGET_LIBS ${EXECUTABLE} INTERFACE_LINK_LIBRARIES)
string(REGEX MATCH "Qt5::Core" HAVE_QT ${TARGET_LIBS})
if ("${HAVE_QT}" STREQUAL "Qt5::Core")
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND cmd /C "set PATH=${Qt5Core_DIR}/../../../bin;%PATH% && ${WINDEPLOYQT_APP} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.exe ${eth_qml_dir}"
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
)
#workaround for https://bugreports.qt.io/browse/QTBUG-42083
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND cmd /C "(echo [Paths] & echo.Prefix=.)" > "qt.conf"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} VERBATIM
)
endif()
#copy additional dlls
foreach(dll ${ETH_INSTALL_EXECUTABLE_DLLS})
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND ${CMAKE_COMMAND}
ARGS -E copy ${dll} "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}"
)
endforeach(dll)
install( TARGETS ${EXECUTABLE} RUNTIME
DESTINATION bin
COMPONENT ${EXECUTABLE}

10
cmake/FindMHD.cmake

@ -29,13 +29,19 @@ set(MHD_LIBRARIES ${MHD_LIBRARY})
# boost is using the same "hack" as us with "optimized" and "debug"
# official MHD project actually uses _d suffix
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
#TODO: place dlls into CMAKE_CFG_INTDIR subfolders
string(REPLACE ".lib" ".dll" MHD_DLL_RELEASE ${MHD_LIBRARY})
string(REPLACE "/lib/" "/bin/" MHD_DLL_RELEASE ${MHD_DLL_RELEASE})
find_library(
MHD_LIBRARY_DEBUG
NAMES microhttpd_d microhttpd-10_d libmicrohttpd_d libmicrohttpd-dll_d
DOC "mhd debug library"
)
set(MHD_LIBRARIES optimized ${MHD_LIBRARIES} debug ${MHD_LIBRARY_DEBUG})
# always use release for now
#string(REPLACE ".lib" ".dll" MHD_DLL_DEBUG ${MHD_LIBRARY_DEBUG})
#set(MHD_LIBRARIES optimized ${MHD_LIBRARIES} debug ${MHD_LIBRARY_DEBUG})
endif()

4
eth/CMakeLists.txt

@ -28,5 +28,9 @@ endif()
target_link_libraries(${EXECUTABLE} webthree)
target_link_libraries(${EXECUTABLE} secp256k1)
if (WIN32)
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD COMMAND ${CMAKE_COMMAND} ARGS -E copy ${MHD_DLL_RELEASE} "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}")
endif()
install( TARGETS ${EXECUTABLE} DESTINATION bin )

1
evmjit/libevmjit/ExecStats.h

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

2
evmjit/libevmjit/preprocessor/llvm_includes_end.h

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

4
evmjit/libevmjit/preprocessor/llvm_includes_start.h

@ -1,6 +1,10 @@
#if defined(_MSC_VER)
#pragma warning(push)
#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
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"

2
extdep/CMakeLists.txt

@ -7,7 +7,7 @@ include(eth_download.cmake)
# all dependencies will be installed into this directory, separated by platform
string(TOLOWER ${CMAKE_SYSTEM_NAME} _system_name)
set(ETH_DEPENDENCY_INSTALL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/install/${_system_name}")
set(ETH_DEPENDENCY_SERVER "http://build.ethdev.com/builds/${_system_name}-precompiled")
set(ETH_DEPENDENCY_SERVER "https://build.ethdev.com/builds/${_system_name}-precompiled")
file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/lib)
file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/include)
file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/bin)

4
libdevcore/RLP.h

@ -242,7 +242,9 @@ public:
AllowNonCanon = 1,
ThrowOnFail = 4,
FailIfTooBig = 8,
FailIfTooSmall = 16,
Strict = ThrowOnFail | FailIfTooBig,
VeryStrict = ThrowOnFail | FailIfTooBig | FailIfTooSmall,
LaisezFaire = AllowNonCanon
};
@ -269,7 +271,7 @@ public:
template <class _N> _N toHash(int _flags = Strict) const
{
if (!isData() || (length() > _N::size && (_flags & FailIfTooBig)))
if (!isData() || (length() > _N::size && (_flags & FailIfTooBig)) || (length() < _N::size && (_flags & FailIfTooSmall)))
if (_flags & ThrowOnFail)
BOOST_THROW_EXCEPTION(BadCast());
else

2
libethereum/Transaction.cpp

@ -40,7 +40,7 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig)
m_gasPrice = rlp[field = 1].toInt<u256>();
m_gas = rlp[field = 2].toInt<u256>();
m_type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall;
m_receiveAddress = rlp[field = 3].toHash<Address>();
m_receiveAddress = rlp[field = 3].isEmpty() ? Address() : rlp[field = 3].toHash<Address>(RLP::VeryStrict);
m_value = rlp[field = 4].toInt<u256>();
m_data = rlp[field = 5].toBytes();
byte v = rlp[field = 6].toInt<byte>() - 27;

71
libsolidity/AST.cpp

@ -77,6 +77,9 @@ void ContractDefinition::checkTypeRequirements()
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
function->checkTypeRequirements();
for (ASTPointer<VariableDeclaration> const& variable: m_stateVariables)
variable->checkTypeRequirements();
// check for hash collisions in function signatures
set<FixedHash<4>> hashes;
for (auto const& it: getInterfaceFunctionList())
@ -294,6 +297,12 @@ bool VariableDeclaration::isLValue() const
return !isExternalFunctionParameter();
}
void VariableDeclaration::checkTypeRequirements()
{
if (m_value)
m_value->checkTypeRequirements();
}
bool VariableDeclaration::isExternalFunctionParameter() const
{
auto const* function = dynamic_cast<FunctionDefinition const*>(getScope());
@ -390,26 +399,26 @@ void Return::checkTypeRequirements()
m_expression->expectType(*m_returnParameters->getParameters().front()->getType());
}
void VariableDefinition::checkTypeRequirements()
void VariableDeclarationStatement::checkTypeRequirements()
{
// Variables can be declared without type (with "var"), in which case the first assignment
// sets the type.
// Note that assignments before the first declaration are legal because of the special scoping
// rules inherited from JavaScript.
if (m_value)
if (m_variable->getValue())
{
if (m_variable->getType())
m_value->expectType(*m_variable->getType());
m_variable->getValue()->expectType(*m_variable->getType());
else
{
// no type declared and no previous assignment, infer the type
m_value->checkTypeRequirements();
TypePointer type = m_value->getType();
m_variable->getValue()->checkTypeRequirements();
TypePointer type = m_variable->getValue()->getType();
if (type->getCategory() == Type::Category::IntegerConstant)
{
auto intType = dynamic_pointer_cast<IntegerConstantType const>(type)->getIntegerType();
if (!intType)
BOOST_THROW_EXCEPTION(m_value->createTypeError("Invalid integer constant " + type->toString()));
BOOST_THROW_EXCEPTION(m_variable->getValue()->createTypeError("Invalid integer constant " + type->toString()));
type = intType;
}
else if (type->getCategory() == Type::Category::Void)
@ -418,7 +427,6 @@ void VariableDefinition::checkTypeRequirements()
}
}
}
void Assignment::checkTypeRequirements()
{
m_leftHandSide->checkTypeRequirements();
@ -601,13 +609,48 @@ void MemberAccess::checkTypeRequirements()
void IndexAccess::checkTypeRequirements()
{
m_base->checkTypeRequirements();
if (m_base->getType()->getCategory() != Type::Category::Mapping)
BOOST_THROW_EXCEPTION(m_base->createTypeError("Indexed expression has to be a mapping (is " +
m_base->getType()->toString() + ")"));
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType());
m_index->expectType(*type.getKeyType());
m_type = type.getValueType();
m_isLValue = true;
switch (m_base->getType()->getCategory())
{
case Type::Category::Array:
{
ArrayType const& type = dynamic_cast<ArrayType const&>(*m_base->getType());
if (!m_index)
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()

64
libsolidity/AST.h

@ -133,7 +133,7 @@ class Declaration: public ASTNode
{
public:
/// Visibility ordered from restricted to unrestricted.
enum class Visibility { Default, Private, Inheritable, Public, External };
enum class Visibility { Default, Private, Internal, Public, External };
Declaration(Location const& _location, ASTPointer<ASTString> const& _name,
Visibility _visibility = Visibility::Default):
@ -144,7 +144,7 @@ public:
Visibility getVisibility() const { return m_visibility == Visibility::Default ? getDefaultVisibility() : m_visibility; }
bool isPublic() const { return getVisibility() >= Visibility::Public; }
bool isVisibleInContract() const { return getVisibility() != Visibility::External; }
bool isVisibleInDerivedContracts() const { return isVisibleInContract() && getVisibility() >= Visibility::Inheritable; }
bool isVisibleInDerivedContracts() const { return isVisibleInContract() && getVisibility() >= Visibility::Internal; }
/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
/// Available only after name and type resolution step.
@ -432,14 +432,17 @@ class VariableDeclaration: public Declaration
{
public:
VariableDeclaration(Location const& _location, ASTPointer<TypeName> const& _type,
ASTPointer<ASTString> const& _name, Visibility _visibility,
bool _isStateVar = false, bool _isIndexed = false):
Declaration(_location, _name, _visibility), m_typeName(_type),
m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed) {}
ASTPointer<ASTString> const& _name, ASTPointer<Expression> _value,
Visibility _visibility,
bool _isStateVar = false, bool _isIndexed = false):
Declaration(_location, _name, _visibility),
m_typeName(_type), m_value(_value),
m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed) {}
virtual void accept(ASTVisitor& _visitor) 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; }
/// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly
/// declared and there is no assignment to the variable that fixes the type.
@ -447,16 +450,20 @@ public:
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
virtual bool isLValue() const override;
/// Calls checkTypeRequirments for all state variables.
void checkTypeRequirements();
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
bool isExternalFunctionParameter() const;
bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; }
protected:
Visibility getDefaultVisibility() const override { return Visibility::Inheritable; }
Visibility getDefaultVisibility() const override { return Visibility::Internal; }
private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
ASTPointer<Expression> m_value; ///< the assigned value, can be missing
bool m_isStateVariable; ///< Whether or not this is a contract state variable
bool m_isIndexed; ///< Whether this is an indexed variable (used by events).
@ -581,7 +588,7 @@ public:
/// 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.
/// 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;
};
/**
@ -598,7 +605,7 @@ public:
}
virtual void accept(ASTVisitor& _visitor) 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; }
@ -616,7 +623,7 @@ public:
TypeName(_location), m_name(_name), m_referencedDeclaration(nullptr) {}
virtual void accept(ASTVisitor& _visitor) 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; }
void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
@ -639,7 +646,7 @@ public:
TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {}
virtual void accept(ASTVisitor& _visitor) 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; }
TypeName const& getValueType() const { return *m_valueType; }
@ -649,6 +656,27 @@ private:
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
@ -833,22 +861,20 @@ private:
* also be "var") but the actual assignment can be missing.
* Examples: var a = 2; uint256 a;
*/
class VariableDefinition: public Statement
class VariableDeclarationStatement: public Statement
{
public:
VariableDefinition(Location const& _location, ASTPointer<VariableDeclaration> _variable,
ASTPointer<Expression> _value):
Statement(_location), m_variable(_variable), m_value(_value) {}
VariableDeclarationStatement(Location const& _location, ASTPointer<VariableDeclaration> _variable):
Statement(_location), m_variable(_variable) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
VariableDeclaration const& getDeclaration() const { return *m_variable; }
Expression const* getExpression() const { return m_value.get(); }
Expression const* getExpression() const { return m_variable->getValue().get(); }
private:
ASTPointer<VariableDeclaration> m_variable;
ASTPointer<Expression> m_value; ///< the assigned value, can be missing
};
/**
@ -1076,7 +1102,7 @@ public:
virtual void checkTypeRequirements() override;
Expression const& getBaseExpression() const { return *m_base; }
Expression const& getIndexExpression() const { return *m_index; }
Expression const* getIndexExpression() const { return m_index.get(); }
private:
ASTPointer<Expression> m_base;

3
libsolidity/ASTForward.h

@ -53,6 +53,7 @@ class TypeName;
class ElementaryTypeName;
class UserDefinedTypeName;
class Mapping;
class ArrayTypeName;
class Statement;
class Block;
class PlaceholderStatement;
@ -63,7 +64,7 @@ class ForStatement;
class Continue;
class Break;
class Return;
class VariableDefinition;
class VariableDeclarationStatement;
class ExpressionStatement;
class Expression;
class Assignment;

4
libsolidity/ASTJsonConverter.cpp

@ -198,7 +198,7 @@ bool ASTJsonConverter::visit(Return const&)
return true;
}
bool ASTJsonConverter::visit(VariableDefinition const&)
bool ASTJsonConverter::visit(VariableDeclarationStatement const&)
{
addJsonNode("VariableDefinition", {}, true);
return true;
@ -394,7 +394,7 @@ void ASTJsonConverter::endVisit(Return const&)
goUp();
}
void ASTJsonConverter::endVisit(VariableDefinition const&)
void ASTJsonConverter::endVisit(VariableDeclarationStatement const&)
{
goUp();
}

4
libsolidity/ASTJsonConverter.h

@ -64,7 +64,7 @@ public:
bool visit(Continue const& _node) override;
bool visit(Break const& _node) override;
bool visit(Return const& _node) override;
bool visit(VariableDefinition const& _node) override;
bool visit(VariableDeclarationStatement const& _node) override;
bool visit(ExpressionStatement const& _node) override;
bool visit(Expression const& _node) override;
bool visit(Assignment const& _node) override;
@ -98,7 +98,7 @@ public:
void endVisit(Continue const&) override;
void endVisit(Break const&) override;
void endVisit(Return const&) override;
void endVisit(VariableDefinition const&) override;
void endVisit(VariableDeclarationStatement const&) override;
void endVisit(ExpressionStatement const&) override;
void endVisit(Expression const&) override;
void endVisit(Assignment const&) override;

18
libsolidity/ASTPrinter.cpp

@ -155,6 +155,13 @@ bool ASTPrinter::visit(Mapping const& _node)
return goDeeper();
}
bool ASTPrinter::visit(ArrayTypeName const& _node)
{
writeLine("ArrayTypeName");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Statement const& _node)
{
writeLine("Statement");
@ -225,9 +232,9 @@ bool ASTPrinter::visit(Return const& _node)
return goDeeper();
}
bool ASTPrinter::visit(VariableDefinition const& _node)
bool ASTPrinter::visit(VariableDeclarationStatement const& _node)
{
writeLine("VariableDefinition");
writeLine("VariableDeclarationStatement");
printSourcePart(_node);
return goDeeper();
}
@ -419,6 +426,11 @@ void ASTPrinter::endVisit(Mapping const&)
m_indentation--;
}
void ASTPrinter::endVisit(ArrayTypeName const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Statement const&)
{
m_indentation--;
@ -469,7 +481,7 @@ void ASTPrinter::endVisit(Return const&)
m_indentation--;
}
void ASTPrinter::endVisit(VariableDefinition const&)
void ASTPrinter::endVisit(VariableDeclarationStatement const&)
{
m_indentation--;
}

6
libsolidity/ASTPrinter.h

@ -58,6 +58,7 @@ public:
bool visit(ElementaryTypeName const& _node) override;
bool visit(UserDefinedTypeName const& _node) override;
bool visit(Mapping const& _node) override;
bool visit(ArrayTypeName const& _node) override;
bool visit(Statement const& _node) override;
bool visit(Block const& _node) override;
bool visit(PlaceholderStatement const& _node) override;
@ -68,7 +69,7 @@ public:
bool visit(Continue const& _node) override;
bool visit(Break const& _node) override;
bool visit(Return const& _node) override;
bool visit(VariableDefinition const& _node) override;
bool visit(VariableDeclarationStatement const& _node) override;
bool visit(ExpressionStatement const& _node) override;
bool visit(Expression const& _node) override;
bool visit(Assignment const& _node) override;
@ -99,6 +100,7 @@ public:
void endVisit(ElementaryTypeName const&) override;
void endVisit(UserDefinedTypeName const&) override;
void endVisit(Mapping const&) override;
void endVisit(ArrayTypeName const&) override;
void endVisit(Statement const&) override;
void endVisit(Block const&) override;
void endVisit(PlaceholderStatement const&) override;
@ -109,7 +111,7 @@ public:
void endVisit(Continue const&) override;
void endVisit(Break const&) override;
void endVisit(Return const&) override;
void endVisit(VariableDefinition const&) override;
void endVisit(VariableDeclarationStatement const&) override;
void endVisit(ExpressionStatement const&) override;
void endVisit(Expression const&) override;
void endVisit(Assignment const&) override;

12
libsolidity/ASTVisitor.h

@ -59,6 +59,7 @@ public:
virtual bool visit(ElementaryTypeName&) { return true; }
virtual bool visit(UserDefinedTypeName&) { return true; }
virtual bool visit(Mapping&) { return true; }
virtual bool visit(ArrayTypeName&) { return true; }
virtual bool visit(Statement&) { return true; }
virtual bool visit(Block&) { return true; }
virtual bool visit(PlaceholderStatement&) { return true; }
@ -69,7 +70,7 @@ public:
virtual bool visit(Continue&) { return true; }
virtual bool visit(Break&) { return true; }
virtual bool visit(Return&) { return true; }
virtual bool visit(VariableDefinition&) { return true; }
virtual bool visit(VariableDeclarationStatement&) { return true; }
virtual bool visit(ExpressionStatement&) { return true; }
virtual bool visit(Expression&) { return true; }
virtual bool visit(Assignment&) { return true; }
@ -102,6 +103,7 @@ public:
virtual void endVisit(ElementaryTypeName&) { }
virtual void endVisit(UserDefinedTypeName&) { }
virtual void endVisit(Mapping&) { }
virtual void endVisit(ArrayTypeName&) { }
virtual void endVisit(Statement&) { }
virtual void endVisit(Block&) { }
virtual void endVisit(PlaceholderStatement&) { }
@ -112,7 +114,7 @@ public:
virtual void endVisit(Continue&) { }
virtual void endVisit(Break&) { }
virtual void endVisit(Return&) { }
virtual void endVisit(VariableDefinition&) { }
virtual void endVisit(VariableDeclarationStatement&) { }
virtual void endVisit(ExpressionStatement&) { }
virtual void endVisit(Expression&) { }
virtual void endVisit(Assignment&) { }
@ -149,6 +151,7 @@ public:
virtual bool visit(ElementaryTypeName const&) { return true; }
virtual bool visit(UserDefinedTypeName 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(Block const&) { return true; }
virtual bool visit(PlaceholderStatement const&) { return true; }
@ -159,7 +162,7 @@ public:
virtual bool visit(Continue const&) { return true; }
virtual bool visit(Break const&) { return true; }
virtual bool visit(Return const&) { return true; }
virtual bool visit(VariableDefinition const&) { return true; }
virtual bool visit(VariableDeclarationStatement const&) { return true; }
virtual bool visit(ExpressionStatement const&) { return true; }
virtual bool visit(Expression const&) { return true; }
virtual bool visit(Assignment const&) { return true; }
@ -192,6 +195,7 @@ public:
virtual void endVisit(ElementaryTypeName const&) { }
virtual void endVisit(UserDefinedTypeName const&) { }
virtual void endVisit(Mapping const&) { }
virtual void endVisit(ArrayTypeName const&) { }
virtual void endVisit(Statement const&) { }
virtual void endVisit(Block const&) { }
virtual void endVisit(PlaceholderStatement const&) { }
@ -202,7 +206,7 @@ public:
virtual void endVisit(Continue const&) { }
virtual void endVisit(Break const&) { }
virtual void endVisit(Return const&) { }
virtual void endVisit(VariableDefinition const&) { }
virtual void endVisit(VariableDeclarationStatement const&) { }
virtual void endVisit(ExpressionStatement const&) { }
virtual void endVisit(Expression const&) { }
virtual void endVisit(Assignment const&) { }

48
libsolidity/AST_accept.h

@ -196,16 +196,24 @@ void FunctionDefinition::accept(ASTConstVisitor& _visitor) const
void VariableDeclaration::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
if (m_typeName)
m_typeName->accept(_visitor);
if (m_value)
m_value->accept(_visitor);
}
_visitor.endVisit(*this);
}
void VariableDeclaration::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
if (m_typeName)
m_typeName->accept(_visitor);
if (m_value)
m_value->accept(_visitor);
}
_visitor.endVisit(*this);
}
@ -319,6 +327,28 @@ void Mapping::accept(ASTConstVisitor& _visitor) const
_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)
{
if (_visitor.visit(*this))
@ -475,25 +505,17 @@ void ExpressionStatement::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this);
}
void VariableDefinition::accept(ASTVisitor& _visitor)
void VariableDeclarationStatement::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_variable->accept(_visitor);
if (m_value)
m_value->accept(_visitor);
}
_visitor.endVisit(*this);
}
void VariableDefinition::accept(ASTConstVisitor& _visitor) const
void VariableDeclarationStatement::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_variable->accept(_visitor);
if (m_value)
m_value->accept(_visitor);
}
_visitor.endVisit(*this);
}
@ -604,7 +626,8 @@ void IndexAccess::accept(ASTVisitor& _visitor)
if (_visitor.visit(*this))
{
m_base->accept(_visitor);
m_index->accept(_visitor);
if (m_index)
m_index->accept(_visitor);
}
_visitor.endVisit(*this);
}
@ -614,7 +637,8 @@ void IndexAccess::accept(ASTConstVisitor& _visitor) const
if (_visitor.visit(*this))
{
m_base->accept(_visitor);
m_index->accept(_visitor);
if (m_index)
m_index->accept(_visitor);
}
_visitor.endVisit(*this);
}

51
libsolidity/Compiler.cpp

@ -34,6 +34,20 @@ using namespace std;
namespace dev {
namespace solidity {
/**
* Simple helper class to ensure that the stack height is the same at certain places in the code.
*/
class StackHeightChecker
{
public:
StackHeightChecker(CompilerContext const& _context):
m_context(_context), stackHeight(m_context.getStackHeight()) {}
void check() { solAssert(m_context.getStackHeight() == stackHeight, "I sense a disturbance in the stack."); }
private:
CompilerContext const& m_context;
unsigned stackHeight;
};
void Compiler::compileContract(ContractDefinition const& _contract,
map<ContractDefinition const*, bytes const*> const& _contracts)
{
@ -73,7 +87,7 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp
for (ASTPointer<InheritanceSpecifier> const& base: contract->getBaseContracts())
{
ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>(
base->getName()->getReferencedDeclaration());
base->getName()->getReferencedDeclaration());
solAssert(baseContract, "");
if (baseArguments.count(baseContract) == 0)
baseArguments[baseContract] = &base->getArguments();
@ -85,12 +99,14 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp
{
ContractDefinition const* base = bases[bases.size() - i];
solAssert(base, "");
initializeStateVariables(*base);
FunctionDefinition const* baseConstructor = base->getConstructor();
if (!baseConstructor)
continue;
solAssert(baseArguments[base], "");
appendBaseConstructorCall(*baseConstructor, *baseArguments[base]);
}
initializeStateVariables(_contract);
if (_contract.getConstructor())
appendConstructorCall(*_contract.getConstructor());
@ -247,6 +263,13 @@ void Compiler::registerStateVariables(ContractDefinition const& _contract)
m_context.addStateVariable(*variable);
}
void Compiler::initializeStateVariables(ContractDefinition const& _contract)
{
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
if (variable->getValue())
ExpressionCompiler::appendStateVariableInitialization(m_context, *variable);
}
bool Compiler::visit(VariableDeclaration const& _variableDeclaration)
{
solAssert(_variableDeclaration.isStateVariable(), "Compiler visit to non-state variable declaration.");
@ -331,6 +354,8 @@ bool Compiler::visit(FunctionDefinition const& _function)
bool Compiler::visit(IfStatement const& _ifStatement)
{
StackHeightChecker checker(m_context);
compileExpression(_ifStatement.getCondition());
eth::AssemblyItem trueTag = m_context.appendConditionalJump();
if (_ifStatement.getFalseStatement())
@ -339,11 +364,15 @@ bool Compiler::visit(IfStatement const& _ifStatement)
m_context << trueTag;
_ifStatement.getTrueStatement().accept(*this);
m_context << endTag;
checker.check();
return false;
}
bool Compiler::visit(WhileStatement const& _whileStatement)
{
StackHeightChecker checker(m_context);
eth::AssemblyItem loopStart = m_context.newTag();
eth::AssemblyItem loopEnd = m_context.newTag();
m_continueTags.push_back(loopStart);
@ -361,11 +390,15 @@ bool Compiler::visit(WhileStatement const& _whileStatement)
m_continueTags.pop_back();
m_breakTags.pop_back();
checker.check();
return false;
}
bool Compiler::visit(ForStatement const& _forStatement)
{
StackHeightChecker checker(m_context);
eth::AssemblyItem loopStart = m_context.newTag();
eth::AssemblyItem loopEnd = m_context.newTag();
m_continueTags.push_back(loopStart);
@ -395,6 +428,8 @@ bool Compiler::visit(ForStatement const& _forStatement)
m_continueTags.pop_back();
m_breakTags.pop_back();
checker.check();
return false;
}
@ -429,29 +464,35 @@ bool Compiler::visit(Return const& _return)
return false;
}
bool Compiler::visit(VariableDefinition const& _variableDefinition)
bool Compiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement)
{
if (Expression const* expression = _variableDefinition.getExpression())
StackHeightChecker checker(m_context);
if (Expression const* expression = _variableDeclarationStatement.getExpression())
{
compileExpression(*expression, _variableDefinition.getDeclaration().getType());
CompilerUtils(m_context).moveToStackVariable(_variableDefinition.getDeclaration());
compileExpression(*expression, _variableDeclarationStatement.getDeclaration().getType());
CompilerUtils(m_context).moveToStackVariable(_variableDeclarationStatement.getDeclaration());
}
checker.check();
return false;
}
bool Compiler::visit(ExpressionStatement const& _expressionStatement)
{
StackHeightChecker checker(m_context);
Expression const& expression = _expressionStatement.getExpression();
compileExpression(expression);
CompilerUtils(m_context).popStackElement(*expression.getType());
checker.check();
return false;
}
bool Compiler::visit(PlaceholderStatement const&)
{
StackHeightChecker checker(m_context);
++m_modifierDepth;
appendModifierOrFunctionCode();
--m_modifierDepth;
checker.check();
return true;
}

3
libsolidity/Compiler.h

@ -59,6 +59,7 @@ private:
void appendReturnValuePacker(TypePointers const& _typeParameters);
void registerStateVariables(ContractDefinition const& _contract);
void initializeStateVariables(ContractDefinition const& _contract);
virtual bool visit(VariableDeclaration const& _variableDeclaration) override;
virtual bool visit(FunctionDefinition const& _function) override;
@ -68,7 +69,7 @@ private:
virtual bool visit(Continue const& _continue) override;
virtual bool visit(Break const& _break) override;
virtual bool visit(Return const& _return) override;
virtual bool visit(VariableDefinition const& _variableDefinition) override;
virtual bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override;
virtual bool visit(ExpressionStatement const& _expressionStatement) override;
virtual bool visit(PlaceholderStatement const&) override;

2
libsolidity/CompilerContext.h

@ -48,7 +48,7 @@ public:
bytes const& getCompiledContract(ContractDefinition const& _contract) const;
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
unsigned getStackHeight() { solAssert(m_asm.deposit() >= 0, ""); return unsigned(m_asm.deposit()); }
unsigned getStackHeight() const { solAssert(m_asm.deposit() >= 0, ""); return unsigned(m_asm.deposit()); }
bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration) != 0; }
bool isLocalVariable(Declaration const* _declaration) const;

63
libsolidity/CompilerStack.cpp

@ -58,21 +58,22 @@ CompilerStack::CompilerStack(bool _addStandardSources):
m_addStandardSources(_addStandardSources), m_parseSuccessful(false)
{
if (m_addStandardSources)
addSources(StandardSources);
addSources(StandardSources, true); // add them as libraries
}
bool CompilerStack::addSource(string const& _name, string const& _content)
bool CompilerStack::addSource(string const& _name, string const& _content, bool _isLibrary)
{
bool existed = m_sources.count(_name) != 0;
reset(true);
m_sources[_name].scanner = make_shared<Scanner>(CharStream(expanded(_content)), _name);
m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content), _name);
m_sources[_name].isLibrary = _isLibrary;
return existed;
}
void CompilerStack::setSource(string const& _sourceCode)
{
reset();
addSource("", expanded(_sourceCode));
addSource("", _sourceCode);
}
void CompilerStack::parse()
@ -126,57 +127,6 @@ vector<string> CompilerStack::getContractNames() const
return contractNames;
}
////// BEGIN: TEMPORARY ONLY
///
/// NOTE: THIS INVALIDATES SOURCE POINTERS AND CAN CRASH THE COMPILER
///
/// remove once import works properly and we have genesis contracts
string CompilerStack::expanded(string const& _sourceCode)
{
const map<string, string> c_standardSources = map<string, string>{
{ "Config", "contract Config{function lookup(uint256 service)constant returns(address a){}function kill(){}function unregister(uint256 id){}function register(uint256 id,address service){}}" },
{ "Coin", "contract Coin{function isApprovedFor(address _target,address _proxy)constant returns(bool _r){}function isApproved(address _proxy)constant returns(bool _r){}function sendCoinFrom(address _from,uint256 _val,address _to){}function coinBalanceOf(address _a)constant returns(uint256 _r){}function sendCoin(uint256 _val,address _to){}function coinBalance()constant returns(uint256 _r){}function approve(address _a){}}"},
{ "CoinReg", "contract CoinReg{function count()constant returns(uint256 r){}function info(uint256 i)constant returns(address addr,string3 name,uint256 denom){}function register(string3 name,uint256 denom){}function unregister(){}}" },
{ "coin", "#require CoinReg\ncontract coin {function coin(string3 name, uint denom) {CoinReg(Config().lookup(3)).register(name, denom);}}" },
{ "service", "#require Config\ncontract service{function service(uint _n){Config().register(_n, this);}}" },
{ "owned", "contract owned{function owned(){owner = msg.sender;}modifier onlyowner(){if(msg.sender==owner)_}address owner;}" },
{ "mortal", "#require owned\ncontract mortal is owned {function kill() { if (msg.sender == owner) suicide(owner); }}" },
{ "NameReg", "contract NameReg{function register(string32 name){}function addressOf(string32 name)constant returns(address addr){}function unregister(){}function nameOf(address addr)constant returns(string32 name){}}" },
{ "named", "#require Config NameReg\ncontract named {function named(string32 name) {NameReg(Config().lookup(1)).register(name);}}" },
{ "std", "#require owned mortal Config NameReg named" },
};
string sub;
set<string> got;
function<string(string const&)> localExpanded;
localExpanded = [&](string const& s) -> string
{
string ret = s;
for (size_t p = 0; p != string::npos;)
if ((p = ret.find("#require ")) != string::npos)
{
string n = ret.substr(p + 9, ret.find_first_of('\n', p + 9) - p - 9);
ret.replace(p, n.size() + 9, "");
vector<string> rs;
boost::split(rs, n, boost::is_any_of(" \t,"), boost::token_compress_on);
for (auto const& r: rs)
if (!got.count(r))
{
if (c_standardSources.count(r))
sub.append("\n" + localExpanded(c_standardSources.at(r)) + "\n");
got.insert(r);
}
}
// TODO: remove once we have genesis contracts.
else if ((p = ret.find("Config()")) != string::npos)
ret.replace(p, 8, "Config(0xc6d9d2cd449a754c494264e1809c50e34d64562b)");
return ret;
};
return sub + localExpanded(_sourceCode);
}
////// END: TEMPORARY ONLY
void CompilerStack::compile(bool _optimize)
{
@ -328,7 +278,8 @@ void CompilerStack::resolveImports()
};
for (auto const& sourcePair: m_sources)
toposort(&sourcePair.second);
if (!sourcePair.second.isLibrary)
toposort(&sourcePair.second);
swap(m_sourceOrder, sourceOrder);
}

13
libsolidity/CompilerStack.h

@ -60,12 +60,12 @@ class CompilerStack: boost::noncopyable
{
public:
/// 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.
/// @returns true if a source object by the name already existed and was replaced.
void addSources(std::map<std::string, std::string> const& _nameContents) { for (auto const& i: _nameContents) addSource(i.first, i.second); }
bool addSource(std::string const& _name, std::string const& _content);
void addSources(std::map<std::string, std::string> const& _nameContents, bool _isLibrary = false) { for (auto const& i: _nameContents) addSource(i.first, i.second, _isLibrary); }
bool addSource(std::string const& _name, std::string const& _content, bool _isLibrary = false);
void setSource(std::string const& _sourceCode);
/// Parses all source units that were added
void parse();
@ -125,7 +125,8 @@ private:
std::shared_ptr<Scanner> scanner;
std::shared_ptr<SourceUnit> ast;
std::string interface;
void reset() { scanner.reset(); ast.reset(); interface.clear(); }
bool isLibrary = false;
void reset() { scanner.reset(); ast.reset(); interface.clear(); isLibrary = false;}
};
struct Contract
@ -143,10 +144,6 @@ private:
Contract();
};
/// Expand source code with preprocessor-like includes.
/// @todo Replace with better framework.
std::string expanded(std::string const& _sourceCode);
void reset(bool _keepSources = false);
void resolveImports();

32
libsolidity/CompilerUtils.cpp

@ -36,14 +36,14 @@ const unsigned int CompilerUtils::dataStartOffset = 4;
unsigned CompilerUtils::loadFromMemory(unsigned _offset, Type const& _type,
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);
return loadFromMemoryHelper(_type, _fromCalldata, _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;
unsigned numBytes = loadFromMemoryHelper(_type, _fromCalldata, _padToWordBoundaries);
// 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)
{
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);
if (numBytes > 0)
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)
{
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
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5
@ -79,7 +80,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
}
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;
// stack here: memory_offset storage_offset length_bytes
// 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;
}
void CompilerUtils::copyByteArrayToStorage(ByteArrayType const& _targetType,
ByteArrayType const& _sourceType) const
void CompilerUtils::copyByteArrayToStorage(
ArrayType const& _targetType, ArrayType const& _sourceType) const
{
// stack layout: [source_ref] target_ref (top)
// 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())
{
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
// slice a byte array from calldata.
@ -224,7 +227,7 @@ void CompilerUtils::copyByteArrayToStorage(ByteArrayType const& _targetType,
<< eth::Instruction::POP << eth::Instruction::POP;
break;
}
case ByteArrayType::Location::Storage:
case ArrayType::Location::Storage:
{
// 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;
}
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
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.
/// Stack pre: [source_reference] 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.
/// Stack pre: reference
/// Stack post:
void clearByteArray(ByteArrayType const& _type) const;
void clearByteArray(ArrayType const& _type) const;
/// Bytes we need to the start of call data.
/// - The size in bytes of the function (hash) identifier.

59
libsolidity/ExpressionCompiler.cpp

@ -56,6 +56,23 @@ void ExpressionCompiler::appendStateVariableAccessor(CompilerContext& _context,
compiler.appendStateVariableAccessor(_varDecl);
}
void ExpressionCompiler::appendStateVariableInitialization(CompilerContext& _context, VariableDeclaration const& _varDecl, bool _optimize)
{
compileExpression(_context, *(_varDecl.getValue()), _optimize);
if (_varDecl.getValue()->getType())
appendTypeConversion(_context, *(_varDecl.getValue())->getType(), *(_varDecl.getValue())->getType());
ExpressionCompiler compiler(_context, _optimize);
compiler.appendStateVariableInitialization(_varDecl);
}
void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration const& _varDecl)
{
LValue var = LValue(m_context);
var.fromDeclaration(_varDecl, _varDecl.getValue()->getLocation());
var.storeValue(*_varDecl.getType(), _varDecl.getLocation());
}
bool ExpressionCompiler::visit(Assignment const& _assignment)
{
_assignment.getRightHandSide().accept(*this);
@ -77,7 +94,6 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
}
m_currentLValue.storeValue(*_assignment.getRightHandSide().getType(), _assignment.getLocation());
m_currentLValue.reset();
return false;
}
@ -514,20 +530,21 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
m_context << enumType->getMemberValue(_memberAccess.getMemberName());
break;
}
case Type::Category::ByteArray:
case Type::Category::Array:
{
solAssert(member == "length", "Illegal bytearray member.");
auto const& type = dynamic_cast<ByteArrayType const&>(*_memberAccess.getExpression().getType());
solAssert(member == "length", "Illegal array member.");
auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.getExpression().getType());
solAssert(type.isByteArray(), "Non byte arrays not yet implemented here.");
switch (type.getLocation())
{
case ByteArrayType::Location::CallData:
case ArrayType::Location::CallData:
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
break;
case ByteArrayType::Location::Storage:
case ArrayType::Location::Storage:
m_context << eth::Instruction::SLOAD;
break;
default:
solAssert(false, "Unsupported byte array location.");
solAssert(false, "Unsupported array location.");
break;
}
break;
@ -545,7 +562,8 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
solAssert(baseType.getCategory() == Type::Category::Mapping, "");
Type const& keyType = *dynamic_cast<MappingType const&>(baseType).getKeyType();
m_context << u256(0);
appendExpressionCopyToMemory(keyType, _indexAccess.getIndexExpression());
solAssert(_indexAccess.getIndexExpression(), "Index expression expected.");
appendExpressionCopyToMemory(keyType, *_indexAccess.getIndexExpression());
solAssert(baseType.getSizeOnStack() == 1,
"Unexpected: Not exactly one stack slot taken by subscriptable expression.");
m_context << eth::Instruction::SWAP1;
@ -572,7 +590,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag();
else if (dynamic_cast<VariableDeclaration const*>(declaration))
{
m_currentLValue.fromIdentifier(_identifier, *declaration);
m_currentLValue.fromDeclaration(*declaration, _identifier.getLocation());
m_currentLValue.retrieveValueIfLValueNotRequested(_identifier);
}
else if (dynamic_cast<ContractDefinition const*>(declaration))
@ -1002,12 +1020,12 @@ ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType
m_size = unsigned(m_dataType->getSizeOnStack());
}
void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration)
void ExpressionCompiler::LValue::fromDeclaration(Declaration const& _declaration, Location const& _location)
{
if (m_context->isLocalVariable(&_declaration))
{
m_type = LValueType::Stack;
m_dataType = _identifier.getType();
m_dataType = _declaration.getType();
m_size = m_dataType->getSizeOnStack();
m_baseStackOffset = m_context->getBaseStackOffsetOfVariable(_declaration);
}
@ -1015,12 +1033,13 @@ void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, D
{
*m_context << m_context->getStorageLocationOfVariable(_declaration);
m_type = LValueType::Storage;
m_dataType = _identifier.getType();
m_dataType = _declaration.getType();
solAssert(m_dataType->getStorageSize() <= numeric_limits<unsigned>::max(),
"The storage size of " + m_dataType->toString() + " should fit in an unsigned");
m_size = unsigned(m_dataType->getStorageSize()); }
m_size = unsigned(m_dataType->getStorageSize());
}
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_identifier.getLocation())
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_location)
<< errinfo_comment("Identifier type not supported or identifier not found."));
}
@ -1117,12 +1136,12 @@ void ExpressionCompiler::LValue::storeValue(Type const& _sourceType, Location co
}
else
{
solAssert(_sourceType.getCategory() == m_dataType->getCategory(), "");
if (m_dataType->getCategory() == Type::Category::ByteArray)
solAssert(_sourceType.getCategory() == m_dataType->getCategory(), "Wrong type conversation for assignment.");
if (m_dataType->getCategory() == Type::Category::Array)
{
CompilerUtils(*m_context).copyByteArrayToStorage(
dynamic_cast<ByteArrayType const&>(*m_dataType),
dynamic_cast<ByteArrayType const&>(_sourceType));
dynamic_cast<ArrayType const&>(*m_dataType),
dynamic_cast<ArrayType const&>(_sourceType));
if (_move)
*m_context << eth::Instruction::POP;
}
@ -1193,8 +1212,8 @@ void ExpressionCompiler::LValue::setToZero(Location const& _location) const
break;
}
case LValueType::Storage:
if (m_dataType->getCategory() == Type::Category::ByteArray)
CompilerUtils(*m_context).clearByteArray(dynamic_cast<ByteArrayType const&>(*m_dataType));
if (m_dataType->getCategory() == Type::Category::Array)
CompilerUtils(*m_context).clearByteArray(dynamic_cast<ArrayType const&>(*m_dataType));
else if (m_dataType->getCategory() == Type::Category::Struct)
{
// stack layout: ref

15
libsolidity/ExpressionCompiler.h

@ -39,7 +39,7 @@ namespace solidity {
class CompilerContext;
class Type;
class IntegerType;
class ByteArrayType;
class ArrayType;
class StaticStringType;
/**
@ -59,6 +59,9 @@ public:
/// Appends code for a State Variable accessor function
static void appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const& _varDecl, bool _optimize = false);
/// Appends code for a State Variable Initialization function
static void appendStateVariableInitialization(CompilerContext& _context, VariableDeclaration const& _varDecl, bool _optimize = false);
private:
explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false):
m_optimize(_optimize), m_context(_compilerContext), m_currentLValue(m_context) {}
@ -111,6 +114,9 @@ private:
/// Appends code for a State Variable accessor function
void appendStateVariableAccessor(VariableDeclaration const& _varDecl);
/// Appends code for a State Variable initialization
void appendStateVariableInitialization(VariableDeclaration const& _varDecl);
/**
* Helper class to store and retrieve lvalues to and from various locations.
* All types except STACK store a reference in a slot on the stack, STACK just
@ -126,8 +132,9 @@ private:
std::shared_ptr<Type const> const& _dataType, unsigned _baseStackOffset = 0);
/// Set type according to the declaration and retrieve the reference.
/// @a _expression is the current expression
void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration);
/// @a _location is the current location
void fromDeclaration(Declaration const& _declaration, Location const& _location);
void reset() { m_type = LValueType::None; m_dataType.reset(); m_baseStackOffset = 0; m_size = 0; }
bool isValid() const { return m_type != LValueType::None; }
@ -158,7 +165,7 @@ private:
/// Convenience function to retrieve Value from Storage. Specific version of @ref retrieveValue
void retrieveValueFromStorage(bool _remove = false) const;
/// 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;
LValueType m_type = LValueType::None;

12
libsolidity/NameAndTypeResolver.cpp

@ -267,12 +267,12 @@ void DeclarationRegistrationHelper::endVisit(ModifierDefinition&)
closeCurrentScope();
}
void DeclarationRegistrationHelper::endVisit(VariableDefinition& _variableDefinition)
void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _variableDeclarationStatement)
{
// Register the local variables with the function
// This does not fit here perfectly, but it saves us another AST visit.
solAssert(m_currentFunction, "Variable definition without function.");
m_currentFunction->addLocalVariable(_variableDefinition.getDeclaration());
solAssert(m_currentFunction, "Variable declaration without function.");
m_currentFunction->addLocalVariable(_variableDeclarationStatement.getDeclaration());
}
bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration)
@ -334,10 +334,10 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
if (_variable.getTypeName())
{
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 (auto const* byteArrayType = dynamic_cast<ByteArrayType const*>(type.get()))
type = byteArrayType->copyForLocation(ByteArrayType::Location::CallData);
if (auto const* arrayType = dynamic_cast<ArrayType const*>(type.get()))
type = arrayType->copyForLocation(ArrayType::Location::CallData);
_variable.setType(type);
if (!_variable.getType())

2
libsolidity/NameAndTypeResolver.h

@ -105,7 +105,7 @@ private:
void endVisit(FunctionDefinition& _function) override;
bool visit(ModifierDefinition& _modifier) override;
void endVisit(ModifierDefinition& _modifier) override;
void endVisit(VariableDefinition& _variableDefinition) override;
void endVisit(VariableDeclarationStatement& _variableDeclarationStatement) override;
bool visit(VariableDeclaration& _declaration) override;
bool visit(EventDefinition& _event) override;
void endVisit(EventDefinition& _event) override;

235
libsolidity/Parser.cpp

@ -41,8 +41,11 @@ class Parser::ASTNodeFactory
public:
ASTNodeFactory(Parser const& _parser):
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 setLocation(Location const& _location) { m_location = _location; }
void setLocationEmpty() { m_location.end = m_location.start; }
/// Set the end position to the one of the given node.
void setEndPositionFromNode(ASTPointer<ASTNode> const& _node) { m_location.end = _node->getLocation().end; }
@ -148,6 +151,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
{
VarDeclParserOptions options;
options.isStateVariable = true;
options.allowInitialValue = true;
stateVariables.push_back(parseVariableDeclaration(options));
expectToken(Token::Semicolon);
}
@ -186,8 +190,8 @@ Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token)
Declaration::Visibility visibility(Declaration::Visibility::Default);
if (_token == Token::Public)
visibility = Declaration::Visibility::Public;
else if (_token == Token::Inheritable)
visibility = Declaration::Visibility::Inheritable;
else if (_token == Token::Internal)
visibility = Declaration::Visibility::Internal;
else if (_token == Token::Private)
visibility = Declaration::Visibility::Private;
else if (_token == Token::External)
@ -298,12 +302,20 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
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);
ASTPointer<TypeName> type = parseTypeName(_options.allowVar);
if (type != nullptr)
nodeFactory.setEndPositionFromNode(type);
ASTNodeFactory nodeFactory = _lookAheadArrayType ?
ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this);
ASTPointer<TypeName> type;
if (_lookAheadArrayType)
type = _lookAheadArrayType;
else
{
type = parseTypeName(_options.allowVar);
if (type != nullptr)
nodeFactory.setEndPositionFromNode(type);
}
bool isIndexed = false;
ASTPointer<ASTString> identifier;
Token::Value token = m_scanner->getCurrentToken();
@ -324,9 +336,19 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(VarDeclParserOp
}
else
identifier = expectIdentifierToken();
return nodeFactory.createNode<VariableDeclaration>(type, identifier,
visibility, _options.isStateVariable,
isIndexed);
ASTPointer<Expression> value;
if (_options.allowInitialValue)
{
if (m_scanner->getCurrentToken() == Token::Assign)
{
m_scanner->next();
value = parseExpression();
nodeFactory.setEndPositionFromNode(value);
}
}
return nodeFactory.createNode<VariableDeclaration>(type, identifier, value,
visibility, _options.isStateVariable,
isIndexed);
}
ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
@ -396,6 +418,7 @@ ASTPointer<Identifier> Parser::parseIdentifier()
ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<TypeName> type;
Token::Value token = m_scanner->getCurrentToken();
if (Token::isElementaryTypeName(token))
@ -410,9 +433,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
m_scanner->next();
}
else if (token == Token::Mapping)
{
type = parseMapping();
}
else if (token == Token::Identifier)
{
ASTNodeFactory nodeFactory(*this);
@ -421,6 +442,18 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
}
else
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;
}
@ -519,7 +552,7 @@ ASTPointer<Statement> Parser::parseStatement()
}
// fall-through
default:
statement = parseVarDefOrExprStmt();
statement = parseSimpleStatement();
}
expectToken(Token::Semicolon);
return statement;
@ -568,7 +601,7 @@ ASTPointer<ForStatement> Parser::parseForStatement()
// LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RParen?
if (m_scanner->getCurrentToken() != Token::Semicolon)
initExpression = parseVarDefOrExprStmt();
initExpression = parseSimpleStatement();
expectToken(Token::Semicolon);
if (m_scanner->getCurrentToken() != Token::Semicolon)
@ -587,56 +620,89 @@ ASTPointer<ForStatement> Parser::parseForStatement()
body);
}
ASTPointer<Statement> Parser::parseVarDefOrExprStmt()
ASTPointer<Statement> Parser::parseSimpleStatement()
{
if (peekVariableDefinition())
return parseVariableDefinition();
else
// 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();
case LookAheadInfo::ExpressionStatement:
return parseExpressionStatement();
}
default:
break;
}
ASTPointer<VariableDefinition> Parser::parseVariableDefinition()
{
ASTNodeFactory nodeFactory(*this);
VarDeclParserOptions options;
options.allowVar = true;
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options);
ASTPointer<Expression> value;
if (m_scanner->getCurrentToken() == Token::Assign)
// 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();
value = parseExpression();
nodeFactory.setEndPositionFromNode(value);
}
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
nodeFactory.setEndPositionFromNode(variable);
return nodeFactory.createNode<VariableDefinition>(variable, value);
return parseExpressionStatement(expressionFromIndexAccessStructure(primary, indices));
}
ASTPointer<ExpressionStatement> Parser::parseExpressionStatement()
ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStatement(
ASTPointer<TypeName> const& _lookAheadArrayType)
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<Expression> expression = parseExpression();
nodeFactory.setEndPositionFromNode(expression);
return nodeFactory.createNode<ExpressionStatement>(expression);
VarDeclParserOptions options;
options.allowVar = true;
options.allowInitialValue = true;
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options, _lookAheadArrayType);
ASTNodeFactory nodeFactory(*this, variable);
return nodeFactory.createNode<VariableDeclarationStatement>(variable);
}
ASTPointer<Expression> Parser::parseExpression()
ASTPointer<ExpressionStatement> Parser::parseExpressionStatement(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure)
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<Expression> expression = parseBinaryExpression();
ASTPointer<Expression> expression = parseExpression(_lookAheadIndexAccessStructure);
return ASTNodeFactory(*this, expression).createNode<ExpressionStatement>(expression);
}
ASTPointer<Expression> Parser::parseExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure)
{
ASTPointer<Expression> expression = parseBinaryExpression(4, _lookAheadIndexAccessStructure);
if (!Token::isAssignmentOp(m_scanner->getCurrentToken()))
return expression;
Token::Value assignmentOperator = expectAssignmentOperator();
ASTPointer<Expression> rightHandSide = parseExpression();
ASTNodeFactory nodeFactory(*this, expression);
nodeFactory.setEndPositionFromNode(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();
ASTPointer<Expression> expression = parseUnaryExpression(_lookAheadIndexAccessStructure);
ASTNodeFactory nodeFactory(*this, expression);
int precedence = Token::precedence(m_scanner->getCurrentToken());
for (; precedence >= _minPrecedence; --precedence)
while (Token::precedence(m_scanner->getCurrentToken()) == precedence)
@ -650,11 +716,13 @@ ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence)
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();
if (Token::isUnaryOp(token) || Token::isCountOp(token))
if (!_lookAheadIndexAccessStructure && (Token::isUnaryOp(token) || Token::isCountOp(token)))
{
// prefix expression
m_scanner->next();
@ -665,7 +733,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
else
{
// potential postfix expression
ASTPointer<Expression> subExpression = parseLeftHandSideExpression();
ASTPointer<Expression> subExpression = parseLeftHandSideExpression(_lookAheadIndexAccessStructure);
token = m_scanner->getCurrentToken();
if (!Token::isCountOp(token))
return subExpression;
@ -675,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;
if (m_scanner->getCurrentToken() == Token::New)
if (_lookAheadIndexAccessStructure)
expression = _lookAheadIndexAccessStructure;
else if (m_scanner->getCurrentToken() == Token::New)
{
expectToken(Token::New);
ASTPointer<Identifier> contractName(parseIdentifier());
@ -696,7 +769,9 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression()
case Token::LBrack:
{
m_scanner->next();
ASTPointer<Expression> index = parseExpression();
ASTPointer<Expression> index;
if (m_scanner->getCurrentToken() != Token::RBrack)
index = parseExpression();
nodeFactory.markEndPosition();
expectToken(Token::RBrack);
expression = nodeFactory.createNode<IndexAccess>(expression, index);
@ -771,10 +846,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
m_scanner->next();
}
else
{
BOOST_THROW_EXCEPTION(createParserError("Expected primary expression."));
return ASTPointer<Expression>(); // this is not reached
}
break;
}
return expression;
@ -821,18 +893,55 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars
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;
}
bool Parser::peekVariableDefinition()
ASTPointer<TypeName> Parser::typeNameIndexAccessStructure(
ASTPointer<PrimaryExpression> const& _primary, vector<pair<ASTPointer<Expression>, Location>> const& _indices)
{
// distinguish between variable definition (and potentially assignment) and expression statement
// (which include assignments to other expressions and pre-declared variables)
// We have a variable definition if we get a keyword that specifies a type name, or
// in the case of a user-defined type, we have two identifiers following each other.
return (m_scanner->getCurrentToken() == Token::Mapping ||
m_scanner->getCurrentToken() == Token::Var ||
((Token::isElementaryTypeName(m_scanner->getCurrentToken()) ||
m_scanner->getCurrentToken() == Token::Identifier) &&
m_scanner->peekNextToken() == Token::Identifier));
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;
}
ASTPointer<Expression> Parser::expressionFromIndexAccessStructure(
ASTPointer<PrimaryExpression> const& _primary, vector<pair<ASTPointer<Expression>, Location>> const& _indices)
{
ASTNodeFactory nodeFactory(*this, _primary);
ASTPointer<Expression> expression(_primary);
for (auto const& index: _indices)
{
nodeFactory.setLocation(index.second);
expression = nodeFactory.createNode<IndexAccess>(expression, index.first);
}
return expression;
}
void Parser::expectToken(Token::Value _value)

45
libsolidity/Parser.h

@ -51,6 +51,7 @@ private:
bool isStateVariable = false;
bool allowIndexed = false;
bool allowEmptyName = false;
bool allowInitialValue = false;
};
///@{
@ -63,7 +64,9 @@ private:
ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<EnumDefinition> parseEnumDefinition();
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<EventDefinition> parseEventDefinition();
ASTPointer<ModifierInvocation> parseModifierInvocation();
@ -76,13 +79,20 @@ private:
ASTPointer<IfStatement> parseIfStatement();
ASTPointer<WhileStatement> parseWhileStatement();
ASTPointer<ForStatement> parseForStatement();
ASTPointer<Statement> parseVarDefOrExprStmt();
ASTPointer<VariableDefinition> parseVariableDefinition();
ASTPointer<ExpressionStatement> parseExpressionStatement();
ASTPointer<Expression> parseExpression();
ASTPointer<Expression> parseBinaryExpression(int _minPrecedence = 4);
ASTPointer<Expression> parseUnaryExpression();
ASTPointer<Expression> parseLeftHandSideExpression();
/// A "simple statement" can be a variable declaration statement or an expression statement.
ASTPointer<Statement> parseSimpleStatement();
ASTPointer<VariableDeclarationStatement> parseVariableDeclarationStatement(
ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>());
ASTPointer<ExpressionStatement> parseExpressionStatement(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>());
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();
std::vector<ASTPointer<Expression>> parseFunctionCallListArguments();
std::pair<std::vector<ASTPointer<Expression>>, std::vector<ASTPointer<ASTString>>> parseFunctionCallArguments();
@ -91,9 +101,24 @@ private:
///@{
///@name Helper functions
/// Peeks ahead in the scanner to determine if a variable definition is going to follow
bool peekVariableDefinition();
/// Used as return value of @see peekStatementType.
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.
void expectToken(Token::Value _value);
Token::Value expectAssignmentOperator();

4
libsolidity/Token.h

@ -162,7 +162,7 @@ namespace solidity
K(New, "new", 0) \
K(Public, "public", 0) \
K(Private, "private", 0) \
K(Inheritable, "inheritable", 0) \
K(Internal, "internal", 0) \
K(Return, "return", 0) \
K(Returns, "returns", 0) \
K(Struct, "struct", 0) \
@ -380,7 +380,7 @@ public:
static bool isCountOp(Value op) { return op == Inc || op == Dec; }
static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); }
static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; }
static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Inheritable; }
static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; }
static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == Token::SubEther; }
// Returns a string corresponding to the JS token string

77
libsolidity/Types.cpp

@ -58,7 +58,7 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
else if (Token::String0 <= _typeToken && _typeToken <= Token::String32)
return make_shared<StaticStringType>(int(_typeToken) - int(Token::String0));
else if (_typeToken == Token::Bytes)
return make_shared<ByteArrayType>(ByteArrayType::Location::Storage);
return make_shared<ArrayType>(ArrayType::Location::Storage);
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type."));
@ -83,17 +83,35 @@ TypePointer Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
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)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name."));
TypePointer valueType = _typeName.getValueType().toType();
TypePointer valueType = _valueType.toType();
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);
}
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)
{
switch (_literal.getToken())
@ -517,27 +535,27 @@ TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const
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();
}
TypePointer ByteArrayType::unaryOperatorResult(Token::Value _operator) const
TypePointer ArrayType::unaryOperatorResult(Token::Value _operator) const
{
if (_operator == Token::Delete)
return make_shared<VoidType>();
return TypePointer();
}
bool ByteArrayType::operator==(Type const& _other) const
bool ArrayType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
return false;
ByteArrayType const& other = dynamic_cast<ByteArrayType const&>(_other);
ArrayType const& other = dynamic_cast<ArrayType const&>(_other);
return other.m_location == m_location;
}
unsigned ByteArrayType::getSizeOnStack() const
unsigned ArrayType::getSizeOnStack() const
{
if (m_location == Location::CallData)
// offset, length (stack top)
@ -547,12 +565,30 @@ unsigned ByteArrayType::getSizeOnStack() const
return 1;
}
shared_ptr<ByteArrayType> ByteArrayType::copyForLocation(ByteArrayType::Location _location) const
string ArrayType::toString() const
{
if (isByteArray())
return "bytes";
string ret = getBaseType()->toString() + "[";
if (!isDynamicallySized())
ret += getLength().str();
return ret + "]";
}
shared_ptr<ArrayType> ArrayType::copyForLocation(ArrayType::Location _location) const
{
return make_shared<ByteArrayType>(_location);
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
{
@ -653,7 +689,7 @@ MemberList const& StructType::getMembers() const
// We need to lazy-initialize it because of recursive references.
if (!m_members)
{
vector<pair<string, TypePointer>> members;
MemberList::MemberMap members;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
members.push_back(make_pair(variable->getName(), variable->getType()));
m_members.reset(new MemberList(members));
@ -833,10 +869,17 @@ string FunctionType::toString() const
unsigned FunctionType::getSizeOnStack() const
{
Location location = m_location;
if (m_location == Location::SetGas || m_location == Location::SetValue)
{
solAssert(m_returnParameterTypes.size() == 1, "");
location = dynamic_cast<FunctionType const&>(*m_returnParameterTypes.front()).m_location;
}
unsigned size = 0;
if (m_location == Location::External)
if (location == Location::External)
size = 2;
else if (m_location == Location::Internal || m_location == Location::Bare)
else if (location == Location::Internal || location == Location::Bare)
size = 1;
if (m_gasSet)
size++;
@ -1033,7 +1076,7 @@ MagicType::MagicType(MagicType::Kind _kind):
m_members = MemberList({{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
{"gas", make_shared<IntegerType>(256)},
{"value", make_shared<IntegerType>(256)},
{"data", make_shared<ByteArrayType>(ByteArrayType::Location::CallData)}});
{"data", make_shared<ArrayType>(ArrayType::Location::CallData)}});
break;
case Kind::Transaction:
m_members = MemberList({{"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},

44
libsolidity/Types.h

@ -36,8 +36,6 @@ namespace dev
namespace solidity
{
// @todo realMxN, dynamic strings, text, arrays
class Type; // forward
class FunctionType; // forward
using TypePointer = std::shared_ptr<Type const>;
@ -78,7 +76,7 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this<Type
public:
enum class Category
{
Integer, IntegerConstant, Bool, Real, ByteArray,
Integer, IntegerConstant, Bool, Real, Array,
String, Contract, Struct, Function, Enum,
Mapping, Void, TypeType, Modifier, Magic
};
@ -89,8 +87,8 @@ public:
static TypePointer fromElementaryTypeName(Token::Value _typeToken);
static TypePointer fromElementaryTypeName(std::string const& _name);
static TypePointer fromUserDefinedTypeName(UserDefinedTypeName const& _typeName);
static TypePointer fromMapping(Mapping const& _typeName);
static TypePointer fromFunction(FunctionDefinition const& _function);
static TypePointer fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType);
static TypePointer fromArrayTypeName(TypeName& _baseTypeName, Expression* _length);
/// @}
/// 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:
enum class Location { Storage, CallData, Memory };
virtual Category getCategory() const override { return Category::ByteArray; }
explicit ByteArrayType(Location _location): m_location(_location) {}
virtual Category getCategory() const override { return Category::Array; }
/// 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 TypePointer unaryOperatorResult(Token::Value _operator) 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 std::string toString() const override { return "bytes"; }
virtual MemberList const& getMembers() const override { return s_byteArrayMemberList; }
virtual std::string toString() const override;
virtual MemberList const& getMembers() const override { return s_arrayTypeMemberList; }
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
/// @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:
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
// are not allowed in argument lists
VariableDeclaration = TypeName Identifier
TypeName = ElementaryTypeName | Identifier | Mapping
TypeName = ElementaryTypeName | Identifier | Mapping | ArrayTypeName
Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')'
ArrayTypeName = TypeName '[' (Expression)? ']'
Block = '{' Statement* '}'
Statement = IfStatement | WhileStatement | Block |
@ -42,5 +43,5 @@ Assignment = Expression (AssignmentOp Expression)
FunctionCall = Expression '(' Expression ( ',' Expression )* ')'
NewExpression = 'new' Identifier
MemberAccess = Expression '.' Identifier
IndexAccess = Expression '[' Expresison ']'
IndexAccess = Expression '[' (Expresison)? ']'
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
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file FileIo.cpp
* @author Arkadiy Paronyan arkadiy@ethdev.com
@ -20,13 +20,21 @@
* Ethereum IDE client.
*/
#include <QDirIterator>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QTextStream>
#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"
using namespace dev;
using namespace dev::crypto;
using namespace dev::mix;
void FileIo::makeDir(QString const& _url)
@ -101,3 +109,72 @@ bool FileIo::fileExists(QString const& _url)
QFile file(url.path());
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
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file FileIo.h
* @author Arkadiy Paronyan arkadiy@ethdev.com
@ -22,6 +22,7 @@
#pragma once
#include <libdevcore/CommonData.h>
#include <QObject>
namespace dev
@ -52,6 +53,8 @@ public:
Q_INVOKABLE void moveFile(QString const& _sourceUrl, QString const& _destUrl);
/// Check if file exists
Q_INVOKABLE bool fileExists(QString const& _url);
/// Compress a folder, @returns sha3 of the compressed file.
Q_INVOKABLE QStringList makePackage(QString const& _deploymentFolder);
private:
QString getHomePath() const;

2
mix/QVariableDefinition.h

@ -53,6 +53,8 @@ public:
virtual bytes encodeValue() = 0;
/// Decode the return value @a _rawValue.
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:
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 documentSaved(var documentId)
signal deploymentStarted()
signal deploymentStepChanged(string message)
signal deploymentComplete()
signal deploymentError(string error)
@ -31,7 +32,7 @@ Item {
property string projectPath: ""
property string projectTitle: ""
property string currentDocumentId: ""
property string deploymentAddress: ""
property var deploymentAddresses: []
property var listModel: projectListModel
property var stateListModel: projectStateListModel.model
property CodeEditorView codeEditor: null
@ -55,6 +56,7 @@ Item {
function getDocumentIndex(documentId) { return ProjectModelCode.getDocumentIndex(documentId); }
function addExistingFiles(paths) { ProjectModelCode.doAddExistingFiles(paths); }
function deployProject() { ProjectModelCode.deployProject(false); }
function registerToUrlHint() { ProjectModelCode.registerToUrlHint(); }
Connections {
target: appContext
@ -91,15 +93,44 @@ Item {
MessageDialog {
id: deployWarningDialog
property bool redeploy
title: qsTr("Project")
text: qsTr("This project has been already deployed to the network. Do you want to re-deploy it?")
standardButtons: StandardButton.Ok | StandardButton.Cancel
text:
{
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
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 {
id: projectListModel
}

1
mix/qml/StatusPane.qml

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

1
mix/qml/Style.qml

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

2
mix/qml/TransactionDialog.qml

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

244
mix/qml/js/ProjectModel.js

@ -19,6 +19,7 @@
* @date 2015
* 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 contractTemplate = "contract Contract {\n}\n";
@ -45,7 +46,11 @@ function saveProject() {
var projectData = {
files: [],
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++)
projectData.files.push(projectListModel.get(i).fileName)
@ -63,11 +68,19 @@ function loadProject(path) {
var projectFile = path + projectFileName;
var json = fileIo.readFile(projectFile);
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) {
var parts = path.split("/");
projectData.title = parts[parts.length - 2];
}
deploymentAddress = projectData.deploymentAddress ? projectData.deploymentAddress : "";
deploymentAddresses = projectData.deploymentAddresses ? projectData.deploymentAddresses : [];
projectTitle = projectData.title;
projectPath = path;
if (!projectData.files)
@ -272,22 +285,27 @@ function generateFileName(name, extension) {
var jsonRpcRequestId = 1;
function deployProject(force) {
saveAll(); //TODO: ask user
deploymentDialog.open();
}
if (!force && deploymentAddress !== "") {
deployWarningDialog.visible = true;
function startDeployProject(erasePrevious)
{
var date = new Date();
var deploymentId = date.toLocaleString(Qt.locale(), "ddMMyyHHmmsszzz");
if (!erasePrevious)
{
finalizeDeployment(deploymentId, projectModel.deploymentAddresses);
return;
}
var date = new Date();
var deploymentId = date.toLocaleString(Qt.locale(), "ddMMyyHHmmsszzz");
var jsonRpcUrl = "http://127.0.0.1:8080";
var jsonRpcUrl = "http://127.0.0.1:8080";
console.log("Deploying " + deploymentId + " to " + jsonRpcUrl);
deploymentStarted();
var requests = [];
var requestNames = [];
for (var c in codeModel.contracts) { //TODO: order based on dependencies
var code = codeModel.contracts[c].codeHex;
requests.push({
@ -299,8 +317,8 @@ function deployProject(force) {
requestNames.push(c);
}
var rpcRequest = JSON.stringify(requests);;
var httpRequest = new XMLHttpRequest()
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);
@ -311,8 +329,8 @@ function deployProject(force) {
var rpcResponse = JSON.parse(httpRequest.responseText);
if (rpcResponse.length === requestNames.length) {
var contractAddresses = {};
for (var r = 0; r < rpcResponse.lenght; r++)
contractAddresses[requestNames[r]] = rpcResponse.result;
for (var r = 0; r < rpcResponse.length; r++)
contractAddresses[requestNames[r]] = rpcResponse[r].result;
finalizeDeployment(deploymentId, contractAddresses);
}
} else {
@ -326,7 +344,7 @@ function deployProject(force) {
}
function finalizeDeployment(deploymentId, addresses) {
//create a dir for frontend files and copy them
deploymentStepChanged(qsTr("Packaging application ..."));
var deploymentDir = projectPath + deploymentId + "/";
fileIo.makeDir(deploymentDir);
for (var i = 0; i < projectListModel.count; i++) {
@ -369,7 +387,203 @@ function finalizeDeployment(deploymentId, addresses) {
//copy scripts
fileIo.copyFile("qrc:///js/bignumber.min.js", deploymentDir + "bignumber.min.js");
fileIo.copyFile("qrc:///js/webthree.js", deploymentDir + "ethereum.js");
deploymentAddress = address;
deploymentAddresses = addresses;
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;
}
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/WebPreviewStyle.qml</file>
<file>qml/img/available_updates.png</file>
<file>qml/DeploymentDialog.qml</file>
</qresource>
</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 "./checkRandomTest.cpp")
if (NOT JSONRPC)
list(REMOVE_ITEM SRC_LIST "./AccountHolder.cpp")
endif()
include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
include_directories(BEFORE ..)
include_directories(${Boost_INCLUDE_DIRS})

56
test/SolidityEndToEndTest.cpp

@ -1669,7 +1669,6 @@ BOOST_AUTO_TEST_CASE(value_insane)
function test() { h = new helper(); }
function sendAmount(uint amount) returns (uint256 bal) {
var x1 = h.getBalance.value;
uint someStackElement = 20;
var x2 = x1(amount).gas;
var x3 = x2(1000).value;
return x3(amount + 3)();// overwrite value
@ -2570,6 +2569,61 @@ BOOST_AUTO_TEST_CASE(constructing_enums_from_ints)
BOOST_CHECK(callContractFunction("test()") == encodeArgs(1));
}
BOOST_AUTO_TEST_CASE(inline_member_init)
{
char const* sourceCode = R"(
contract test {
function test(){
m_b = 6;
m_c = 8;
}
uint m_a = 5;
uint m_b;
uint m_c = 7;
function get() returns (uint a, uint b, uint c){
a = m_a;
b = m_b;
c = m_c;
}
})";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("get()") == encodeArgs(5, 6, 8));
}
BOOST_AUTO_TEST_CASE(inline_member_init_inheritence)
{
char const* sourceCode = R"(
contract Base {
function Base(){}
uint m_base = 5;
function getBMember() returns (uint i) { return m_base; }
}
contract Derived is Base {
function Derived(){}
uint m_derived = 6;
function getDMember() returns (uint i) { return m_derived; }
})";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("getBMember()") == encodeArgs(5));
BOOST_CHECK(callContractFunction("getDMember()") == encodeArgs(6));
}
BOOST_AUTO_TEST_CASE(inline_member_init_inheritence_without_constructor)
{
char const* sourceCode = R"(
contract Base {
uint m_base = 5;
function getBMember() returns (uint i) { return m_base; }
}
contract Derived is Base {
uint m_derived = 6;
function getDMember() returns (uint i) { return m_derived; }
})";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("getBMember()") == encodeArgs(5));
BOOST_CHECK(callContractFunction("getDMember()") == encodeArgs(6));
}
BOOST_AUTO_TEST_CASE(external_function)
{
char const* sourceCode = R"(

21
test/SolidityNameAndTypeResolution.cpp

@ -470,7 +470,7 @@ BOOST_AUTO_TEST_CASE(illegal_override_indirect)
BOOST_AUTO_TEST_CASE(illegal_override_visibility)
{
char const* text = R"(
contract B { function f() inheritable {} }
contract B { function f() internal {} }
contract C is B { function f() public {} }
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
@ -706,7 +706,7 @@ BOOST_AUTO_TEST_CASE(private_state_variable)
" uint64(2);\n"
" }\n"
"uint256 private foo;\n"
"uint256 inheritable bar;\n"
"uint256 internal bar;\n"
"}\n";
ASTPointer<SourceUnit> source;
@ -717,7 +717,7 @@ BOOST_AUTO_TEST_CASE(private_state_variable)
function = retrieveFunctionBySignature(contract, "foo()");
BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of a private variable should not exist");
function = retrieveFunctionBySignature(contract, "bar()");
BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of an inheritable variable should not exist");
BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of an internal variable should not exist");
}
BOOST_AUTO_TEST_CASE(fallback_function)
@ -832,11 +832,11 @@ BOOST_AUTO_TEST_CASE(access_to_default_function_visibility)
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(access_to_inheritable_function)
BOOST_AUTO_TEST_CASE(access_to_internal_function)
{
char const* text = R"(
contract c {
function f() inheritable {}
function f() internal {}
}
contract d {
function g() { c(0).f(); }
@ -856,7 +856,7 @@ BOOST_AUTO_TEST_CASE(access_to_default_state_variable_visibility)
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(access_to_inheritable_state_variable)
BOOST_AUTO_TEST_CASE(access_to_internal_state_variable)
{
char const* text = R"(
contract c {
@ -1176,6 +1176,15 @@ BOOST_AUTO_TEST_CASE(test_for_bug_override_function_with_bytearray_type)
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()
}

57
test/SolidityParser.cpp

@ -480,6 +480,18 @@ BOOST_AUTO_TEST_CASE(statement_starting_with_type_conversion)
char const* text = "contract test {\n"
" function fun() {\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";
BOOST_CHECK_NO_THROW(parseText(text));
@ -651,13 +663,13 @@ BOOST_AUTO_TEST_CASE(visibility_specifiers)
char const* text = R"(
contract c {
uint private a;
uint inheritable b;
uint internal b;
uint public c;
uint d;
function f() {}
function f_priv() private {}
function f_public() public {}
function f_inheritable() inheritable {}
function f_internal() internal {}
})";
BOOST_CHECK_NO_THROW(parseText(text));
}
@ -666,7 +678,7 @@ BOOST_AUTO_TEST_CASE(multiple_visibility_specifiers)
{
char const* text = R"(
contract c {
uint private inheritable a;
uint private internal a;
})";
BOOST_CHECK_THROW(parseText(text), ParserError);
}
@ -753,6 +765,45 @@ BOOST_AUTO_TEST_CASE(external_variable)
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()
}

4
test/TestHelper.cpp

@ -475,11 +475,11 @@ void executeTests(const string& _name, const string& _testPathAppendix, std::fun
}
catch (Exception const& _e)
{
BOOST_ERROR("Failed test with Exception: " << diagnostic_information(_e));
BOOST_ERROR("Failed filling test with Exception: " << diagnostic_information(_e));
}
catch (std::exception const& _e)
{
BOOST_ERROR("Failed test with Exception: " << _e.what());
BOOST_ERROR("Failed filling test with Exception: " << _e.what());
}
break;
}

50
test/stSolidityTestFiller.json

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

7
test/transaction.cpp

@ -51,7 +51,7 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin)
catch(...)
{
BOOST_CHECK_MESSAGE(o.count("transaction") == 0, "A transaction object should not be defined because the RLP is invalid!");
return;
continue;
}
BOOST_REQUIRE(o.count("transaction") > 0);
@ -108,6 +108,11 @@ BOOST_AUTO_TEST_CASE(TransactionTest)
dev::test::executeTests("ttTransactionTest", "/TransactionTests", dev::test::doTransactionTests);
}
BOOST_AUTO_TEST_CASE(ttWrongRLPTransaction)
{
dev::test::executeTests("ttWrongRLPTransaction", "/TransactionTests", dev::test::doTransactionTests);
}
BOOST_AUTO_TEST_CASE(tt10mbDataField)
{
dev::test::executeTests("tt10mbDataField", "/TransactionTests", dev::test::doTransactionTests);

15
test/ttTransactionTestFiller.json

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

2
third/CMakeLists.txt

@ -51,5 +51,5 @@ target_link_libraries(${EXECUTABLE} web3jsonrpc)
target_link_libraries(${EXECUTABLE} jsqrc)
# eth_install_executable is defined in cmake/EthExecutableHelper.cmake
eth_install_executable(${EXECUTABLE})
eth_install_executable(${EXECUTABLE} DLLS ${MHD_DLL_RELEASE})

Loading…
Cancel
Save