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() endif()
# eth_install_executable is defined in cmake/EthExecutableHelper.cmake # 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 (extra_macro_args ${ARGN})
set (options) set (options)
set (one_value_args QMLDIR) 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}") cmake_parse_arguments (ETH_INSTALL_EXECUTABLE "${options}" "${one_value_args}" "${multi_value_args}" "${extra_macro_args}")
if (ETH_INSTALL_EXECUTABLE_QMLDIR) if (ETH_INSTALL_EXECUTABLE_QMLDIR)
@ -90,15 +90,29 @@ macro(eth_install_executable EXECUTABLE)
" COMPONENT RUNTIME ) " COMPONENT RUNTIME )
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 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}" get_target_property(TARGET_LIBS ${EXECUTABLE} INTERFACE_LINK_LIBRARIES)
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} string(REGEX MATCH "Qt5::Core" HAVE_QT ${TARGET_LIBS})
) if ("${HAVE_QT}" STREQUAL "Qt5::Core")
#workaround for https://bugreports.qt.io/browse/QTBUG-42083 add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
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}"
COMMAND cmd /C "(echo [Paths] & echo.Prefix=.)" > "qt.conf" WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} VERBATIM )
#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 install( TARGETS ${EXECUTABLE} RUNTIME
DESTINATION bin DESTINATION bin
COMPONENT ${EXECUTABLE} 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" # boost is using the same "hack" as us with "optimized" and "debug"
# official MHD project actually uses _d suffix # official MHD project actually uses _d suffix
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 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( find_library(
MHD_LIBRARY_DEBUG MHD_LIBRARY_DEBUG
NAMES microhttpd_d microhttpd-10_d libmicrohttpd_d libmicrohttpd-dll_d NAMES microhttpd_d microhttpd-10_d libmicrohttpd_d libmicrohttpd-dll_d
DOC "mhd debug library" DOC "mhd debug library"
) )
# always use release for now
set(MHD_LIBRARIES optimized ${MHD_LIBRARIES} debug ${MHD_LIBRARY_DEBUG}) #string(REPLACE ".lib" ".dll" MHD_DLL_DEBUG ${MHD_LIBRARY_DEBUG})
#set(MHD_LIBRARIES optimized ${MHD_LIBRARIES} debug ${MHD_LIBRARY_DEBUG})
endif() endif()

4
eth/CMakeLists.txt

@ -28,5 +28,9 @@ endif()
target_link_libraries(${EXECUTABLE} webthree) target_link_libraries(${EXECUTABLE} webthree)
target_link_libraries(${EXECUTABLE} secp256k1) 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 ) install( TARGETS ${EXECUTABLE} DESTINATION bin )

1
evmjit/libevmjit/ExecStats.h

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

2
evmjit/libevmjit/preprocessor/llvm_includes_end.h

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

4
evmjit/libevmjit/preprocessor/llvm_includes_start.h

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

2
extdep/CMakeLists.txt

@ -7,7 +7,7 @@ include(eth_download.cmake)
# all dependencies will be installed into this directory, separated by platform # all dependencies will be installed into this directory, separated by platform
string(TOLOWER ${CMAKE_SYSTEM_NAME} _system_name) string(TOLOWER ${CMAKE_SYSTEM_NAME} _system_name)
set(ETH_DEPENDENCY_INSTALL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/install/${_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}/lib)
file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/include) file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/include)
file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/bin) file(MAKE_DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/bin)

4
libdevcore/RLP.h

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

2
libethereum/Transaction.cpp

@ -40,7 +40,7 @@ Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig)
m_gasPrice = rlp[field = 1].toInt<u256>(); m_gasPrice = rlp[field = 1].toInt<u256>();
m_gas = rlp[field = 2].toInt<u256>(); m_gas = rlp[field = 2].toInt<u256>();
m_type = rlp[field = 3].isEmpty() ? ContractCreation : MessageCall; 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_value = rlp[field = 4].toInt<u256>();
m_data = rlp[field = 5].toBytes(); m_data = rlp[field = 5].toBytes();
byte v = rlp[field = 6].toInt<byte>() - 27; 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()) for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
function->checkTypeRequirements(); function->checkTypeRequirements();
for (ASTPointer<VariableDeclaration> const& variable: m_stateVariables)
variable->checkTypeRequirements();
// check for hash collisions in function signatures // check for hash collisions in function signatures
set<FixedHash<4>> hashes; set<FixedHash<4>> hashes;
for (auto const& it: getInterfaceFunctionList()) for (auto const& it: getInterfaceFunctionList())
@ -294,6 +297,12 @@ bool VariableDeclaration::isLValue() const
return !isExternalFunctionParameter(); return !isExternalFunctionParameter();
} }
void VariableDeclaration::checkTypeRequirements()
{
if (m_value)
m_value->checkTypeRequirements();
}
bool VariableDeclaration::isExternalFunctionParameter() const bool VariableDeclaration::isExternalFunctionParameter() const
{ {
auto const* function = dynamic_cast<FunctionDefinition const*>(getScope()); auto const* function = dynamic_cast<FunctionDefinition const*>(getScope());
@ -390,26 +399,26 @@ void Return::checkTypeRequirements()
m_expression->expectType(*m_returnParameters->getParameters().front()->getType()); 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 // Variables can be declared without type (with "var"), in which case the first assignment
// sets the type. // sets the type.
// Note that assignments before the first declaration are legal because of the special scoping // Note that assignments before the first declaration are legal because of the special scoping
// rules inherited from JavaScript. // rules inherited from JavaScript.
if (m_value) if (m_variable->getValue())
{ {
if (m_variable->getType()) if (m_variable->getType())
m_value->expectType(*m_variable->getType()); m_variable->getValue()->expectType(*m_variable->getType());
else else
{ {
// no type declared and no previous assignment, infer the type // no type declared and no previous assignment, infer the type
m_value->checkTypeRequirements(); m_variable->getValue()->checkTypeRequirements();
TypePointer type = m_value->getType(); TypePointer type = m_variable->getValue()->getType();
if (type->getCategory() == Type::Category::IntegerConstant) if (type->getCategory() == Type::Category::IntegerConstant)
{ {
auto intType = dynamic_pointer_cast<IntegerConstantType const>(type)->getIntegerType(); auto intType = dynamic_pointer_cast<IntegerConstantType const>(type)->getIntegerType();
if (!intType) 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; type = intType;
} }
else if (type->getCategory() == Type::Category::Void) else if (type->getCategory() == Type::Category::Void)
@ -418,7 +427,6 @@ void VariableDefinition::checkTypeRequirements()
} }
} }
} }
void Assignment::checkTypeRequirements() void Assignment::checkTypeRequirements()
{ {
m_leftHandSide->checkTypeRequirements(); m_leftHandSide->checkTypeRequirements();
@ -601,13 +609,48 @@ void MemberAccess::checkTypeRequirements()
void IndexAccess::checkTypeRequirements() void IndexAccess::checkTypeRequirements()
{ {
m_base->checkTypeRequirements(); m_base->checkTypeRequirements();
if (m_base->getType()->getCategory() != Type::Category::Mapping) switch (m_base->getType()->getCategory())
BOOST_THROW_EXCEPTION(m_base->createTypeError("Indexed expression has to be a mapping (is " + {
m_base->getType()->toString() + ")")); case Type::Category::Array:
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType()); {
m_index->expectType(*type.getKeyType()); ArrayType const& type = dynamic_cast<ArrayType const&>(*m_base->getType());
m_type = type.getValueType(); if (!m_index)
m_isLValue = true; BOOST_THROW_EXCEPTION(createTypeError("Index expression cannot be omitted."));
m_index->expectType(IntegerType(256));
m_type = type.getBaseType();
m_isLValue = true;
break;
}
case Type::Category::Mapping:
{
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType());
if (!m_index)
BOOST_THROW_EXCEPTION(createTypeError("Index expression cannot be omitted."));
m_index->expectType(*type.getKeyType());
m_type = type.getValueType();
m_isLValue = true;
break;
}
case Type::Category::TypeType:
{
TypeType const& type = dynamic_cast<TypeType const&>(*m_base->getType());
if (!m_index)
m_type = make_shared<TypeType>(make_shared<ArrayType>(ArrayType::Location::Memory, type.getActualType()));
else
{
m_index->checkTypeRequirements();
auto length = dynamic_cast<IntegerConstantType const*>(m_index->getType().get());
if (!length)
BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected."));
m_type = make_shared<TypeType>(make_shared<ArrayType>(
ArrayType::Location::Memory, type.getActualType(), length->literalValue(nullptr)));
}
break;
}
default:
BOOST_THROW_EXCEPTION(m_base->createTypeError(
"Indexed expression has to be a type, mapping or array (is " + m_base->getType()->toString() + ")"));
}
} }
void Identifier::checkTypeRequirements() void Identifier::checkTypeRequirements()

64
libsolidity/AST.h

@ -133,7 +133,7 @@ class Declaration: public ASTNode
{ {
public: public:
/// Visibility ordered from restricted to unrestricted. /// 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, Declaration(Location const& _location, ASTPointer<ASTString> const& _name,
Visibility _visibility = Visibility::Default): Visibility _visibility = Visibility::Default):
@ -144,7 +144,7 @@ public:
Visibility getVisibility() const { return m_visibility == Visibility::Default ? getDefaultVisibility() : m_visibility; } Visibility getVisibility() const { return m_visibility == Visibility::Default ? getDefaultVisibility() : m_visibility; }
bool isPublic() const { return getVisibility() >= Visibility::Public; } bool isPublic() const { return getVisibility() >= Visibility::Public; }
bool isVisibleInContract() const { return getVisibility() != Visibility::External; } 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. /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
/// Available only after name and type resolution step. /// Available only after name and type resolution step.
@ -432,14 +432,17 @@ class VariableDeclaration: public Declaration
{ {
public: public:
VariableDeclaration(Location const& _location, ASTPointer<TypeName> const& _type, VariableDeclaration(Location const& _location, ASTPointer<TypeName> const& _type,
ASTPointer<ASTString> const& _name, Visibility _visibility, ASTPointer<ASTString> const& _name, ASTPointer<Expression> _value,
bool _isStateVar = false, bool _isIndexed = false): Visibility _visibility,
Declaration(_location, _name, _visibility), m_typeName(_type), bool _isStateVar = false, bool _isIndexed = false):
m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed) {} 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(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
TypeName const* getTypeName() const { return m_typeName.get(); } TypeName* getTypeName() { return m_typeName.get(); }
ASTPointer<Expression> const& getValue() const { return m_value; }
/// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly /// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly
/// declared and there is no assignment to the variable that fixes the type. /// 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; } void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
virtual bool isLValue() const override; virtual bool isLValue() const override;
/// Calls checkTypeRequirments for all state variables.
void checkTypeRequirements();
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); } bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
bool isExternalFunctionParameter() const; bool isExternalFunctionParameter() const;
bool isStateVariable() const { return m_isStateVariable; } bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; } bool isIndexed() const { return m_isIndexed; }
protected: protected:
Visibility getDefaultVisibility() const override { return Visibility::Inheritable; } Visibility getDefaultVisibility() const override { return Visibility::Internal; }
private: private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var") 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_isStateVariable; ///< Whether or not this is a contract state variable
bool m_isIndexed; ///< Whether this is an indexed variable (used by events). 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 /// Retrieve the element of the type hierarchy this node refers to. Can return an empty shared
/// pointer until the types have been resolved using the @ref NameAndTypeResolver. /// pointer until the types have been resolved using the @ref NameAndTypeResolver.
/// If it returns an empty shared pointer after that, this indicates that the type was not found. /// If it returns an empty shared pointer after that, this indicates that the type was not found.
virtual std::shared_ptr<Type const> toType() const = 0; virtual std::shared_ptr<Type const> toType() = 0;
}; };
/** /**
@ -598,7 +605,7 @@ public:
} }
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual std::shared_ptr<Type const> toType() const override { return Type::fromElementaryTypeName(m_type); } virtual std::shared_ptr<Type const> toType() override { return Type::fromElementaryTypeName(m_type); }
Token::Value getTypeName() const { return m_type; } Token::Value getTypeName() const { return m_type; }
@ -616,7 +623,7 @@ public:
TypeName(_location), m_name(_name), m_referencedDeclaration(nullptr) {} TypeName(_location), m_name(_name), m_referencedDeclaration(nullptr) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual std::shared_ptr<Type const> toType() const override { return Type::fromUserDefinedTypeName(*this); } virtual std::shared_ptr<Type const> toType() override { return Type::fromUserDefinedTypeName(*this); }
ASTString const& getName() const { return *m_name; } ASTString const& getName() const { return *m_name; }
void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
@ -639,7 +646,7 @@ public:
TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual std::shared_ptr<Type const> toType() const override { return Type::fromMapping(*this); } virtual TypePointer toType() override { return Type::fromMapping(*m_keyType, *m_valueType); }
ElementaryTypeName const& getKeyType() const { return *m_keyType; } ElementaryTypeName const& getKeyType() const { return *m_keyType; }
TypeName const& getValueType() const { return *m_valueType; } TypeName const& getValueType() const { return *m_valueType; }
@ -649,6 +656,27 @@ private:
ASTPointer<TypeName> m_valueType; ASTPointer<TypeName> m_valueType;
}; };
/**
* An array type, can be "typename[]" or "typename[<expression>]".
*/
class ArrayTypeName: public TypeName
{
public:
ArrayTypeName(Location const& _location, ASTPointer<TypeName> const& _baseType,
ASTPointer<Expression> const& _length):
TypeName(_location), m_baseType(_baseType), m_length(_length) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual std::shared_ptr<Type const> toType() override { return Type::fromArrayTypeName(*m_baseType, m_length.get()); }
TypeName const& getBaseType() const { return *m_baseType; }
Expression const* getLength() const { return m_length.get(); }
private:
ASTPointer<TypeName> m_baseType;
ASTPointer<Expression> m_length; ///< Length of the array, might be empty.
};
/// @} /// @}
/// Statements /// Statements
@ -833,22 +861,20 @@ private:
* also be "var") but the actual assignment can be missing. * also be "var") but the actual assignment can be missing.
* Examples: var a = 2; uint256 a; * Examples: var a = 2; uint256 a;
*/ */
class VariableDefinition: public Statement class VariableDeclarationStatement: public Statement
{ {
public: public:
VariableDefinition(Location const& _location, ASTPointer<VariableDeclaration> _variable, VariableDeclarationStatement(Location const& _location, ASTPointer<VariableDeclaration> _variable):
ASTPointer<Expression> _value): Statement(_location), m_variable(_variable) {}
Statement(_location), m_variable(_variable), m_value(_value) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
VariableDeclaration const& getDeclaration() const { return *m_variable; } 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: private:
ASTPointer<VariableDeclaration> m_variable; ASTPointer<VariableDeclaration> m_variable;
ASTPointer<Expression> m_value; ///< the assigned value, can be missing
}; };
/** /**
@ -1076,7 +1102,7 @@ public:
virtual void checkTypeRequirements() override; virtual void checkTypeRequirements() override;
Expression const& getBaseExpression() const { return *m_base; } Expression const& getBaseExpression() const { return *m_base; }
Expression const& getIndexExpression() const { return *m_index; } Expression const* getIndexExpression() const { return m_index.get(); }
private: private:
ASTPointer<Expression> m_base; ASTPointer<Expression> m_base;

3
libsolidity/ASTForward.h

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

4
libsolidity/ASTJsonConverter.cpp

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

4
libsolidity/ASTJsonConverter.h

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

18
libsolidity/ASTPrinter.cpp

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

6
libsolidity/ASTPrinter.h

@ -58,6 +58,7 @@ public:
bool visit(ElementaryTypeName const& _node) override; bool visit(ElementaryTypeName const& _node) override;
bool visit(UserDefinedTypeName const& _node) override; bool visit(UserDefinedTypeName const& _node) override;
bool visit(Mapping const& _node) override; bool visit(Mapping const& _node) override;
bool visit(ArrayTypeName const& _node) override;
bool visit(Statement const& _node) override; bool visit(Statement const& _node) override;
bool visit(Block const& _node) override; bool visit(Block const& _node) override;
bool visit(PlaceholderStatement const& _node) override; bool visit(PlaceholderStatement const& _node) override;
@ -68,7 +69,7 @@ public:
bool visit(Continue const& _node) override; bool visit(Continue const& _node) override;
bool visit(Break const& _node) override; bool visit(Break const& _node) override;
bool visit(Return 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(ExpressionStatement const& _node) override;
bool visit(Expression const& _node) override; bool visit(Expression const& _node) override;
bool visit(Assignment const& _node) override; bool visit(Assignment const& _node) override;
@ -99,6 +100,7 @@ public:
void endVisit(ElementaryTypeName const&) override; void endVisit(ElementaryTypeName const&) override;
void endVisit(UserDefinedTypeName const&) override; void endVisit(UserDefinedTypeName const&) override;
void endVisit(Mapping const&) override; void endVisit(Mapping const&) override;
void endVisit(ArrayTypeName const&) override;
void endVisit(Statement const&) override; void endVisit(Statement const&) override;
void endVisit(Block const&) override; void endVisit(Block const&) override;
void endVisit(PlaceholderStatement const&) override; void endVisit(PlaceholderStatement const&) override;
@ -109,7 +111,7 @@ public:
void endVisit(Continue const&) override; void endVisit(Continue const&) override;
void endVisit(Break const&) override; void endVisit(Break const&) override;
void endVisit(Return const&) override; void endVisit(Return const&) override;
void endVisit(VariableDefinition const&) override; void endVisit(VariableDeclarationStatement const&) override;
void endVisit(ExpressionStatement const&) override; void endVisit(ExpressionStatement const&) override;
void endVisit(Expression const&) override; void endVisit(Expression const&) override;
void endVisit(Assignment 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(ElementaryTypeName&) { return true; }
virtual bool visit(UserDefinedTypeName&) { return true; } virtual bool visit(UserDefinedTypeName&) { return true; }
virtual bool visit(Mapping&) { return true; } virtual bool visit(Mapping&) { return true; }
virtual bool visit(ArrayTypeName&) { return true; }
virtual bool visit(Statement&) { return true; } virtual bool visit(Statement&) { return true; }
virtual bool visit(Block&) { return true; } virtual bool visit(Block&) { return true; }
virtual bool visit(PlaceholderStatement&) { return true; } virtual bool visit(PlaceholderStatement&) { return true; }
@ -69,7 +70,7 @@ public:
virtual bool visit(Continue&) { return true; } virtual bool visit(Continue&) { return true; }
virtual bool visit(Break&) { return true; } virtual bool visit(Break&) { return true; }
virtual bool visit(Return&) { 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(ExpressionStatement&) { return true; }
virtual bool visit(Expression&) { return true; } virtual bool visit(Expression&) { return true; }
virtual bool visit(Assignment&) { return true; } virtual bool visit(Assignment&) { return true; }
@ -102,6 +103,7 @@ public:
virtual void endVisit(ElementaryTypeName&) { } virtual void endVisit(ElementaryTypeName&) { }
virtual void endVisit(UserDefinedTypeName&) { } virtual void endVisit(UserDefinedTypeName&) { }
virtual void endVisit(Mapping&) { } virtual void endVisit(Mapping&) { }
virtual void endVisit(ArrayTypeName&) { }
virtual void endVisit(Statement&) { } virtual void endVisit(Statement&) { }
virtual void endVisit(Block&) { } virtual void endVisit(Block&) { }
virtual void endVisit(PlaceholderStatement&) { } virtual void endVisit(PlaceholderStatement&) { }
@ -112,7 +114,7 @@ public:
virtual void endVisit(Continue&) { } virtual void endVisit(Continue&) { }
virtual void endVisit(Break&) { } virtual void endVisit(Break&) { }
virtual void endVisit(Return&) { } virtual void endVisit(Return&) { }
virtual void endVisit(VariableDefinition&) { } virtual void endVisit(VariableDeclarationStatement&) { }
virtual void endVisit(ExpressionStatement&) { } virtual void endVisit(ExpressionStatement&) { }
virtual void endVisit(Expression&) { } virtual void endVisit(Expression&) { }
virtual void endVisit(Assignment&) { } virtual void endVisit(Assignment&) { }
@ -149,6 +151,7 @@ public:
virtual bool visit(ElementaryTypeName const&) { return true; } virtual bool visit(ElementaryTypeName const&) { return true; }
virtual bool visit(UserDefinedTypeName const&) { return true; } virtual bool visit(UserDefinedTypeName const&) { return true; }
virtual bool visit(Mapping const&) { return true; } virtual bool visit(Mapping const&) { return true; }
virtual bool visit(ArrayTypeName const&) { return true; }
virtual bool visit(Statement const&) { return true; } virtual bool visit(Statement const&) { return true; }
virtual bool visit(Block const&) { return true; } virtual bool visit(Block const&) { return true; }
virtual bool visit(PlaceholderStatement const&) { return true; } virtual bool visit(PlaceholderStatement const&) { return true; }
@ -159,7 +162,7 @@ public:
virtual bool visit(Continue const&) { return true; } virtual bool visit(Continue const&) { return true; }
virtual bool visit(Break const&) { return true; } virtual bool visit(Break const&) { return true; }
virtual bool visit(Return 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(ExpressionStatement const&) { return true; }
virtual bool visit(Expression const&) { return true; } virtual bool visit(Expression const&) { return true; }
virtual bool visit(Assignment const&) { return true; } virtual bool visit(Assignment const&) { return true; }
@ -192,6 +195,7 @@ public:
virtual void endVisit(ElementaryTypeName const&) { } virtual void endVisit(ElementaryTypeName const&) { }
virtual void endVisit(UserDefinedTypeName const&) { } virtual void endVisit(UserDefinedTypeName const&) { }
virtual void endVisit(Mapping const&) { } virtual void endVisit(Mapping const&) { }
virtual void endVisit(ArrayTypeName const&) { }
virtual void endVisit(Statement const&) { } virtual void endVisit(Statement const&) { }
virtual void endVisit(Block const&) { } virtual void endVisit(Block const&) { }
virtual void endVisit(PlaceholderStatement const&) { } virtual void endVisit(PlaceholderStatement const&) { }
@ -202,7 +206,7 @@ public:
virtual void endVisit(Continue const&) { } virtual void endVisit(Continue const&) { }
virtual void endVisit(Break const&) { } virtual void endVisit(Break const&) { }
virtual void endVisit(Return const&) { } virtual void endVisit(Return const&) { }
virtual void endVisit(VariableDefinition const&) { } virtual void endVisit(VariableDeclarationStatement const&) { }
virtual void endVisit(ExpressionStatement const&) { } virtual void endVisit(ExpressionStatement const&) { }
virtual void endVisit(Expression const&) { } virtual void endVisit(Expression const&) { }
virtual void endVisit(Assignment 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) void VariableDeclaration::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{
if (m_typeName) if (m_typeName)
m_typeName->accept(_visitor); m_typeName->accept(_visitor);
if (m_value)
m_value->accept(_visitor);
}
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
void VariableDeclaration::accept(ASTConstVisitor& _visitor) const void VariableDeclaration::accept(ASTConstVisitor& _visitor) const
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{
if (m_typeName) if (m_typeName)
m_typeName->accept(_visitor); m_typeName->accept(_visitor);
if (m_value)
m_value->accept(_visitor);
}
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
@ -319,6 +327,28 @@ void Mapping::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
void ArrayTypeName::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_baseType->accept(_visitor);
if (m_length)
m_length->accept(_visitor);
}
_visitor.endVisit(*this);
}
void ArrayTypeName::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_baseType->accept(_visitor);
if (m_length)
m_length->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Block::accept(ASTVisitor& _visitor) void Block::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
@ -475,25 +505,17 @@ void ExpressionStatement::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
void VariableDefinition::accept(ASTVisitor& _visitor) void VariableDeclarationStatement::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{
m_variable->accept(_visitor); m_variable->accept(_visitor);
if (m_value)
m_value->accept(_visitor);
}
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
void VariableDefinition::accept(ASTConstVisitor& _visitor) const void VariableDeclarationStatement::accept(ASTConstVisitor& _visitor) const
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{
m_variable->accept(_visitor); m_variable->accept(_visitor);
if (m_value)
m_value->accept(_visitor);
}
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
@ -604,7 +626,8 @@ void IndexAccess::accept(ASTVisitor& _visitor)
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{ {
m_base->accept(_visitor); m_base->accept(_visitor);
m_index->accept(_visitor); if (m_index)
m_index->accept(_visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
@ -614,7 +637,8 @@ void IndexAccess::accept(ASTConstVisitor& _visitor) const
if (_visitor.visit(*this)) if (_visitor.visit(*this))
{ {
m_base->accept(_visitor); m_base->accept(_visitor);
m_index->accept(_visitor); if (m_index)
m_index->accept(_visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }

51
libsolidity/Compiler.cpp

@ -34,6 +34,20 @@ using namespace std;
namespace dev { namespace dev {
namespace solidity { 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, void Compiler::compileContract(ContractDefinition const& _contract,
map<ContractDefinition const*, bytes const*> const& _contracts) 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()) for (ASTPointer<InheritanceSpecifier> const& base: contract->getBaseContracts())
{ {
ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>( ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>(
base->getName()->getReferencedDeclaration()); base->getName()->getReferencedDeclaration());
solAssert(baseContract, ""); solAssert(baseContract, "");
if (baseArguments.count(baseContract) == 0) if (baseArguments.count(baseContract) == 0)
baseArguments[baseContract] = &base->getArguments(); baseArguments[baseContract] = &base->getArguments();
@ -85,12 +99,14 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp
{ {
ContractDefinition const* base = bases[bases.size() - i]; ContractDefinition const* base = bases[bases.size() - i];
solAssert(base, ""); solAssert(base, "");
initializeStateVariables(*base);
FunctionDefinition const* baseConstructor = base->getConstructor(); FunctionDefinition const* baseConstructor = base->getConstructor();
if (!baseConstructor) if (!baseConstructor)
continue; continue;
solAssert(baseArguments[base], ""); solAssert(baseArguments[base], "");
appendBaseConstructorCall(*baseConstructor, *baseArguments[base]); appendBaseConstructorCall(*baseConstructor, *baseArguments[base]);
} }
initializeStateVariables(_contract);
if (_contract.getConstructor()) if (_contract.getConstructor())
appendConstructorCall(*_contract.getConstructor()); appendConstructorCall(*_contract.getConstructor());
@ -247,6 +263,13 @@ void Compiler::registerStateVariables(ContractDefinition const& _contract)
m_context.addStateVariable(*variable); 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) bool Compiler::visit(VariableDeclaration const& _variableDeclaration)
{ {
solAssert(_variableDeclaration.isStateVariable(), "Compiler visit to non-state variable declaration."); 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) bool Compiler::visit(IfStatement const& _ifStatement)
{ {
StackHeightChecker checker(m_context);
compileExpression(_ifStatement.getCondition()); compileExpression(_ifStatement.getCondition());
eth::AssemblyItem trueTag = m_context.appendConditionalJump(); eth::AssemblyItem trueTag = m_context.appendConditionalJump();
if (_ifStatement.getFalseStatement()) if (_ifStatement.getFalseStatement())
@ -339,11 +364,15 @@ bool Compiler::visit(IfStatement const& _ifStatement)
m_context << trueTag; m_context << trueTag;
_ifStatement.getTrueStatement().accept(*this); _ifStatement.getTrueStatement().accept(*this);
m_context << endTag; m_context << endTag;
checker.check();
return false; return false;
} }
bool Compiler::visit(WhileStatement const& _whileStatement) bool Compiler::visit(WhileStatement const& _whileStatement)
{ {
StackHeightChecker checker(m_context);
eth::AssemblyItem loopStart = m_context.newTag(); eth::AssemblyItem loopStart = m_context.newTag();
eth::AssemblyItem loopEnd = m_context.newTag(); eth::AssemblyItem loopEnd = m_context.newTag();
m_continueTags.push_back(loopStart); m_continueTags.push_back(loopStart);
@ -361,11 +390,15 @@ bool Compiler::visit(WhileStatement const& _whileStatement)
m_continueTags.pop_back(); m_continueTags.pop_back();
m_breakTags.pop_back(); m_breakTags.pop_back();
checker.check();
return false; return false;
} }
bool Compiler::visit(ForStatement const& _forStatement) bool Compiler::visit(ForStatement const& _forStatement)
{ {
StackHeightChecker checker(m_context);
eth::AssemblyItem loopStart = m_context.newTag(); eth::AssemblyItem loopStart = m_context.newTag();
eth::AssemblyItem loopEnd = m_context.newTag(); eth::AssemblyItem loopEnd = m_context.newTag();
m_continueTags.push_back(loopStart); m_continueTags.push_back(loopStart);
@ -395,6 +428,8 @@ bool Compiler::visit(ForStatement const& _forStatement)
m_continueTags.pop_back(); m_continueTags.pop_back();
m_breakTags.pop_back(); m_breakTags.pop_back();
checker.check();
return false; return false;
} }
@ -429,29 +464,35 @@ bool Compiler::visit(Return const& _return)
return false; 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()); compileExpression(*expression, _variableDeclarationStatement.getDeclaration().getType());
CompilerUtils(m_context).moveToStackVariable(_variableDefinition.getDeclaration()); CompilerUtils(m_context).moveToStackVariable(_variableDeclarationStatement.getDeclaration());
} }
checker.check();
return false; return false;
} }
bool Compiler::visit(ExpressionStatement const& _expressionStatement) bool Compiler::visit(ExpressionStatement const& _expressionStatement)
{ {
StackHeightChecker checker(m_context);
Expression const& expression = _expressionStatement.getExpression(); Expression const& expression = _expressionStatement.getExpression();
compileExpression(expression); compileExpression(expression);
CompilerUtils(m_context).popStackElement(*expression.getType()); CompilerUtils(m_context).popStackElement(*expression.getType());
checker.check();
return false; return false;
} }
bool Compiler::visit(PlaceholderStatement const&) bool Compiler::visit(PlaceholderStatement const&)
{ {
StackHeightChecker checker(m_context);
++m_modifierDepth; ++m_modifierDepth;
appendModifierOrFunctionCode(); appendModifierOrFunctionCode();
--m_modifierDepth; --m_modifierDepth;
checker.check();
return true; return true;
} }

3
libsolidity/Compiler.h

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

2
libsolidity/CompilerContext.h

@ -48,7 +48,7 @@ public:
bytes const& getCompiledContract(ContractDefinition const& _contract) const; bytes const& getCompiledContract(ContractDefinition const& _contract) const;
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); } 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 isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration) != 0; }
bool isLocalVariable(Declaration const* _declaration) const; bool isLocalVariable(Declaration const* _declaration) const;

63
libsolidity/CompilerStack.cpp

@ -58,21 +58,22 @@ CompilerStack::CompilerStack(bool _addStandardSources):
m_addStandardSources(_addStandardSources), m_parseSuccessful(false) m_addStandardSources(_addStandardSources), m_parseSuccessful(false)
{ {
if (m_addStandardSources) 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; bool existed = m_sources.count(_name) != 0;
reset(true); 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; return existed;
} }
void CompilerStack::setSource(string const& _sourceCode) void CompilerStack::setSource(string const& _sourceCode)
{ {
reset(); reset();
addSource("", expanded(_sourceCode)); addSource("", _sourceCode);
} }
void CompilerStack::parse() void CompilerStack::parse()
@ -126,57 +127,6 @@ vector<string> CompilerStack::getContractNames() const
return contractNames; 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) void CompilerStack::compile(bool _optimize)
{ {
@ -328,7 +278,8 @@ void CompilerStack::resolveImports()
}; };
for (auto const& sourcePair: m_sources) for (auto const& sourcePair: m_sources)
toposort(&sourcePair.second); if (!sourcePair.second.isLibrary)
toposort(&sourcePair.second);
swap(m_sourceOrder, sourceOrder); swap(m_sourceOrder, sourceOrder);
} }

13
libsolidity/CompilerStack.h

@ -60,12 +60,12 @@ class CompilerStack: boost::noncopyable
{ {
public: public:
/// Creates a new compiler stack. Adds standard sources if @a _addStandardSources. /// Creates a new compiler stack. Adds standard sources if @a _addStandardSources.
explicit CompilerStack(bool _addStandardSources = false); explicit CompilerStack(bool _addStandardSources = true);
/// Adds a source object (e.g. file) to the parser. After this, parse has to be called again. /// Adds a source object (e.g. file) to the parser. After this, parse has to be called again.
/// @returns true if a source object by the name already existed and was replaced. /// @returns true if a source object by the name already existed and was replaced.
void addSources(std::map<std::string, std::string> const& _nameContents) { for (auto const& i: _nameContents) addSource(i.first, i.second); } 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 addSource(std::string const& _name, std::string const& _content, bool _isLibrary = false);
void setSource(std::string const& _sourceCode); void setSource(std::string const& _sourceCode);
/// Parses all source units that were added /// Parses all source units that were added
void parse(); void parse();
@ -125,7 +125,8 @@ private:
std::shared_ptr<Scanner> scanner; std::shared_ptr<Scanner> scanner;
std::shared_ptr<SourceUnit> ast; std::shared_ptr<SourceUnit> ast;
std::string interface; 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 struct Contract
@ -143,10 +144,6 @@ private:
Contract(); 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 reset(bool _keepSources = false);
void resolveImports(); void resolveImports();

32
libsolidity/CompilerUtils.cpp

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

4
libsolidity/CompilerUtils.h

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

59
libsolidity/ExpressionCompiler.cpp

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

15
libsolidity/ExpressionCompiler.h

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

12
libsolidity/NameAndTypeResolver.cpp

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

2
libsolidity/NameAndTypeResolver.h

@ -105,7 +105,7 @@ private:
void endVisit(FunctionDefinition& _function) override; void endVisit(FunctionDefinition& _function) override;
bool visit(ModifierDefinition& _modifier) override; bool visit(ModifierDefinition& _modifier) override;
void endVisit(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(VariableDeclaration& _declaration) override;
bool visit(EventDefinition& _event) override; bool visit(EventDefinition& _event) override;
void endVisit(EventDefinition& _event) override; void endVisit(EventDefinition& _event) override;

235
libsolidity/Parser.cpp

@ -41,8 +41,11 @@ class Parser::ASTNodeFactory
public: public:
ASTNodeFactory(Parser const& _parser): ASTNodeFactory(Parser const& _parser):
m_parser(_parser), m_location(_parser.getPosition(), -1, _parser.getSourceName()) {} m_parser(_parser), m_location(_parser.getPosition(), -1, _parser.getSourceName()) {}
ASTNodeFactory(Parser const& _parser, ASTPointer<ASTNode> const& _childNode):
m_parser(_parser), m_location(_childNode->getLocation()) {}
void markEndPosition() { m_location.end = m_parser.getEndPosition(); } void markEndPosition() { m_location.end = m_parser.getEndPosition(); }
void setLocation(Location const& _location) { m_location = _location; }
void setLocationEmpty() { m_location.end = m_location.start; } void setLocationEmpty() { m_location.end = m_location.start; }
/// Set the end position to the one of the given node. /// Set the end position to the one of the given node.
void setEndPositionFromNode(ASTPointer<ASTNode> const& _node) { m_location.end = _node->getLocation().end; } void setEndPositionFromNode(ASTPointer<ASTNode> const& _node) { m_location.end = _node->getLocation().end; }
@ -148,6 +151,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
{ {
VarDeclParserOptions options; VarDeclParserOptions options;
options.isStateVariable = true; options.isStateVariable = true;
options.allowInitialValue = true;
stateVariables.push_back(parseVariableDeclaration(options)); stateVariables.push_back(parseVariableDeclaration(options));
expectToken(Token::Semicolon); expectToken(Token::Semicolon);
} }
@ -186,8 +190,8 @@ Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token)
Declaration::Visibility visibility(Declaration::Visibility::Default); Declaration::Visibility visibility(Declaration::Visibility::Default);
if (_token == Token::Public) if (_token == Token::Public)
visibility = Declaration::Visibility::Public; visibility = Declaration::Visibility::Public;
else if (_token == Token::Inheritable) else if (_token == Token::Internal)
visibility = Declaration::Visibility::Inheritable; visibility = Declaration::Visibility::Internal;
else if (_token == Token::Private) else if (_token == Token::Private)
visibility = Declaration::Visibility::Private; visibility = Declaration::Visibility::Private;
else if (_token == Token::External) else if (_token == Token::External)
@ -298,12 +302,20 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
return nodeFactory.createNode<EnumDefinition>(name, members); return nodeFactory.createNode<EnumDefinition>(name, members);
} }
ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(VarDeclParserOptions const& _options) ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
VarDeclParserOptions const& _options, ASTPointer<TypeName> const& _lookAheadArrayType)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory = _lookAheadArrayType ?
ASTPointer<TypeName> type = parseTypeName(_options.allowVar); ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this);
if (type != nullptr) ASTPointer<TypeName> type;
nodeFactory.setEndPositionFromNode(type); if (_lookAheadArrayType)
type = _lookAheadArrayType;
else
{
type = parseTypeName(_options.allowVar);
if (type != nullptr)
nodeFactory.setEndPositionFromNode(type);
}
bool isIndexed = false; bool isIndexed = false;
ASTPointer<ASTString> identifier; ASTPointer<ASTString> identifier;
Token::Value token = m_scanner->getCurrentToken(); Token::Value token = m_scanner->getCurrentToken();
@ -324,9 +336,19 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(VarDeclParserOp
} }
else else
identifier = expectIdentifierToken(); identifier = expectIdentifierToken();
return nodeFactory.createNode<VariableDeclaration>(type, identifier, ASTPointer<Expression> value;
visibility, _options.isStateVariable, if (_options.allowInitialValue)
isIndexed); {
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() ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
@ -396,6 +418,7 @@ ASTPointer<Identifier> Parser::parseIdentifier()
ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
{ {
ASTNodeFactory nodeFactory(*this);
ASTPointer<TypeName> type; ASTPointer<TypeName> type;
Token::Value token = m_scanner->getCurrentToken(); Token::Value token = m_scanner->getCurrentToken();
if (Token::isElementaryTypeName(token)) if (Token::isElementaryTypeName(token))
@ -410,9 +433,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
m_scanner->next(); m_scanner->next();
} }
else if (token == Token::Mapping) else if (token == Token::Mapping)
{
type = parseMapping(); type = parseMapping();
}
else if (token == Token::Identifier) else if (token == Token::Identifier)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
@ -421,6 +442,18 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
} }
else else
BOOST_THROW_EXCEPTION(createParserError("Expected type name")); BOOST_THROW_EXCEPTION(createParserError("Expected type name"));
// Parse "[...]" postfixes for arrays.
while (m_scanner->getCurrentToken() == Token::LBrack)
{
m_scanner->next();
ASTPointer<Expression> length;
if (m_scanner->getCurrentToken() != Token::RBrack)
length = parseExpression();
nodeFactory.markEndPosition();
expectToken(Token::RBrack);
type = nodeFactory.createNode<ArrayTypeName>(type, length);
}
return type; return type;
} }
@ -519,7 +552,7 @@ ASTPointer<Statement> Parser::parseStatement()
} }
// fall-through // fall-through
default: default:
statement = parseVarDefOrExprStmt(); statement = parseSimpleStatement();
} }
expectToken(Token::Semicolon); expectToken(Token::Semicolon);
return statement; 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? // LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RParen?
if (m_scanner->getCurrentToken() != Token::Semicolon) if (m_scanner->getCurrentToken() != Token::Semicolon)
initExpression = parseVarDefOrExprStmt(); initExpression = parseSimpleStatement();
expectToken(Token::Semicolon); expectToken(Token::Semicolon);
if (m_scanner->getCurrentToken() != Token::Semicolon) if (m_scanner->getCurrentToken() != Token::Semicolon)
@ -587,56 +620,89 @@ ASTPointer<ForStatement> Parser::parseForStatement()
body); body);
} }
ASTPointer<Statement> Parser::parseVarDefOrExprStmt() ASTPointer<Statement> Parser::parseSimpleStatement()
{ {
if (peekVariableDefinition()) // These two cases are very hard to distinguish:
return parseVariableDefinition(); // x[7 * 20 + 3] a; - x[7 * 20 + 3] = 9;
else // 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(); return parseExpressionStatement();
} default:
break;
}
ASTPointer<VariableDefinition> Parser::parseVariableDefinition() // At this point, we have '(Identifier|ElementaryTypeName) "["'.
{ // We parse '(Identifier|ElementaryTypeName) ( "[" Expression "]" )+' and then decide whether to hand this over
ASTNodeFactory nodeFactory(*this); // to ExpressionStatement or create a VariableDeclarationStatement out of it.
VarDeclParserOptions options; ASTPointer<PrimaryExpression> primary;
options.allowVar = true; if (m_scanner->getCurrentToken() == Token::Identifier)
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options); primary = parseIdentifier();
ASTPointer<Expression> value; else
if (m_scanner->getCurrentToken() == Token::Assign)
{ {
primary = ASTNodeFactory(*this).createNode<ElementaryTypeNameExpression>(m_scanner->getCurrentToken());
m_scanner->next(); 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 else
nodeFactory.setEndPositionFromNode(variable); return parseExpressionStatement(expressionFromIndexAccessStructure(primary, indices));
return nodeFactory.createNode<VariableDefinition>(variable, value);
} }
ASTPointer<ExpressionStatement> Parser::parseExpressionStatement() ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStatement(
ASTPointer<TypeName> const& _lookAheadArrayType)
{ {
ASTNodeFactory nodeFactory(*this); VarDeclParserOptions options;
ASTPointer<Expression> expression = parseExpression(); options.allowVar = true;
nodeFactory.setEndPositionFromNode(expression); options.allowInitialValue = true;
return nodeFactory.createNode<ExpressionStatement>(expression); 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 = parseExpression(_lookAheadIndexAccessStructure);
ASTPointer<Expression> expression = parseBinaryExpression(); 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())) if (!Token::isAssignmentOp(m_scanner->getCurrentToken()))
return expression; return expression;
Token::Value assignmentOperator = expectAssignmentOperator(); Token::Value assignmentOperator = expectAssignmentOperator();
ASTPointer<Expression> rightHandSide = parseExpression(); ASTPointer<Expression> rightHandSide = parseExpression();
ASTNodeFactory nodeFactory(*this, expression);
nodeFactory.setEndPositionFromNode(rightHandSide); nodeFactory.setEndPositionFromNode(rightHandSide);
return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide); return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide);
} }
ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence) ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence,
ASTPointer<Expression> const& _lookAheadIndexAccessStructure)
{ {
ASTNodeFactory nodeFactory(*this); ASTPointer<Expression> expression = parseUnaryExpression(_lookAheadIndexAccessStructure);
ASTPointer<Expression> expression = parseUnaryExpression(); ASTNodeFactory nodeFactory(*this, expression);
int precedence = Token::precedence(m_scanner->getCurrentToken()); int precedence = Token::precedence(m_scanner->getCurrentToken());
for (; precedence >= _minPrecedence; --precedence) for (; precedence >= _minPrecedence; --precedence)
while (Token::precedence(m_scanner->getCurrentToken()) == precedence) while (Token::precedence(m_scanner->getCurrentToken()) == precedence)
@ -650,11 +716,13 @@ ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence)
return expression; return expression;
} }
ASTPointer<Expression> Parser::parseUnaryExpression() ASTPointer<Expression> Parser::parseUnaryExpression(
ASTPointer<Expression> const& _lookAheadIndexAccessStructure)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ?
ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this);
Token::Value token = m_scanner->getCurrentToken(); Token::Value token = m_scanner->getCurrentToken();
if (Token::isUnaryOp(token) || Token::isCountOp(token)) if (!_lookAheadIndexAccessStructure && (Token::isUnaryOp(token) || Token::isCountOp(token)))
{ {
// prefix expression // prefix expression
m_scanner->next(); m_scanner->next();
@ -665,7 +733,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
else else
{ {
// potential postfix expression // potential postfix expression
ASTPointer<Expression> subExpression = parseLeftHandSideExpression(); ASTPointer<Expression> subExpression = parseLeftHandSideExpression(_lookAheadIndexAccessStructure);
token = m_scanner->getCurrentToken(); token = m_scanner->getCurrentToken();
if (!Token::isCountOp(token)) if (!Token::isCountOp(token))
return subExpression; return subExpression;
@ -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; ASTPointer<Expression> expression;
if (m_scanner->getCurrentToken() == Token::New) if (_lookAheadIndexAccessStructure)
expression = _lookAheadIndexAccessStructure;
else if (m_scanner->getCurrentToken() == Token::New)
{ {
expectToken(Token::New); expectToken(Token::New);
ASTPointer<Identifier> contractName(parseIdentifier()); ASTPointer<Identifier> contractName(parseIdentifier());
@ -696,7 +769,9 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression()
case Token::LBrack: case Token::LBrack:
{ {
m_scanner->next(); m_scanner->next();
ASTPointer<Expression> index = parseExpression(); ASTPointer<Expression> index;
if (m_scanner->getCurrentToken() != Token::RBrack)
index = parseExpression();
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RBrack); expectToken(Token::RBrack);
expression = nodeFactory.createNode<IndexAccess>(expression, index); expression = nodeFactory.createNode<IndexAccess>(expression, index);
@ -771,10 +846,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
m_scanner->next(); m_scanner->next();
} }
else else
{
BOOST_THROW_EXCEPTION(createParserError("Expected primary expression.")); BOOST_THROW_EXCEPTION(createParserError("Expected primary expression."));
return ASTPointer<Expression>(); // this is not reached
}
break; break;
} }
return expression; return expression;
@ -821,18 +893,55 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars
return ret; return ret;
} }
Parser::LookAheadInfo Parser::peekStatementType() const
{
// Distinguish between variable declaration (and potentially assignment) and expression statement
// (which include assignments to other expressions and pre-declared variables).
// We have a variable declaration if we get a keyword that specifies a type name.
// If it is an identifier or an elementary type name followed by an identifier, we also have
// a variable declaration.
// If we get an identifier followed by a "[", it can be both ("type[9] a;" or "arr[9] = 7;").
// In all other cases, we have an expression statement.
Token::Value token(m_scanner->getCurrentToken());
bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier);
if (token == Token::Mapping || token == Token::Var ||
(mightBeTypeName && m_scanner->peekNextToken() == Token::Identifier))
return LookAheadInfo::VariableDeclarationStatement;
if (mightBeTypeName && m_scanner->peekNextToken() == Token::LBrack)
return LookAheadInfo::IndexAccessStructure;
return LookAheadInfo::ExpressionStatement;
}
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 ASTNodeFactory nodeFactory(*this, _primary);
// (which include assignments to other expressions and pre-declared variables) ASTPointer<TypeName> type;
// We have a variable definition if we get a keyword that specifies a type name, or if (auto identifier = dynamic_cast<Identifier const*>(_primary.get()))
// in the case of a user-defined type, we have two identifiers following each other. type = nodeFactory.createNode<UserDefinedTypeName>(make_shared<ASTString>(identifier->getName()));
return (m_scanner->getCurrentToken() == Token::Mapping || else if (auto typeName = dynamic_cast<ElementaryTypeNameExpression const*>(_primary.get()))
m_scanner->getCurrentToken() == Token::Var || type = nodeFactory.createNode<ElementaryTypeName>(typeName->getTypeToken());
((Token::isElementaryTypeName(m_scanner->getCurrentToken()) || else
m_scanner->getCurrentToken() == Token::Identifier) && solAssert(false, "Invalid type name for array look-ahead.");
m_scanner->peekNextToken() == Token::Identifier)); 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) void Parser::expectToken(Token::Value _value)

45
libsolidity/Parser.h

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

4
libsolidity/Token.h

@ -162,7 +162,7 @@ namespace solidity
K(New, "new", 0) \ K(New, "new", 0) \
K(Public, "public", 0) \ K(Public, "public", 0) \
K(Private, "private", 0) \ K(Private, "private", 0) \
K(Inheritable, "inheritable", 0) \ K(Internal, "internal", 0) \
K(Return, "return", 0) \ K(Return, "return", 0) \
K(Returns, "returns", 0) \ K(Returns, "returns", 0) \
K(Struct, "struct", 0) \ K(Struct, "struct", 0) \
@ -380,7 +380,7 @@ public:
static bool isCountOp(Value op) { return op == Inc || op == Dec; } static bool isCountOp(Value op) { return op == Inc || op == Dec; }
static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); } static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); }
static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; } 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; } static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == Token::SubEther; }
// Returns a string corresponding to the JS token string // 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) else if (Token::String0 <= _typeToken && _typeToken <= Token::String32)
return make_shared<StaticStringType>(int(_typeToken) - int(Token::String0)); return make_shared<StaticStringType>(int(_typeToken) - int(Token::String0));
else if (_typeToken == Token::Bytes) else if (_typeToken == Token::Bytes)
return make_shared<ByteArrayType>(ByteArrayType::Location::Storage); return make_shared<ArrayType>(ArrayType::Location::Storage);
else else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
std::string(Token::toString(_typeToken)) + " to type.")); std::string(Token::toString(_typeToken)) + " to type."));
@ -83,17 +83,35 @@ TypePointer Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
return TypePointer(); return TypePointer();
} }
TypePointer Type::fromMapping(Mapping const& _typeName) TypePointer Type::fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType)
{ {
TypePointer keyType = _typeName.getKeyType().toType(); TypePointer keyType = _keyType.toType();
if (!keyType) if (!keyType)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name.")); BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name."));
TypePointer valueType = _typeName.getValueType().toType(); TypePointer valueType = _valueType.toType();
if (!valueType) if (!valueType)
BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name")); BOOST_THROW_EXCEPTION(_valueType.createTypeError("Invalid type name."));
return make_shared<MappingType>(keyType, valueType); return make_shared<MappingType>(keyType, valueType);
} }
TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length)
{
TypePointer baseType = _baseTypeName.toType();
if (!baseType)
BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Invalid type name."));
if (_length)
{
if (!_length->getType())
_length->checkTypeRequirements();
auto const* length = dynamic_cast<IntegerConstantType const*>(_length->getType().get());
if (!length)
BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array length."));
return make_shared<ArrayType>(ArrayType::Location::Storage, baseType, length->literalValue(nullptr));
}
else
return make_shared<ArrayType>(ArrayType::Location::Storage, baseType);
}
TypePointer Type::forLiteral(Literal const& _literal) TypePointer Type::forLiteral(Literal const& _literal)
{ {
switch (_literal.getToken()) switch (_literal.getToken())
@ -517,27 +535,27 @@ TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer(); return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
} }
bool ByteArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
{ {
return _convertTo.getCategory() == getCategory(); return _convertTo.getCategory() == getCategory();
} }
TypePointer ByteArrayType::unaryOperatorResult(Token::Value _operator) const TypePointer ArrayType::unaryOperatorResult(Token::Value _operator) const
{ {
if (_operator == Token::Delete) if (_operator == Token::Delete)
return make_shared<VoidType>(); return make_shared<VoidType>();
return TypePointer(); return TypePointer();
} }
bool ByteArrayType::operator==(Type const& _other) const bool ArrayType::operator==(Type const& _other) const
{ {
if (_other.getCategory() != getCategory()) if (_other.getCategory() != getCategory())
return false; return false;
ByteArrayType const& other = dynamic_cast<ByteArrayType const&>(_other); ArrayType const& other = dynamic_cast<ArrayType const&>(_other);
return other.m_location == m_location; return other.m_location == m_location;
} }
unsigned ByteArrayType::getSizeOnStack() const unsigned ArrayType::getSizeOnStack() const
{ {
if (m_location == Location::CallData) if (m_location == Location::CallData)
// offset, length (stack top) // offset, length (stack top)
@ -547,12 +565,30 @@ unsigned ByteArrayType::getSizeOnStack() const
return 1; return 1;
} }
shared_ptr<ByteArrayType> ByteArrayType::copyForLocation(ByteArrayType::Location _location) const string ArrayType::toString() const
{
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 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. // We need to lazy-initialize it because of recursive references.
if (!m_members) if (!m_members)
{ {
vector<pair<string, TypePointer>> members; MemberList::MemberMap members;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers()) for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
members.push_back(make_pair(variable->getName(), variable->getType())); members.push_back(make_pair(variable->getName(), variable->getType()));
m_members.reset(new MemberList(members)); m_members.reset(new MemberList(members));
@ -833,10 +869,17 @@ string FunctionType::toString() const
unsigned FunctionType::getSizeOnStack() 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; unsigned size = 0;
if (m_location == Location::External) if (location == Location::External)
size = 2; size = 2;
else if (m_location == Location::Internal || m_location == Location::Bare) else if (location == Location::Internal || location == Location::Bare)
size = 1; size = 1;
if (m_gasSet) if (m_gasSet)
size++; size++;
@ -1033,7 +1076,7 @@ MagicType::MagicType(MagicType::Kind _kind):
m_members = MemberList({{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, m_members = MemberList({{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
{"gas", make_shared<IntegerType>(256)}, {"gas", make_shared<IntegerType>(256)},
{"value", make_shared<IntegerType>(256)}, {"value", make_shared<IntegerType>(256)},
{"data", make_shared<ByteArrayType>(ByteArrayType::Location::CallData)}}); {"data", make_shared<ArrayType>(ArrayType::Location::CallData)}});
break; break;
case Kind::Transaction: case Kind::Transaction:
m_members = MemberList({{"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, m_members = MemberList({{"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},

44
libsolidity/Types.h

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

5
libsolidity/grammar.txt

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

99
mix/FileIo.cpp

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

25
mix/FileIo.h

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

2
mix/QVariableDefinition.h

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

232
mix/qml/DeploymentDialog.qml

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

39
mix/qml/ProjectModel.qml

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

1
mix/qml/StatusPane.qml

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

1
mix/qml/Style.qml

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

2
mix/qml/TransactionDialog.qml

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

244
mix/qml/js/ProjectModel.js

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

16
mix/qml/js/QEtherHelper.js

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

1
mix/res.qrc

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

4
test/CMakeLists.txt

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

56
test/SolidityEndToEndTest.cpp

@ -1669,7 +1669,6 @@ BOOST_AUTO_TEST_CASE(value_insane)
function test() { h = new helper(); } function test() { h = new helper(); }
function sendAmount(uint amount) returns (uint256 bal) { function sendAmount(uint amount) returns (uint256 bal) {
var x1 = h.getBalance.value; var x1 = h.getBalance.value;
uint someStackElement = 20;
var x2 = x1(amount).gas; var x2 = x1(amount).gas;
var x3 = x2(1000).value; var x3 = x2(1000).value;
return x3(amount + 3)();// overwrite 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_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) BOOST_AUTO_TEST_CASE(external_function)
{ {
char const* sourceCode = R"( 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) BOOST_AUTO_TEST_CASE(illegal_override_visibility)
{ {
char const* text = R"( char const* text = R"(
contract B { function f() inheritable {} } contract B { function f() internal {} }
contract C is B { function f() public {} } contract C is B { function f() public {} }
)"; )";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
@ -706,7 +706,7 @@ BOOST_AUTO_TEST_CASE(private_state_variable)
" uint64(2);\n" " uint64(2);\n"
" }\n" " }\n"
"uint256 private foo;\n" "uint256 private foo;\n"
"uint256 inheritable bar;\n" "uint256 internal bar;\n"
"}\n"; "}\n";
ASTPointer<SourceUnit> source; ASTPointer<SourceUnit> source;
@ -717,7 +717,7 @@ BOOST_AUTO_TEST_CASE(private_state_variable)
function = retrieveFunctionBySignature(contract, "foo()"); function = retrieveFunctionBySignature(contract, "foo()");
BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of a private variable should not exist"); BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of a private variable should not exist");
function = retrieveFunctionBySignature(contract, "bar()"); 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) 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_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"( char const* text = R"(
contract c { contract c {
function f() inheritable {} function f() internal {}
} }
contract d { contract d {
function g() { c(0).f(); } 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_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"( char const* text = R"(
contract c { 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_CHECK_NO_THROW(parseTextAndResolveNamesWithChecks(sourceCode));
} }
BOOST_AUTO_TEST_CASE(array_with_nonconstant_length)
{
char const* text = R"(
contract c {
function f(uint a) { uint8[a] x; }
})";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

57
test/SolidityParser.cpp

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

4
test/TestHelper.cpp

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

50
test/stSolidityTestFiller.json

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

72
test/stTransactionTestFiller.json

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

7
test/transaction.cpp

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

15
test/ttTransactionTestFiller.json

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

2
third/CMakeLists.txt

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

Loading…
Cancel
Save