Browse Source

Merge branch 'develop' into p2p

cl-refactor
subtly 10 years ago
parent
commit
2eaebc1871
  1. 5
      .gitignore
  2. 26
      CMakeLists.txt
  3. 2
      cmake/EthDependencies.cmake
  4. 18
      cmake/EthExecutableHelper.cmake
  5. 49
      cmake/FindCURL.cmake
  6. 4
      cmake/FindGmp.cmake
  7. 47
      cmake/FindJsonRpcCpp.cmake
  8. 27
      libevmcore/Assembly.cpp
  9. 5
      libevmcore/Assembly.h
  10. 68
      libsolidity/AST.cpp
  11. 82
      libsolidity/AST.h
  12. 2
      libsolidity/ASTForward.h
  13. 25
      libsolidity/ASTPrinter.cpp
  14. 4
      libsolidity/ASTPrinter.h
  15. 8
      libsolidity/ASTVisitor.h
  16. 50
      libsolidity/AST_accept.h
  17. 67
      libsolidity/CallGraph.cpp
  18. 55
      libsolidity/CallGraph.h
  19. 124
      libsolidity/Compiler.cpp
  20. 15
      libsolidity/Compiler.h
  21. 24
      libsolidity/CompilerContext.cpp
  22. 14
      libsolidity/CompilerContext.h
  23. 15
      libsolidity/CompilerStack.cpp
  24. 2
      libsolidity/CompilerStack.h
  25. 6
      libsolidity/CompilerUtils.cpp
  26. 63
      libsolidity/ExpressionCompiler.cpp
  27. 1
      libsolidity/ExpressionCompiler.h
  28. 11
      libsolidity/InterfaceHandler.cpp
  29. 8
      libsolidity/InterfaceHandler.h
  30. 32
      libsolidity/NameAndTypeResolver.cpp
  31. 2
      libsolidity/NameAndTypeResolver.h
  32. 79
      libsolidity/Parser.cpp
  33. 5
      libsolidity/Parser.h
  34. 13
      libsolidity/Scanner.cpp
  35. 13
      libsolidity/Token.h
  36. 26
      libsolidity/Types.cpp
  37. 11
      libsolidity/Types.h
  38. 49
      libsolidity/Utils.h
  39. 10
      libsolidity/grammar.txt
  40. 99
      solc/CommandLineInterface.cpp
  41. 1
      test/CMakeLists.txt
  42. 13
      test/solidityCompiler.cpp
  43. 117
      test/solidityEndToEndTest.cpp
  44. 9
      test/solidityExpressionCompiler.cpp
  45. 27
      test/solidityNameAndTypeResolution.cpp
  46. 64
      test/solidityParser.cpp
  47. 40
      test/vm.cpp
  48. 2
      test/vm.h

5
.gitignore

@ -18,11 +18,14 @@ ipch
*.sdf
*.opensdf
*.suo
*.vcxproj
*.vcxproj.filters
*.sln
# VIM stuff
*.swp
#Xcode stuff
# Xcode stuff
build_xc
*.user

26
CMakeLists.txt

@ -156,4 +156,28 @@ add_test(NAME alltests WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test COMMAND testet
#unset(TARGET_PLATFORM CACHE)
if (WIN32)
# packaging stuff
include(InstallRequiredSystemLibraries)
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "ethereum")
set(CPACK_PACKAGE_VENDOR "ethereum.org")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set(CPACK_PACKAGE_VERSION "0.7")
set(CPACK_GENERATOR "NSIS")
# our stuff
set(CPACK_COMPONENT_ALETHZERO_GROUP "Applications")
set(CPACK_COMPONENT_THIRD_GROUP "Applications")
set(CPACK_COMPONENT_MIX_GROUP "Applications")
set(CPACK_COMPONENTS_ALL alethzero third mix)
# nsis specific stuff
set(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY} ethereum")
set(CPACK_NSIS_HELP_LINK "https://github.com/ethereum/cpp-ethereum")
set(CPACK_NSIS_URL_INFO_ABOUT "https://github.com/ethereum/cpp-ethereum")
set(CPACK_NSIS_CONTACT "ethereum.org")
set(CPACK_NSIS_MODIFY_PATH ON)
include(CPack)
endif (WIN32)

2
cmake/EthDependencies.cmake

@ -82,7 +82,7 @@ if (GMP_FOUND)
endif()
# curl is only requried for tests
# TODO specify min curl version, on windows we are currenly using 7.29
# TODO specify min curl version, on windows we are currently using 7.29
find_package (CURL)
message(" - curl header: ${CURL_INCLUDE_DIRS}")
message(" - curl lib : ${CURL_LIBRARIES}")

18
cmake/EthExecutableHelper.cmake

@ -61,12 +61,13 @@ macro(eth_install_executable EXECUTABLE)
if (ETH_INSTALL_EXECUTABLE_QMLDIR)
set(eth_qml_dir "-qmldir=${ETH_INSTALL_EXECUTABLE_QMLDIR}")
message(STATUS "${EXECUTABLE} qmldir: ${eth_qml_dir}")
endif()
if (APPLE)
# First have qt5 install plugins and frameworks
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND ${MACDEPLOYQT_APP} ${eth_qml_dir} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app
COMMAND ${MACDEPLOYQT_APP} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${EXECUTABLE}.app ${eth_qml_dir}
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
# This tool and next will inspect linked libraries in order to determine which dependencies are required
@ -107,7 +108,20 @@ macro(eth_install_executable EXECUTABLE)
$<TARGET_FILE_DIR:${EXECUTABLE}>/platforms
)
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin)
install( FILES ${DLLS}
DESTINATION bin
COMPONENT ${EXECUTABLE}
)
install( DIRECTORY ${ETH_DEPENDENCY_INSTALL_DIR}/plugins/platforms
DESTINATION bin
COMPONENT ${EXECUTABLE}
)
install( TARGETS ${EXECUTABLE} RUNTIME
DESTINATION bin
COMPONENT ${EXECUTABLE}
)
else()
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin)

49
cmake/FindCURL.cmake

@ -0,0 +1,49 @@
# Find CURL
#
# Find the curl includes and library
#
# if you nee to add a custom library search path, do it via via CMAKE_PREFIX_PATH
#
# This module defines
# CURL_INCLUDE_DIRS, where to find header, etc.
# CURL_LIBRARIES, the libraries needed to use curl.
# CURL_FOUND, If false, do not try to use curl.
# only look in default directories
find_path(
CURL_INCLUDE_DIR
NAMES curl/curl.h
DOC "curl include dir"
)
find_library(
CURL_LIBRARY
# names from cmake's FindCURL
NAMES curl curllib libcurl_imp curllib_static libcurl
DOC "curl library"
)
set(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIR})
set(CURL_LIBRARIES ${CURL_LIBRARY})
# debug library on windows
# same naming convention as in qt (appending debug library with d)
# boost is using the same "hack" as us with "optimized" and "debug"
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
find_library(
CURL_LIBRARY_DEBUG
NAMES curld libcurld
DOC "curl debug library"
)
set(CURL_LIBRARIES optimized ${CURL_LIBRARIES} debug ${CURL_LIBRARY_DEBUG})
endif()
# handle the QUIETLY and REQUIRED arguments and set CURL_FOUND to TRUE
# if all listed variables are TRUE, hide their existence from configuration view
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(CURL DEFAULT_MSG
CURL_INCLUDE_DIR CURL_LIBRARY)
mark_as_advanced (CURL_INCLUDE_DIR CURL_LIBRARY)

4
cmake/FindGmp.cmake

@ -14,13 +14,13 @@ find_path(
GMP_INCLUDE_DIR
NAMES gmp.h
DOC "gmp include dir"
)
)
find_library(
GMP_LIBRARY
NAMES gmp
DOC "gmp library"
)
)
set(GMP_INCLUDE_DIRS ${GMP_INCLUDE_DIR})
set(GMP_LIBRARIES ${GMP_LIBRARY})

47
cmake/FindJsonRpcCpp.cmake

@ -43,6 +43,53 @@ set (JSON_RPC_CPP_LIBRARIES ${JSON_RPC_CPP_COMMON_LIBRARY} ${JSON_RPC_CPP_SERVER
set (JSON_RPC_CPP_SERVER_LIBRARIES ${JSON_RPC_CPP_COMMON_LIBRARY} ${JSON_RPC_CPP_SERVER_LIBRARY})
set (JSON_RPC_CPP_CLIENT_LIBRARIES ${JSON_RPC_CPP_COMMON_LIBRARY} ${JSON_RPC_CPP_CLIENT_LIBRARY})
# debug library on windows
# same naming convention as in qt (appending debug library with d)
# boost is using the same "hack" as us with "optimized" and "debug"
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
find_library(
JSON_RPC_CPP_COMMON_LIBRARY_DEBUG
NAMES jsonrpccpp-commond
DOC "json-rpc-cpp common debug library"
)
find_library(
JSON_RPC_CPP_SERVER_LIBRARY_DEBUG
NAMES jsonrpccpp-serverd
DOC "json-rpc-cpp server debug library"
)
find_library(
JSON_RPC_CPP_CLIENT_LIBRARY_DEBUG
NAMES jsonrpccpp-clientd
DOC "json-rpc-cpp client debug library"
)
set (JSON_RPC_CPP_LIBRARIES
optimized ${JSON_RPC_CPP_COMMON_LIBRARY}
optimized ${JSON_RPC_CPP_SERVER_LIBRARY}
optimized ${JSON_RPC_CPP_CLIENT_LIBRARY}
debug ${JSON_RPC_CPP_COMMON_LIBRARY_DEBUG}
debug ${JSON_RPC_CPP_SERVER_LIBRARY_DEBUG}
debug ${JSON_RPC_CPP_CLIENT_LIBRARY_DEBUG}
)
set (JSON_RPC_CPP_SERVER_LIBRARIES
optimized ${JSON_RPC_CPP_COMMON_LIBRARY}
optimized ${JSON_RPC_CPP_SERVER_LIBRARY}
debug ${JSON_RPC_CPP_COMMON_LIBRARY_DEBUG}
debug ${JSON_RPC_CPP_SERVER_LIBRARY_DEBUG}
)
set (JSON_RPC_CPP_CLIENT_LIBRARIES
optimized ${JSON_RPC_CPP_COMMON_LIBRARY}
optimized ${JSON_RPC_CPP_CLIENT_LIBRARY}
debug ${JSON_RPC_CPP_COMMON_LIBRARY_DEBUG}
debug ${JSON_RPC_CPP_CLIENT_LIBRARY_DEBUG}
)
endif()
# handle the QUIETLY and REQUIRED arguments and set JSON_RPC_CPP_FOUND to TRUE
# if all listed variables are TRUE, hide their existence from configuration view
include(FindPackageHandleStandardArgs)

27
libevmcore/Assembly.cpp

@ -39,6 +39,7 @@ unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const
case Push:
return 1 + max<unsigned>(1, dev::bytesRequired(m_data));
case PushSubSize:
case PushProgramSize:
return 4; // worst case: a 16MB program
case PushTag:
case PushData:
@ -59,7 +60,13 @@ int AssemblyItem::deposit() const
{
case Operation:
return instructionInfo((Instruction)(byte)m_data).ret - instructionInfo((Instruction)(byte)m_data).args;
case Push: case PushString: case PushTag: case PushData: case PushSub: case PushSubSize:
case Push:
case PushString:
case PushTag:
case PushData:
case PushSub:
case PushSubSize:
case PushProgramSize:
return 1;
case Tag:
return 0;
@ -146,6 +153,9 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItemsConstRef _i)
case PushSubSize:
_out << " PUSHss[" << hex << h256(i.data()).abridged() << "]";
break;
case PushProgramSize:
_out << " PUSHSIZE";
break;
case NoOptimizeBegin:
_out << " DoNotOptimze{{";
break;
@ -185,6 +195,9 @@ ostream& Assembly::streamRLP(ostream& _out, string const& _prefix) const
case PushSubSize:
_out << _prefix << " PUSH #[$" << h256(i.m_data).abridged() << "]" << endl;
break;
case PushProgramSize:
_out << _prefix << " PUSHSIZE" << endl;
break;
case Tag:
_out << _prefix << "tag" << i.m_data << ": " << endl << _prefix << " JUMPDEST" << endl;
break;
@ -303,6 +316,7 @@ Assembly& Assembly::optimise(bool _enable)
{ { PushString, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushSub, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushSubSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushProgramSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { Push, PushTag, Instruction::JUMPI }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].data()) return { m[1], Instruction::JUMP }; else return {}; } },
{ { Instruction::ISZERO, Instruction::ISZERO }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
};
@ -468,6 +482,7 @@ bytes Assembly::assemble() const
vector<unsigned> tagPos(m_usedTags);
map<unsigned, unsigned> tagRef;
multimap<h256, unsigned> dataRef;
vector<unsigned> sizeRef; ///< Pointers to code locations where the size of the program is inserted
unsigned bytesPerTag = dev::bytesRequired(totalBytes);
byte tagPush = (byte)Instruction::PUSH1 - 1 + bytesPerTag;
@ -526,6 +541,11 @@ bytes Assembly::assemble() const
toBigEndian(s, byr);
break;
}
case PushProgramSize:
ret.push_back(tagPush);
sizeRef.push_back(ret.size());
ret.resize(ret.size() + bytesPerTag);
break;
case Tag:
tagPos[(unsigned)i.m_data] = ret.size();
ret.push_back((byte)Instruction::JUMPDEST);
@ -561,5 +581,10 @@ bytes Assembly::assemble() const
}
}
}
for (unsigned pos: sizeRef)
{
bytesRef r(ret.data() + pos, bytesPerTag);
toBigEndian(ret.size(), r);
}
return ret;
}

5
libevmcore/Assembly.h

@ -32,7 +32,7 @@ namespace dev
namespace eth
{
enum AssemblyItemType { UndefinedItem, Operation, Push, PushString, PushTag, PushSub, PushSubSize, Tag, PushData, NoOptimizeBegin, NoOptimizeEnd };
enum AssemblyItemType { UndefinedItem, Operation, Push, PushString, PushTag, PushSub, PushSubSize, PushProgramSize, Tag, PushData, NoOptimizeBegin, NoOptimizeEnd };
class Assembly;
@ -86,6 +86,9 @@ public:
AssemblyItem const& append(std::string const& _data) { return append(newPushString(_data)); }
AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); }
AssemblyItem appendSubSize(Assembly const& _a) { auto ret = newSub(_a); append(newPushSubSize(ret.data())); return ret; }
/// Pushes the final size of the current assembly itself. Use this when the code is modified
/// after compilation and CODESIZE is not an option.
void appendProgramSize() { append(AssemblyItem(PushProgramSize)); }
AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; }
AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; }

68
libsolidity/AST.cpp

@ -21,7 +21,7 @@
*/
#include <algorithm>
#include <libsolidity/Utils.h>
#include <libsolidity/AST.h>
#include <libsolidity/ASTVisitor.h>
#include <libsolidity/Exceptions.h>
@ -39,6 +39,17 @@ TypeError ASTNode::createTypeError(string const& _description) const
return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description);
}
void ContractDefinition::checkTypeRequirements()
{
FunctionDefinition const* constructor = getConstructor();
if (constructor && !constructor->getReturnParameters().empty())
BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError(
"Non-empty \"returns\" directive for constructor."));
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
function->checkTypeRequirements();
}
vector<FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const
{
vector<FunctionDefinition const*> exportedFunctions;
@ -54,6 +65,14 @@ vector<FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() co
return exportedFunctions;
}
FunctionDefinition const* ContractDefinition::getConstructor() const
{
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
if (f->getName() == getName())
return f.get();
return nullptr;
}
void StructDefinition::checkMemberTypes() const
{
for (ASTPointer<VariableDeclaration> const& member: getMembers())
@ -111,12 +130,22 @@ void WhileStatement::checkTypeRequirements()
m_body->checkTypeRequirements();
}
void ForStatement::checkTypeRequirements()
{
if (m_initExpression)
m_initExpression->checkTypeRequirements();
if (m_condExpression)
m_condExpression->expectType(BoolType());
if (m_loopExpression)
m_loopExpression->checkTypeRequirements();
m_body->checkTypeRequirements();
}
void Return::checkTypeRequirements()
{
if (!m_expression)
return;
if (asserts(m_returnParameters))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Return parameters not assigned."));
solAssert(m_returnParameters, "Return parameters not assigned.");
if (m_returnParameters->getParameters().size() != 1)
BOOST_THROW_EXCEPTION(createTypeError("Different number of arguments in return statement "
"than in returns declaration."));
@ -235,13 +264,12 @@ void FunctionCall::checkTypeRequirements()
BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed."));
m_type = type.getActualType();
}
else
else if (FunctionType const* functionType = dynamic_cast<FunctionType const*>(expressionType))
{
//@todo would be nice to create a struct type from the arguments
// and then ask if that is implicitly convertible to the struct represented by the
// function parameters
FunctionType const& functionType = dynamic_cast<FunctionType const&>(*expressionType);
TypePointers const& parameterTypes = functionType.getParameterTypes();
TypePointers const& parameterTypes = functionType->getParameterTypes();
if (parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call."));
for (size_t i = 0; i < m_arguments.size(); ++i)
@ -249,11 +277,13 @@ void FunctionCall::checkTypeRequirements()
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call."));
// @todo actually the return type should be an anonymous struct,
// but we change it to the type of the first return value until we have structs
if (functionType.getReturnParameterTypes().empty())
if (functionType->getReturnParameterTypes().empty())
m_type = make_shared<VoidType>();
else
m_type = functionType.getReturnParameterTypes().front();
m_type = functionType->getReturnParameterTypes().front();
}
else
BOOST_THROW_EXCEPTION(createTypeError("Type is not callable."));
}
bool FunctionCall::isTypeConversion() const
@ -261,6 +291,25 @@ bool FunctionCall::isTypeConversion() const
return m_expression->getType()->getCategory() == Type::Category::TYPE;
}
void NewExpression::checkTypeRequirements()
{
m_contractName->checkTypeRequirements();
for (ASTPointer<Expression> const& argument: m_arguments)
argument->checkTypeRequirements();
m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration());
if (!m_contract)
BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract."));
shared_ptr<ContractType const> type = make_shared<ContractType const>(*m_contract);
m_type = type;
TypePointers const& parameterTypes = type->getConstructorType()->getParameterTypes();
if (parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for constructor call."));
for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i]))
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in constructor call."));
}
void MemberAccess::checkTypeRequirements()
{
m_expression->checkTypeRequirements();
@ -286,8 +335,7 @@ void IndexAccess::checkTypeRequirements()
void Identifier::checkTypeRequirements()
{
if (asserts(m_referencedDeclaration))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier not resolved."));
solAssert(m_referencedDeclaration, "Identifier not resolved.");
VariableDeclaration const* variable = dynamic_cast<VariableDeclaration const*>(m_referencedDeclaration);
if (variable)

82
libsolidity/AST.h

@ -27,6 +27,7 @@
#include <vector>
#include <memory>
#include <boost/noncopyable.hpp>
#include <libsolidity/Utils.h>
#include <libsolidity/ASTForward.h>
#include <libsolidity/BaseTypes.h>
#include <libsolidity/Token.h>
@ -174,6 +175,10 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; }
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; }
/// Checks that the constructor does not have a "returns" statement and calls
/// checkTypeRequirements on all its functions.
void checkTypeRequirements();
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
@ -181,6 +186,9 @@ public:
/// Returns the functions that make up the calling interface in the intended order.
std::vector<FunctionDefinition const*> getInterfaceFunctions() const;
/// Returns the constructor or nullptr if no constructor was specified
FunctionDefinition const* getConstructor() const;
private:
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
@ -357,7 +365,7 @@ public:
explicit ElementaryTypeName(Location const& _location, Token::Value _type):
TypeName(_location), m_type(_type)
{
if (asserts(Token::isElementaryTypeName(_type))) BOOST_THROW_EXCEPTION(InternalCompilerError());
solAssert(Token::isElementaryTypeName(_type), "");
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
@ -502,6 +510,42 @@ private:
ASTPointer<Statement> m_body;
};
/**
* For loop statement
*/
class ForStatement: public BreakableStatement
{
public:
ForStatement(Location const& _location,
ASTPointer<Statement> const& _initExpression,
ASTPointer<Expression> const& _conditionExpression,
ASTPointer<ExpressionStatement> const& _loopExpression,
ASTPointer<Statement> const& _body):
BreakableStatement(_location),
m_initExpression(_initExpression),
m_condExpression(_conditionExpression),
m_loopExpression(_loopExpression),
m_body(_body) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
Statement const* getInitializationExpression() const { return m_initExpression.get(); }
Expression const* getCondition() const { return m_condExpression.get(); }
ExpressionStatement const* getLoopExpression() const { return m_loopExpression.get(); }
Statement const& getBody() const { return *m_body; }
private:
/// For statement's initialization expresion. for(XXX; ; ). Can be empty
ASTPointer<Statement> m_initExpression;
/// For statement's condition expresion. for(; XXX ; ). Can be empty
ASTPointer<Expression> m_condExpression;
/// For statement's loop expresion. for(;;XXX). Can be empty
ASTPointer<ExpressionStatement> m_loopExpression;
/// The body of the loop
ASTPointer<Statement> m_body;
};
class Continue: public Statement
{
public:
@ -532,8 +576,7 @@ public:
void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; }
ParameterList const& getFunctionReturnParameters() const
{
if (asserts(m_returnParameters))
BOOST_THROW_EXCEPTION(InternalCompilerError());
solAssert(m_returnParameters, "");
return *m_returnParameters;
}
Expression const* getExpression() const { return m_expression.get(); }
@ -639,7 +682,7 @@ public:
Expression(_location), m_leftHandSide(_leftHandSide),
m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide)
{
if (asserts(Token::isAssignmentOp(_assignmentOperator))) BOOST_THROW_EXCEPTION(InternalCompilerError());
solAssert(Token::isAssignmentOp(_assignmentOperator), "");
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
@ -667,7 +710,7 @@ public:
Expression(_location), m_operator(_operator),
m_subExpression(_subExpression), m_isPrefix(_isPrefix)
{
if (asserts(Token::isUnaryOp(_operator))) BOOST_THROW_EXCEPTION(InternalCompilerError());
solAssert(Token::isUnaryOp(_operator), "");
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
@ -694,7 +737,7 @@ public:
Token::Value _operator, ASTPointer<Expression> const& _right):
Expression(_location), m_left(_left), m_operator(_operator), m_right(_right)
{
if (asserts(Token::isBinaryOp(_operator) || Token::isCompareOp(_operator))) BOOST_THROW_EXCEPTION(InternalCompilerError());
solAssert(Token::isBinaryOp(_operator) || Token::isCompareOp(_operator), "");
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
@ -740,6 +783,31 @@ private:
std::vector<ASTPointer<Expression>> m_arguments;
};
/**
* Expression that creates a new contract, e.g. "new SomeContract(1, 2)".
*/
class NewExpression: public Expression
{
public:
NewExpression(Location const& _location, ASTPointer<Identifier> const& _contractName,
std::vector<ASTPointer<Expression>> const& _arguments):
Expression(_location), m_contractName(_contractName), m_arguments(_arguments) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
virtual void checkTypeRequirements() override;
std::vector<ASTPointer<Expression const>> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; }
/// Returns the referenced contract. Can only be called after type checking.
ContractDefinition const* getContract() const { solAssert(m_contract, ""); return m_contract; }
private:
ASTPointer<Identifier> m_contractName;
std::vector<ASTPointer<Expression>> m_arguments;
ContractDefinition const* m_contract = nullptr;
};
/**
* Access to a member of an object. Example: x.name
*/
@ -826,7 +894,7 @@ public:
ElementaryTypeNameExpression(Location const& _location, Token::Value _typeToken):
PrimaryExpression(_location), m_typeToken(_typeToken)
{
if (asserts(Token::isElementaryTypeName(_typeToken))) BOOST_THROW_EXCEPTION(InternalCompilerError());
solAssert(Token::isElementaryTypeName(_typeToken), "");
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;

2
libsolidity/ASTForward.h

@ -52,6 +52,7 @@ class Block;
class IfStatement;
class BreakableStatement;
class WhileStatement;
class ForStatement;
class Continue;
class Break;
class Return;
@ -62,6 +63,7 @@ class Assignment;
class UnaryOperation;
class BinaryOperation;
class FunctionCall;
class NewExpression;
class MemberAccess;
class IndexAccess;
class PrimaryExpression;

25
libsolidity/ASTPrinter.cpp

@ -150,6 +150,13 @@ bool ASTPrinter::visit(WhileStatement const& _node)
return goDeeper();
}
bool ASTPrinter::visit(ForStatement const& _node)
{
writeLine("ForStatement");
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(Continue const& _node)
{
writeLine("Continue");
@ -226,6 +233,14 @@ bool ASTPrinter::visit(FunctionCall const& _node)
return goDeeper();
}
bool ASTPrinter::visit(NewExpression const& _node)
{
writeLine("NewExpression");
printType(_node);
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(MemberAccess const& _node)
{
writeLine("MemberAccess to member " + _node.getMemberName());
@ -352,6 +367,11 @@ void ASTPrinter::endVisit(WhileStatement const&)
m_indentation--;
}
void ASTPrinter::endVisit(ForStatement const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(Continue const&)
{
m_indentation--;
@ -402,6 +422,11 @@ void ASTPrinter::endVisit(FunctionCall const&)
m_indentation--;
}
void ASTPrinter::endVisit(NewExpression const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(MemberAccess const&)
{
m_indentation--;

4
libsolidity/ASTPrinter.h

@ -57,6 +57,7 @@ public:
bool visit(IfStatement const& _node) override;
bool visit(BreakableStatement const& _node) override;
bool visit(WhileStatement const& _node) override;
bool visit(ForStatement const& _node) override;
bool visit(Continue const& _node) override;
bool visit(Break const& _node) override;
bool visit(Return const& _node) override;
@ -67,6 +68,7 @@ public:
bool visit(UnaryOperation const& _node) override;
bool visit(BinaryOperation const& _node) override;
bool visit(FunctionCall const& _node) override;
bool visit(NewExpression const& _node) override;
bool visit(MemberAccess const& _node) override;
bool visit(IndexAccess const& _node) override;
bool visit(PrimaryExpression const& _node) override;
@ -89,6 +91,7 @@ public:
void endVisit(IfStatement const&) override;
void endVisit(BreakableStatement const&) override;
void endVisit(WhileStatement const&) override;
void endVisit(ForStatement const&) override;
void endVisit(Continue const&) override;
void endVisit(Break const&) override;
void endVisit(Return const&) override;
@ -99,6 +102,7 @@ public:
void endVisit(UnaryOperation const&) override;
void endVisit(BinaryOperation const&) override;
void endVisit(FunctionCall const&) override;
void endVisit(NewExpression const&) override;
void endVisit(MemberAccess const&) override;
void endVisit(IndexAccess const&) override;
void endVisit(PrimaryExpression const&) override;

8
libsolidity/ASTVisitor.h

@ -58,6 +58,7 @@ public:
virtual bool visit(IfStatement&) { return true; }
virtual bool visit(BreakableStatement&) { return true; }
virtual bool visit(WhileStatement&) { return true; }
virtual bool visit(ForStatement&) { return true; }
virtual bool visit(Continue&) { return true; }
virtual bool visit(Break&) { return true; }
virtual bool visit(Return&) { return true; }
@ -68,6 +69,7 @@ public:
virtual bool visit(UnaryOperation&) { return true; }
virtual bool visit(BinaryOperation&) { return true; }
virtual bool visit(FunctionCall&) { return true; }
virtual bool visit(NewExpression&) { return true; }
virtual bool visit(MemberAccess&) { return true; }
virtual bool visit(IndexAccess&) { return true; }
virtual bool visit(PrimaryExpression&) { return true; }
@ -92,6 +94,7 @@ public:
virtual void endVisit(IfStatement&) { }
virtual void endVisit(BreakableStatement&) { }
virtual void endVisit(WhileStatement&) { }
virtual void endVisit(ForStatement&) { }
virtual void endVisit(Continue&) { }
virtual void endVisit(Break&) { }
virtual void endVisit(Return&) { }
@ -102,6 +105,7 @@ public:
virtual void endVisit(UnaryOperation&) { }
virtual void endVisit(BinaryOperation&) { }
virtual void endVisit(FunctionCall&) { }
virtual void endVisit(NewExpression&) { }
virtual void endVisit(MemberAccess&) { }
virtual void endVisit(IndexAccess&) { }
virtual void endVisit(PrimaryExpression&) { }
@ -130,6 +134,7 @@ public:
virtual bool visit(IfStatement const&) { return true; }
virtual bool visit(BreakableStatement const&) { return true; }
virtual bool visit(WhileStatement const&) { return true; }
virtual bool visit(ForStatement const&) { return true; }
virtual bool visit(Continue const&) { return true; }
virtual bool visit(Break const&) { return true; }
virtual bool visit(Return const&) { return true; }
@ -140,6 +145,7 @@ public:
virtual bool visit(UnaryOperation const&) { return true; }
virtual bool visit(BinaryOperation const&) { return true; }
virtual bool visit(FunctionCall const&) { return true; }
virtual bool visit(NewExpression const&) { return true; }
virtual bool visit(MemberAccess const&) { return true; }
virtual bool visit(IndexAccess const&) { return true; }
virtual bool visit(PrimaryExpression const&) { return true; }
@ -164,6 +170,7 @@ public:
virtual void endVisit(IfStatement const&) { }
virtual void endVisit(BreakableStatement const&) { }
virtual void endVisit(WhileStatement const&) { }
virtual void endVisit(ForStatement const&) { }
virtual void endVisit(Continue const&) { }
virtual void endVisit(Break const&) { }
virtual void endVisit(Return const&) { }
@ -174,6 +181,7 @@ public:
virtual void endVisit(UnaryOperation const&) { }
virtual void endVisit(BinaryOperation const&) { }
virtual void endVisit(FunctionCall const&) { }
virtual void endVisit(NewExpression const&) { }
virtual void endVisit(MemberAccess const&) { }
virtual void endVisit(IndexAccess const&) { }
virtual void endVisit(PrimaryExpression const&) { }

50
libsolidity/AST_accept.h

@ -267,6 +267,36 @@ void WhileStatement::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this);
}
void ForStatement::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
if (m_initExpression)
m_initExpression->accept(_visitor);
if (m_condExpression)
m_condExpression->accept(_visitor);
if (m_loopExpression)
m_loopExpression->accept(_visitor);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
}
void ForStatement::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
if (m_initExpression)
m_initExpression->accept(_visitor);
if (m_condExpression)
m_condExpression->accept(_visitor);
if (m_loopExpression)
m_loopExpression->accept(_visitor);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
}
void Continue::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
@ -419,6 +449,26 @@ void FunctionCall::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this);
}
void NewExpression::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_contractName->accept(_visitor);
listAccept(m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
void NewExpression::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_contractName->accept(_visitor);
listAccept(m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
void MemberAccess::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))

67
libsolidity/CallGraph.cpp

@ -0,0 +1,67 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Callgraph of functions inside a contract.
*/
#include <libsolidity/AST.h>
#include <libsolidity/CallGraph.h>
using namespace std;
namespace dev
{
namespace solidity
{
void CallGraph::addFunction(FunctionDefinition const& _function)
{
if (!m_functionsSeen.count(&_function))
{
m_functionsSeen.insert(&_function);
m_workQueue.push(&_function);
}
}
set<FunctionDefinition const*> const& CallGraph::getCalls()
{
return m_functionsSeen;
}
void CallGraph::computeCallGraph()
{
while (!m_workQueue.empty())
{
FunctionDefinition const* fun = m_workQueue.front();
fun->accept(*this);
m_workQueue.pop();
}
}
bool CallGraph::visit(Identifier const& _identifier)
{
FunctionDefinition const* fun = dynamic_cast<FunctionDefinition const*>(_identifier.getReferencedDeclaration());
if (fun)
addFunction(*fun);
return true;
}
}
}

55
libsolidity/CallGraph.h

@ -0,0 +1,55 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Callgraph of functions inside a contract.
*/
#include <set>
#include <queue>
#include <boost/range/iterator_range.hpp>
#include <libsolidity/ASTVisitor.h>
namespace dev
{
namespace solidity
{
/**
* Can be used to compute the graph of calls (or rather references) between functions of the same
* contract. Current functionality is limited to computing all functions that are directly
* or indirectly called by some functions.
*/
class CallGraph: private ASTConstVisitor
{
public:
void addFunction(FunctionDefinition const& _function);
void computeCallGraph();
std::set<FunctionDefinition const*> const& getCalls();
private:
void addFunctionToQueue(FunctionDefinition const& _function);
virtual bool visit(Identifier const& _identifier) override;
std::set<FunctionDefinition const*> m_functionsSeen;
std::queue<FunctionDefinition const*> m_workQueue;
};
}
}

124
libsolidity/Compiler.cpp

@ -27,64 +27,93 @@
#include <libsolidity/Compiler.h>
#include <libsolidity/ExpressionCompiler.h>
#include <libsolidity/CompilerUtils.h>
#include <libsolidity/CallGraph.h>
using namespace std;
namespace dev {
namespace solidity {
void Compiler::compileContract(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals)
void Compiler::compileContract(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals,
map<ContractDefinition const*, bytes const*> const& _contracts)
{
m_context = CompilerContext(); // clear it just in case
for (MagicVariableDeclaration const* variable: _magicGlobals)
m_context.addMagicGlobal(*variable);
initializeContext(_contract, _magicGlobals, _contracts);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
if (function->getName() != _contract.getName()) // don't add the constructor here
m_context.addFunction(*function);
registerStateVariables(_contract);
appendFunctionSelector(_contract);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
if (function->getName() != _contract.getName()) // don't add the constructor here
function->accept(*this);
packIntoContractCreator(_contract);
}
void Compiler::packIntoContractCreator(ContractDefinition const& _contract)
{
// Swap the runtime context with the creation-time context
CompilerContext runtimeContext;
swap(m_context, runtimeContext);
initializeContext(_contract, _magicGlobals, _contracts);
packIntoContractCreator(_contract, runtimeContext);
}
void Compiler::initializeContext(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals,
map<ContractDefinition const*, bytes const*> const& _contracts)
{
m_context.setCompiledContracts(_contracts);
for (MagicVariableDeclaration const* variable: _magicGlobals)
m_context.addMagicGlobal(*variable);
registerStateVariables(_contract);
}
FunctionDefinition* constructor = nullptr;
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
if (function->getName() == _contract.getName())
{
constructor = function.get();
break;
}
void Compiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext)
{
set<FunctionDefinition const*> neededFunctions;
FunctionDefinition const* constructor = _contract.getConstructor();
if (constructor)
{
eth::AssemblyItem returnTag = m_context.pushNewTag();
m_context.addFunction(*constructor); // note that it cannot be called due to syntactic reasons
//@todo copy constructor arguments from calldata to memory prior to this
//@todo calling other functions inside the constructor should either trigger a parse error
//or we should copy them here (register them above and call "accept") - detecting which
// functions are referenced / called needs to be done in a recursive way.
appendCalldataUnpacker(*constructor, true);
m_context.appendJumpTo(m_context.getFunctionEntryLabel(*constructor));
constructor->accept(*this);
m_context << returnTag;
}
neededFunctions = getFunctionsNeededByConstructor(*constructor);
for (FunctionDefinition const* fun: neededFunctions)
m_context.addFunction(*fun);
eth::AssemblyItem sub = m_context.addSubroutine(runtimeContext.getAssembly());
if (constructor)
appendConstructorCall(*constructor);
eth::AssemblyItem sub = m_context.addSubroutine(_runtimeContext.getAssembly());
// stack contains sub size
m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY;
m_context << u256(0) << eth::Instruction::RETURN;
// note that we have to explicitly include all used functions because of absolute jump
// labels
for (FunctionDefinition const* fun: neededFunctions)
fun->accept(*this);
}
void Compiler::appendConstructorCall(FunctionDefinition const& _constructor)
{
eth::AssemblyItem returnTag = m_context.pushNewTag();
// copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
unsigned argumentSize = 0;
for (ASTPointer<VariableDeclaration> const& var: _constructor.getParameters())
argumentSize += var->getType()->getCalldataEncodedSize();
if (argumentSize > 0)
{
m_context << u256(argumentSize);
m_context.appendProgramSize();
m_context << u256(1); // copy it to byte one as expected for ABI calls
m_context << eth::Instruction::CODECOPY;
appendCalldataUnpacker(_constructor, true);
}
m_context.appendJumpTo(m_context.getFunctionEntryLabel(_constructor));
m_context << returnTag;
}
set<FunctionDefinition const*> Compiler::getFunctionsNeededByConstructor(FunctionDefinition const& _constructor)
{
CallGraph callgraph;
callgraph.addFunction(_constructor);
callgraph.computeCallGraph();
return callgraph.getCalls();
}
void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
@ -130,7 +159,6 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b
{
// We do not check the calldata size, everything is zero-padded.
unsigned dataOffset = 1;
//@todo this can be done more efficiently, saving some CALLDATALOAD calls
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
{
@ -274,6 +302,40 @@ bool Compiler::visit(WhileStatement const& _whileStatement)
return false;
}
bool Compiler::visit(ForStatement const& _forStatement)
{
eth::AssemblyItem loopStart = m_context.newTag();
eth::AssemblyItem loopEnd = m_context.newTag();
m_continueTags.push_back(loopStart);
m_breakTags.push_back(loopEnd);
if (_forStatement.getInitializationExpression())
_forStatement.getInitializationExpression()->accept(*this);
m_context << loopStart;
// if there is no terminating condition in for, default is to always be true
if (_forStatement.getCondition())
{
compileExpression(*_forStatement.getCondition());
m_context << eth::Instruction::ISZERO;
m_context.appendConditionalJumpTo(loopEnd);
}
_forStatement.getBody().accept(*this);
// for's loop expression if existing
if (_forStatement.getLoopExpression())
_forStatement.getLoopExpression()->accept(*this);
m_context.appendJumpTo(loopStart);
m_context << loopEnd;
m_continueTags.pop_back();
m_breakTags.pop_back();
return false;
}
bool Compiler::visit(Continue const&)
{
if (!m_continueTags.empty())

15
libsolidity/Compiler.h

@ -32,14 +32,22 @@ class Compiler: private ASTConstVisitor
public:
explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_returnTag(m_context.newTag()) {}
void compileContract(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals);
void compileContract(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals,
std::map<ContractDefinition const*, bytes const*> const& _contracts);
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); }
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
private:
/// Creates a new compiler context / assembly, packs the current code into the data part and
/// Registers the global objects and the non-function objects inside the contract with the context.
void initializeContext(ContractDefinition const& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals,
std::map<ContractDefinition const*, bytes const*> const& _contracts);
/// Adds the code that is run at creation time. Should be run after exchanging the run-time context
/// with a new and initialized context.
/// adds the constructor code.
void packIntoContractCreator(ContractDefinition const& _contract);
void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext);
void appendConstructorCall(FunctionDefinition const& _constructor);
/// Recursively searches the call graph and returns all functions needed by the constructor (including itself).
std::set<FunctionDefinition const*> getFunctionsNeededByConstructor(FunctionDefinition const& _constructor);
void appendFunctionSelector(ContractDefinition const& _contract);
/// Creates code that unpacks the arguments for the given function, from memory if
/// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes.
@ -51,6 +59,7 @@ private:
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(IfStatement const& _ifStatement) override;
virtual bool visit(WhileStatement const& _whileStatement) override;
virtual bool visit(ForStatement const& _forStatement) override;
virtual bool visit(Continue const& _continue) override;
virtual bool visit(Break const& _break) override;
virtual bool visit(Return const& _return) override;

24
libsolidity/CompilerContext.cpp

@ -27,8 +27,10 @@
using namespace std;
namespace dev {
namespace solidity {
namespace dev
{
namespace solidity
{
void CompilerContext::addMagicGlobal(MagicVariableDeclaration const& _declaration)
{
@ -62,6 +64,13 @@ void CompilerContext::addFunction(FunctionDefinition const& _function)
m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag()));
}
bytes const& CompilerContext::getCompiledContract(const ContractDefinition& _contract) const
{
auto ret = m_compiledContracts.find(&_contract);
solAssert(ret != m_compiledContracts.end(), "Compiled contract not found.");
return *ret->second;
}
bool CompilerContext::isLocalVariable(Declaration const* _declaration) const
{
return m_localVariables.count(_declaration) > 0;
@ -70,16 +79,14 @@ bool CompilerContext::isLocalVariable(Declaration const* _declaration) const
eth::AssemblyItem CompilerContext::getFunctionEntryLabel(FunctionDefinition const& _function) const
{
auto res = m_functionEntryLabels.find(&_function);
if (asserts(res != m_functionEntryLabels.end()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Function entry label not found."));
solAssert(res != m_functionEntryLabels.end(), "Function entry label not found.");
return res->second.tag();
}
unsigned CompilerContext::getBaseStackOffsetOfVariable(Declaration const& _declaration) const
{
auto res = m_localVariables.find(&_declaration);
if (asserts(res != m_localVariables.end()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Variable not found on stack."));
solAssert(res != m_localVariables.end(), "Variable not found on stack.");
return m_localVariablesSize - res->second - 1;
}
@ -91,12 +98,9 @@ unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const
u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const
{
auto it = m_stateVariables.find(&_declaration);
if (it == m_stateVariables.end())
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Variable not found in storage."));
solAssert(it != m_stateVariables.end(), "Variable not found in storage.");
return it->second;
}
}
}

14
libsolidity/CompilerContext.h

@ -39,8 +39,6 @@ namespace solidity {
class CompilerContext
{
public:
CompilerContext(): m_stateVariablesSize(0) {}
void addMagicGlobal(MagicVariableDeclaration const& _declaration);
void addStateVariable(VariableDeclaration const& _declaration);
void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); }
@ -48,6 +46,9 @@ public:
void addAndInitializeVariable(VariableDeclaration const& _declaration);
void addFunction(FunctionDefinition const& _function);
void setCompiledContracts(std::map<ContractDefinition const*, bytes const*> const& _contracts) { m_compiledContracts = _contracts; }
bytes const& getCompiledContract(ContractDefinition const& _contract) const;
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration); }
@ -80,6 +81,10 @@ public:
/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
/// on the stack. @returns the assembly item corresponding to the pushed subroutine, i.e. its offset.
eth::AssemblyItem addSubroutine(eth::Assembly const& _assembly) { return m_asm.appendSubSize(_assembly); }
/// Pushes the size of the final program
void appendProgramSize() { return m_asm.appendProgramSize(); }
/// Adds data to the data section, pushes a reference to the stack
eth::AssemblyItem appendData(bytes const& _data) { return m_asm.append(_data); }
/// Append elements to the current instruction list and adjust @a m_stackOffset.
CompilerContext& operator<<(eth::AssemblyItem const& _item) { m_asm.append(_item); return *this; }
@ -90,13 +95,16 @@ public:
eth::Assembly const& getAssembly() const { return m_asm; }
void streamAssembly(std::ostream& _stream) const { _stream << m_asm; }
bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); }
private:
eth::Assembly m_asm;
/// Magic global variables like msg, tx or this, distinguished by type.
std::set<Declaration const*> m_magicGlobals;
/// Other already compiled contracts to be used in contract creation calls.
std::map<ContractDefinition const*, bytes const*> m_compiledContracts;
/// Size of the state variables, offset of next variable to be added.
u256 m_stateVariablesSize;
u256 m_stateVariablesSize = 0;
/// Storage offsets of state variables
std::map<Declaration const*, u256> m_stateVariables;
/// Offsets of local variables on the stack (relative to stack base).

15
libsolidity/CompilerStack.cpp

@ -73,6 +73,15 @@ void CompilerStack::parse()
resolver.resolveNamesAndTypes(*contract);
m_contracts[contract->getName()].contract = contract;
}
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
m_globalContext->setCurrentContract(*contract);
resolver.updateDeclaration(*m_globalContext->getCurrentThis());
resolver.checkTypeRequirements(*contract);
m_contracts[contract->getName()].contract = contract;
}
m_parseSuccessful = true;
}
@ -96,16 +105,20 @@ void CompilerStack::compile(bool _optimize)
{
if (!m_parseSuccessful)
parse();
map<ContractDefinition const*, bytes const*> contractBytecode;
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
m_globalContext->setCurrentContract(*contract);
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
compiler->compileContract(*contract, m_globalContext->getMagicVariables());
compiler->compileContract(*contract, m_globalContext->getMagicVariables(),
contractBytecode);
Contract& compiledContract = m_contracts[contract->getName()];
compiledContract.bytecode = compiler->getAssembledBytecode();
compiledContract.compiler = move(compiler);
contractBytecode[compiledContract.contract] = &compiledContract.bytecode;
}
}

2
libsolidity/CompilerStack.h

@ -108,7 +108,7 @@ private:
struct Contract
{
ContractDefinition* contract;
ContractDefinition const* contract;
std::shared_ptr<Compiler> compiler;
bytes bytecode;
std::shared_ptr<InterfaceHandler> interfaceHandler;

6
libsolidity/CompilerUtils.cpp

@ -39,8 +39,7 @@ void CompilerUtils::loadFromMemory(unsigned _offset, unsigned _bytes, bool _left
return;
}
eth::Instruction load = _fromCalldata ? eth::Instruction::CALLDATALOAD : eth::Instruction::MLOAD;
if (asserts(_bytes <= 32))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory load of more than 32 bytes requested."));
solAssert(_bytes <= 32, "Memory load of more than 32 bytes requested.");
if (_bytes == 32)
m_context << u256(_offset) << load;
else
@ -63,8 +62,7 @@ void CompilerUtils::storeInMemory(unsigned _offset, unsigned _bytes, bool _leftA
m_context << eth::Instruction::POP;
return;
}
if (asserts(_bytes <= 32))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Memory store of more than 32 bytes requested."));
solAssert(_bytes <= 32, "Memory store of more than 32 bytes requested.");
if (_bytes != 32 && !_leftAligned)
// shift the value accordingly before storing
m_context << (u256(1) << ((32 - _bytes) * 8)) << eth::Instruction::MUL;

63
libsolidity/ExpressionCompiler.cpp

@ -30,8 +30,10 @@
using namespace std;
namespace dev {
namespace solidity {
namespace dev
{
namespace solidity
{
void ExpressionCompiler::compileExpression(CompilerContext& _context, Expression const& _expression, bool _optimize)
{
@ -51,8 +53,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
_assignment.getRightHandSide().accept(*this);
appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType());
_assignment.getLeftHandSide().accept(*this);
if (asserts(m_currentLValue.isValid()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("LValue not retrieved."));
solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
Token::Value op = _assignment.getAssignmentOperator();
if (op != Token::ASSIGN) // compound assignment
@ -84,8 +85,7 @@ void ExpressionCompiler::endVisit(UnaryOperation const& _unaryOperation)
break;
case Token::DELETE: // delete
// @todo semantics change for complex types
if (asserts(m_currentLValue.isValid()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("LValue not retrieved."));
solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
m_context << u256(0);
if (m_currentLValue.storesReferenceOnStack())
@ -95,8 +95,7 @@ void ExpressionCompiler::endVisit(UnaryOperation const& _unaryOperation)
break;
case Token::INC: // ++ (pre- or postfix)
case Token::DEC: // -- (pre- or postfix)
if (asserts(m_currentLValue.isValid()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("LValue not retrieved."));
solAssert(m_currentLValue.isValid(), "LValue not retrieved.");
m_currentLValue.retrieveValue(_unaryOperation);
if (!_unaryOperation.isPrefixOperation())
{
@ -179,8 +178,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
if (_functionCall.isTypeConversion())
{
//@todo struct construction
if (asserts(_functionCall.getArguments().size() == 1))
BOOST_THROW_EXCEPTION(InternalCompilerError());
solAssert(_functionCall.getArguments().size() == 1, "");
Expression const& firstArgument = *_functionCall.getArguments().front();
firstArgument.accept(*this);
if (firstArgument.getType()->getCategory() == Type::Category::CONTRACT &&
@ -195,8 +193,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{
FunctionType const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType());
vector<ASTPointer<Expression const>> arguments = _functionCall.getArguments();
if (asserts(arguments.size() == function.getParameterTypes().size()))
BOOST_THROW_EXCEPTION(InternalCompilerError());
solAssert(arguments.size() == function.getParameterTypes().size(), "");
switch (function.getLocation())
{
@ -279,6 +276,42 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
return false;
}
bool ExpressionCompiler::visit(NewExpression const& _newExpression)
{
ContractType const* type = dynamic_cast<ContractType const*>(_newExpression.getType().get());
solAssert(type, "");
TypePointers const& types = type->getConstructorType()->getParameterTypes();
vector<ASTPointer<Expression const>> arguments = _newExpression.getArguments();
solAssert(arguments.size() == types.size(), "");
// copy the contracts code into memory
bytes const& bytecode = m_context.getCompiledContract(*_newExpression.getContract());
m_context << u256(bytecode.size());
//@todo could be done by actually appending the Assembly, but then we probably need to compile
// multiple times. Will revisit once external fuctions are inlined.
m_context.appendData(bytecode);
//@todo copy to memory position 0, shift as soon as we use memory
m_context << u256(0) << eth::Instruction::CODECOPY;
unsigned dataOffset = bytecode.size();
for (unsigned i = 0; i < arguments.size(); ++i)
{
arguments[i]->accept(*this);
appendTypeConversion(*arguments[i]->getType(), *types[i]);
unsigned const numBytes = types[i]->getCalldataEncodedSize();
if (numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(arguments[i]->getLocation())
<< errinfo_comment("Type " + types[i]->toString() + " not yet supported."));
bool const leftAligned = types[i]->getCategory() == Type::Category::STRING;
CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned);
dataOffset += numBytes;
}
// size, offset, endowment
m_context << u256(dataOffset) << u256(0) << u256(0) << eth::Instruction::CREATE;
return false;
}
void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
{
ASTString const& member = _memberAccess.getMemberName();
@ -401,8 +434,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryOperation)
{
Token::Value const op = _binaryOperation.getOperator();
if (asserts(op == Token::OR || op == Token::AND))
BOOST_THROW_EXCEPTION(InternalCompilerError());
solAssert(op == Token::OR || op == Token::AND, "");
_binaryOperation.getLeftExpression().accept(*this);
m_context << eth::Instruction::DUP1;
@ -554,8 +586,7 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
vector<ASTPointer<Expression const>> const& _arguments,
FunctionCallOptions const& _options)
{
if (asserts(_arguments.size() == _functionType.getParameterTypes().size()))
BOOST_THROW_EXCEPTION(InternalCompilerError());
solAssert(_arguments.size() == _functionType.getParameterTypes().size(), "");
unsigned dataOffset = _options.bare ? 0 : 1; // reserve one byte for the function index
for (unsigned i = 0; i < _arguments.size(); ++i)

1
libsolidity/ExpressionCompiler.h

@ -60,6 +60,7 @@ private:
virtual void endVisit(UnaryOperation const& _unaryOperation) override;
virtual bool visit(BinaryOperation const& _binaryOperation) override;
virtual bool visit(FunctionCall const& _functionCall) override;
virtual bool visit(NewExpression const& _newExpression) override;
virtual void endVisit(MemberAccess const& _memberAccess) override;
virtual bool visit(IndexAccess const& _indexAccess) override;
virtual void endVisit(Identifier const& _identifier) override;

11
libsolidity/InterfaceHandler.cpp

@ -15,7 +15,7 @@ InterfaceHandler::InterfaceHandler()
m_lastTag = DocTagType::NONE;
}
std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefinition& _contractDef,
std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefinition const& _contractDef,
DocumentationType _type)
{
switch(_type)
@ -32,7 +32,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefiniti
return nullptr;
}
std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinition& _contractDef)
std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinition const& _contractDef)
{
Json::Value methods(Json::arrayValue);
@ -63,7 +63,7 @@ std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinitio
return std::unique_ptr<std::string>(new std::string(m_writer.write(methods)));
}
std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefinition& _contractDef)
std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefinition const& _contractDef)
{
Json::Value doc;
Json::Value methods(Json::objectValue);
@ -88,7 +88,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
return std::unique_ptr<std::string>(new std::string(m_writer.write(doc)));
}
std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefinition& _contractDef)
std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefinition const& _contractDef)
{
// LTODO: Somewhere in this function warnings for mismatch of param names
// should be thrown
@ -198,8 +198,7 @@ std::string::const_iterator InterfaceHandler::appendDocTagParam(std::string::con
std::string::const_iterator _end)
{
// Should never be called with an empty vector
if (asserts(!m_params.empty()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Internal: Tried to append to empty parameter"));
solAssert(!m_params.empty(), "Internal: Tried to append to empty parameter");
auto pair = m_params.back();
pair.second += " ";

8
libsolidity/InterfaceHandler.h

@ -67,23 +67,23 @@ public:
/// types provided by @c DocumentationType
/// @return A unique pointer contained string with the json
/// representation of provided type
std::unique_ptr<std::string> getDocumentation(ContractDefinition& _contractDef,
std::unique_ptr<std::string> getDocumentation(ContractDefinition const& _contractDef,
DocumentationType _type);
/// Get the ABI Interface of the contract
/// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json
/// representation of the contract's ABI Interface
std::unique_ptr<std::string> getABIInterface(ContractDefinition& _contractDef);
std::unique_ptr<std::string> getABIInterface(ContractDefinition const& _contractDef);
/// Get the User documentation of the contract
/// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json
/// representation of the contract's user documentation
std::unique_ptr<std::string> getUserDocumentation(ContractDefinition& _contractDef);
std::unique_ptr<std::string> getUserDocumentation(ContractDefinition const& _contractDef);
/// Get the Developer's documentation of the contract
/// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json
/// representation of the contract's developer documentation
std::unique_ptr<std::string> getDevDocumentation(ContractDefinition& _contractDef);
std::unique_ptr<std::string> getDevDocumentation(ContractDefinition const& _contractDef);
private:
void resetUser();

32
libsolidity/NameAndTypeResolver.cpp

@ -49,8 +49,6 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
m_currentScope = &m_scopes[&_contract];
for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs())
ReferencesResolver resolver(*structDef, *this, nullptr);
for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs())
structDef->checkValidityOfMembers();
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
ReferencesResolver resolver(*variable, *this, nullptr);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
@ -59,22 +57,20 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
ReferencesResolver referencesResolver(*function, *this,
function->getReturnParameterList().get());
}
// First, the parameter types of all functions need to be resolved before we can check
// the types, since it is possible to call functions that are only defined later
// in the source.
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
{
m_currentScope = &m_scopes[function.get()];
function->checkTypeRequirements();
}
m_currentScope = &m_scopes[nullptr];
}
void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract)
{
for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs())
structDef->checkValidityOfMembers();
_contract.checkTypeRequirements();
}
void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
{
m_scopes[nullptr].registerDeclaration(_declaration, true);
if (asserts(_declaration.getScope() == nullptr))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Updated declaration outside global scope."));
solAssert(_declaration.getScope() == nullptr, "Updated declaration outside global scope.");
}
Declaration const* NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const
@ -136,8 +132,7 @@ void DeclarationRegistrationHelper::endVisit(VariableDefinition& _variableDefini
{
// Register the local variables with the function
// This does not fit here perfectly, but it saves us another AST visit.
if (asserts(m_currentFunction))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Variable definition without function."));
solAssert(m_currentFunction, "Variable definition without function.");
m_currentFunction->addLocalVariable(_variableDefinition.getDeclaration());
}
@ -152,15 +147,13 @@ void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declara
map<ASTNode const*, DeclarationContainer>::iterator iter;
bool newlyAdded;
tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, DeclarationContainer(m_currentScope, &m_scopes[m_currentScope]));
if (asserts(newlyAdded))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to add new scope."));
solAssert(newlyAdded, "Unable to add new scope.");
m_currentScope = &_declaration;
}
void DeclarationRegistrationHelper::closeCurrentScope()
{
if (asserts(m_currentScope))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Closed non-existing scope."));
solAssert(m_currentScope, "Closed non-existing scope.");
m_currentScope = m_scopes[m_currentScope].getEnclosingDeclaration();
}
@ -199,8 +192,7 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
bool ReferencesResolver::visit(Return& _return)
{
if (asserts(m_returnParameters))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Return parameters not set."));
solAssert(m_returnParameters, "Return parameters not set.");
_return.setFunctionReturnParameters(*m_returnParameters);
return true;
}

2
libsolidity/NameAndTypeResolver.h

@ -46,6 +46,8 @@ public:
void registerDeclarations(SourceUnit& _sourceUnit);
/// Resolves all names and types referenced from the given contract.
void resolveNamesAndTypes(ContractDefinition& _contract);
/// Check all type requirements in the given contract.
void checkTypeRequirements(ContractDefinition& _contract);
/// Updates the given global declaration (used for "this"). Not to be used with declarations
/// that create their own scope.
void updateDeclaration(Declaration const& _declaration);

79
libsolidity/Parser.cpp

@ -304,6 +304,8 @@ ASTPointer<Statement> Parser::parseStatement()
return parseIfStatement();
case Token::WHILE:
return parseWhileStatement();
case Token::FOR:
return parseForStatement();
case Token::LBRACE:
return parseBlock();
// starting from here, all statements must be terminated by a semicolon
@ -328,18 +330,7 @@ ASTPointer<Statement> Parser::parseStatement()
}
break;
default:
// distinguish between variable definition (and potentially assignment) and expression statement
// (which include assignments to other expressions and pre-declared variables)
// We have a variable definition if we get a keyword that specifies a type name, or
// in the case of a user-defined type, we have two identifiers following each other.
if (m_scanner->getCurrentToken() == Token::MAPPING ||
m_scanner->getCurrentToken() == Token::VAR ||
((Token::isElementaryTypeName(m_scanner->getCurrentToken()) ||
m_scanner->getCurrentToken() == Token::IDENTIFIER) &&
m_scanner->peekNextToken() == Token::IDENTIFIER))
statement = parseVariableDefinition();
else // "ordinary" expression statement
statement = parseExpressionStatement();
statement = parseVarDefOrExprStmt();
}
expectToken(Token::SEMICOLON);
return statement;
@ -377,6 +368,44 @@ ASTPointer<WhileStatement> Parser::parseWhileStatement()
return nodeFactory.createNode<WhileStatement>(condition, body);
}
ASTPointer<ForStatement> Parser::parseForStatement()
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<Statement> initExpression;
ASTPointer<Expression> conditionExpression;
ASTPointer<ExpressionStatement> loopExpression;
expectToken(Token::FOR);
expectToken(Token::LPAREN);
// LTODO: Maybe here have some predicate like peekExpression() instead of checking for semicolon and RPAREN?
if (m_scanner->getCurrentToken() != Token::SEMICOLON)
initExpression = parseVarDefOrExprStmt();
expectToken(Token::SEMICOLON);
if (m_scanner->getCurrentToken() != Token::SEMICOLON)
conditionExpression = parseExpression();
expectToken(Token::SEMICOLON);
if (m_scanner->getCurrentToken() != Token::RPAREN)
loopExpression = parseExpressionStatement();
expectToken(Token::RPAREN);
ASTPointer<Statement> body = parseStatement();
nodeFactory.setEndPositionFromNode(body);
return nodeFactory.createNode<ForStatement>(initExpression,
conditionExpression,
loopExpression,
body);
}
ASTPointer<Statement> Parser::parseVarDefOrExprStmt()
{
if (peekVariableDefinition())
return parseVariableDefinition();
else
return parseExpressionStatement();
}
ASTPointer<VariableDefinition> Parser::parseVariableDefinition()
{
ASTNodeFactory nodeFactory(*this);
@ -437,7 +466,17 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
{
ASTNodeFactory nodeFactory(*this);
Token::Value token = m_scanner->getCurrentToken();
if (Token::isUnaryOp(token) || Token::isCountOp(token))
if (token == Token::NEW)
{
expectToken(Token::NEW);
ASTPointer<Identifier> contractName = ASTNodeFactory(*this).createNode<Identifier>(expectIdentifierToken());
expectToken(Token::LPAREN);
vector<ASTPointer<Expression>> arguments(parseFunctionCallArguments());
expectToken(Token::RPAREN);
nodeFactory.markEndPosition();
return nodeFactory.createNode<NewExpression>(contractName, arguments);
}
else if (Token::isUnaryOp(token) || Token::isCountOp(token))
{
// prefix expression
m_scanner->next();
@ -556,6 +595,20 @@ vector<ASTPointer<Expression>> Parser::parseFunctionCallArguments()
return arguments;
}
bool Parser::peekVariableDefinition()
{
// distinguish between variable definition (and potentially assignment) and expression statement
// (which include assignments to other expressions and pre-declared variables)
// We have a variable definition if we get a keyword that specifies a type name, or
// in the case of a user-defined type, we have two identifiers following each other.
return (m_scanner->getCurrentToken() == Token::MAPPING ||
m_scanner->getCurrentToken() == Token::VAR ||
((Token::isElementaryTypeName(m_scanner->getCurrentToken()) ||
m_scanner->getCurrentToken() == Token::IDENTIFIER) &&
m_scanner->peekNextToken() == Token::IDENTIFIER));
}
void Parser::expectToken(Token::Value _value)
{
if (m_scanner->getCurrentToken() != _value)

5
libsolidity/Parser.h

@ -59,6 +59,8 @@ private:
ASTPointer<Statement> parseStatement();
ASTPointer<IfStatement> parseIfStatement();
ASTPointer<WhileStatement> parseWhileStatement();
ASTPointer<ForStatement> parseForStatement();
ASTPointer<Statement> parseVarDefOrExprStmt();
ASTPointer<VariableDefinition> parseVariableDefinition();
ASTPointer<ExpressionStatement> parseExpressionStatement();
ASTPointer<Expression> parseExpression();
@ -72,6 +74,9 @@ private:
///@{
///@name Helper functions
/// Peeks ahead in the scanner to determine if a variable definition is going to follow
bool peekVariableDefinition();
/// If current token value is not _value, throw exception otherwise advance token.
void expectToken(Token::Value _value);
Token::Value expectAssignmentOperator();

13
libsolidity/Scanner.cpp

@ -52,6 +52,7 @@
#include <algorithm>
#include <tuple>
#include <libsolidity/Utils.h>
#include <libsolidity/Scanner.h>
using namespace std;
@ -249,8 +250,7 @@ Token::Value Scanner::scanDocumentationComment()
Token::Value Scanner::skipMultiLineComment()
{
if (asserts(m_char == '*'))
BOOST_THROW_EXCEPTION(InternalCompilerError());
solAssert(m_char == '*', "");
advance();
while (!isSourcePastEndOfInput())
{
@ -597,8 +597,7 @@ Token::Value Scanner::scanNumber(char _charSeen)
// scan exponent, if any
if (m_char == 'e' || m_char == 'E')
{
if (asserts(kind != HEX)) // 'e'/'E' must be scanned as part of the hex number
BOOST_THROW_EXCEPTION(InternalCompilerError());
solAssert(kind != HEX, "'e'/'E' must be scanned as part of the hex number");
if (kind != DECIMAL)
return Token::ILLEGAL;
// scan exponent
@ -639,8 +638,7 @@ static Token::Value keywordOrIdentifierToken(string const& _input)
Token::Value Scanner::scanIdentifierOrKeyword()
{
if (asserts(isIdentifierStart(m_char)))
BOOST_THROW_EXCEPTION(InternalCompilerError());
solAssert(isIdentifierStart(m_char), "");
LiteralScope literal(this, LITERAL_TYPE_STRING);
addLiteralCharAndAdvance();
// Scan the rest of the identifier characters.
@ -662,8 +660,7 @@ char CharStream::advanceAndGet(size_t _chars)
char CharStream::rollback(size_t _amount)
{
if (asserts(m_pos >= _amount))
BOOST_THROW_EXCEPTION(InternalCompilerError());
solAssert(m_pos >= _amount, "");
m_pos -= _amount;
return get();
}

13
libsolidity/Token.h

@ -44,6 +44,7 @@
#include <libdevcore/Common.h>
#include <libdevcore/Log.h>
#include <libsolidity/Utils.h>
#include <libsolidity/Exceptions.h>
namespace dev
@ -344,8 +345,7 @@ public:
// (e.g. "LT" for the token LT).
static char const* getName(Value tok)
{
if (asserts(tok < NUM_TOKENS))
BOOST_THROW_EXCEPTION(InternalCompilerError());
solAssert(tok < NUM_TOKENS, "");
return m_name[tok];
}
@ -360,8 +360,7 @@ public:
static Value AssignmentToBinaryOp(Value op)
{
if (asserts(isAssignmentOp(op) && op != ASSIGN))
BOOST_THROW_EXCEPTION(InternalCompilerError());
solAssert(isAssignmentOp(op) && op != ASSIGN, "");
return Token::Value(op + (BIT_OR - ASSIGN_BIT_OR));
}
@ -375,8 +374,7 @@ public:
// have a (unique) string (e.g. an IDENTIFIER).
static char const* toString(Value tok)
{
if (asserts(tok < NUM_TOKENS))
BOOST_THROW_EXCEPTION(InternalCompilerError());
solAssert(tok < NUM_TOKENS, "");
return m_string[tok];
}
@ -384,8 +382,7 @@ public:
// operators; returns 0 otherwise.
static int precedence(Value tok)
{
if (asserts(tok < NUM_TOKENS))
BOOST_THROW_EXCEPTION(InternalCompilerError());
solAssert(tok < NUM_TOKENS, "");
return m_precedence[tok];
}

26
libsolidity/Types.cpp

@ -22,6 +22,7 @@
#include <libdevcore/CommonIO.h>
#include <libdevcore/CommonData.h>
#include <libsolidity/Utils.h>
#include <libsolidity/Types.h>
#include <libsolidity/AST.h>
@ -34,8 +35,7 @@ namespace solidity
shared_ptr<Type const> Type::fromElementaryTypeName(Token::Value _typeToken)
{
if (asserts(Token::isElementaryTypeName(_typeToken)))
BOOST_THROW_EXCEPTION(InternalCompilerError());
solAssert(Token::isElementaryTypeName(_typeToken), "");
if (Token::INT <= _typeToken && _typeToken <= Token::HASH256)
{
@ -120,8 +120,8 @@ IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier):
{
if (isAddress())
m_bits = 160;
if (asserts(m_bits > 0 && m_bits <= 256 && m_bits % 8 == 0))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid bit number for integer type: " + dev::toString(_bits)));
solAssert(m_bits > 0 && m_bits <= 256 && m_bits % 8 == 0,
"Invalid bit number for integer type: " + dev::toString(_bits));
}
bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
@ -215,9 +215,8 @@ shared_ptr<StaticStringType> StaticStringType::smallestTypeForLiteral(string con
StaticStringType::StaticStringType(int _bytes): m_bytes(_bytes)
{
if (asserts(m_bytes >= 0 && m_bytes <= 32))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid byte number for static string type: " +
dev::toString(m_bytes)));
solAssert(m_bytes >= 0 && m_bytes <= 32,
"Invalid byte number for static string type: " + dev::toString(m_bytes));
}
bool StaticStringType::isImplicitlyConvertibleTo(Type const& _convertTo) const
@ -310,6 +309,19 @@ MemberList const& ContractType::getMembers() const
return *m_members;
}
shared_ptr<FunctionType const> const& ContractType::getConstructorType() const
{
if (!m_constructorType)
{
FunctionDefinition const* constructor = m_contract.getConstructor();
if (constructor)
m_constructorType = make_shared<FunctionType const>(*constructor);
else
m_constructorType = make_shared<FunctionType const>(TypePointers(), TypePointers());
}
return m_constructorType;
}
unsigned ContractType::getFunctionIndex(string const& _functionName) const
{
unsigned index = 0;

11
libsolidity/Types.h

@ -39,6 +39,7 @@ namespace solidity
// @todo realMxN, dynamic strings, text, arrays
class Type; // forward
class FunctionType; // forward
using TypePointer = std::shared_ptr<Type const>;
using TypePointers = std::vector<TypePointer>;
@ -249,10 +250,16 @@ public:
virtual MemberList const& getMembers() const override;
/// Returns the function type of the constructor. Note that the location part of the function type
/// is not used, as this type cannot be the type of a variable or expression.
std::shared_ptr<FunctionType const> const& getConstructorType() const;
unsigned getFunctionIndex(std::string const& _functionName) const;
private:
ContractDefinition const& m_contract;
/// Type of the constructor, @see getConstructorType. Lazily initialized.
mutable std::shared_ptr<FunctionType const> m_constructorType;
/// List of member types, will be lazy-initialized because of recursive references.
mutable std::unique_ptr<MemberList> m_members;
};
@ -339,8 +346,8 @@ public:
virtual std::string toString() const override;
virtual bool canLiveOutsideStorage() const override { return false; }
TypePointer getKeyType() const { return m_keyType; }
TypePointer getValueType() const { return m_valueType; }
TypePointer const& getKeyType() const { return m_keyType; }
TypePointer const& getValueType() const { return m_valueType; }
private:
TypePointer m_keyType;

49
libsolidity/Utils.h

@ -0,0 +1,49 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity Utilities.
*/
#pragma once
#include <string>
#include <libsolidity/Exceptions.h>
namespace dev
{
namespace solidity
{
/// Assertion that throws an InternalCompilerError containing the given description if it is not met.
#define solAssert(CONDITION, DESCRIPTION) \
::dev::solidity::solAssertAux(CONDITION, DESCRIPTION, __LINE__, __FILE__, ETH_FUNC)
inline void solAssertAux(bool _condition, std::string const& _errorDescription, unsigned _line,
char const* _file, char const* _function)
{
if (!_condition)
::boost::throw_exception( InternalCompilerError()
<< errinfo_comment(_errorDescription)
<< ::boost::throw_function(_function)
<< ::boost::throw_file(_file)
<< ::boost::throw_line(_line));
}
}
}

10
libsolidity/grammar.txt

@ -16,20 +16,24 @@ Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')'
Block = '{' Statement* '}'
Statement = IfStatement | WhileStatement | Block |
( Continue | Break | Return | VariableDefinition | Expression ) ';'
( Continue | Break | Return | VariableDefinition | ExpressionStatement ) ';'
ExpressionStatement = Expression
IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )?
WhileStatement = 'while' '(' Expression ')' Statement
VardefOrExprStmt = Variabledefinition | ExpressionStatement
ForStatement = 'for' '(' (VardefOrExprStmt)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement
Continue = 'continue' ';'
Break = 'break' ';'
Return = 'return' Expression? ';'
VariableDefinition = VariableDeclaration ( = Expression )? ';'
Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | IndexAccess |
Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | NewExpression | IndexAccess |
MemberAccess | PrimaryExpression
// The expression syntax is actually much more complicated
Assignment = Expression (AssignmentOp Expression)
FunctionCall = Expression '(' ( Expression ( ',' Expression )* ) ')'
FunctionCall = Expression '(' Expression ( ',' Expression )* ')'
NewExpression = 'new' Identifier '(' ( Expression ( ',' Expression )* ) ')'
MemberAccess = Expression '.' Identifier
IndexAccess = Expression '[' Expresison ']'
PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')'

99
solc/CommandLineInterface.cpp

@ -48,6 +48,16 @@ namespace dev
namespace solidity
{
// LTODO: Maybe some argument class pairing names with
// extensions and other attributes would be a better choice here?
static string const g_argAbiStr = "abi";
static string const g_argAsmStr = "asm";
static string const g_argAstStr = "ast";
static string const g_argBinaryStr = "binary";
static string const g_argOpcodesStr = "opcodes";
static string const g_argNatspecDevStr = "natspec-dev";
static string const g_argNatspecUserStr = "natspec-user";
static void version()
{
cout << "solc, the solidity complier commandline interface " << dev::Version << endl
@ -56,15 +66,16 @@ static void version()
exit(0);
}
static inline bool argToStdout(po::variables_map const& _args, const char* _name)
static inline bool argToStdout(po::variables_map const& _args, string const& _name)
{
return _args.count(_name) && _args[_name].as<OutputType>() != OutputType::FILE;
}
static bool needStdout(po::variables_map const& _args)
{
return argToStdout(_args, "abi") || argToStdout(_args, "natspec-user") || argToStdout(_args, "natspec-dev") ||
argToStdout(_args, "asm") || argToStdout(_args, "opcodes") || argToStdout(_args, "binary");
return argToStdout(_args, g_argAbiStr) || argToStdout(_args, g_argNatspecUserStr) ||
argToStdout(_args, g_argNatspecDevStr) || argToStdout(_args, g_argAsmStr) ||
argToStdout(_args, g_argOpcodesStr) || argToStdout(_args, g_argBinaryStr);
}
static inline bool outputToFile(OutputType type)
@ -94,7 +105,7 @@ static std::istream& operator>>(std::istream& _in, OutputType& io_output)
void CommandLineInterface::handleBinary(string const& _contract)
{
auto choice = m_args["binary"].as<OutputType>();
auto choice = m_args[g_argBinaryStr].as<OutputType>();
if (outputToStdout(choice))
{
cout << "Binary: " << endl;
@ -111,28 +122,27 @@ void CommandLineInterface::handleBinary(string const& _contract)
void CommandLineInterface::handleOpcode(string const& _contract)
{
// TODO: Figure out why the wrong operator << (from boost) is used here
auto choice = m_args["opcode"].as<OutputType>();
auto choice = m_args[g_argOpcodesStr].as<OutputType>();
if (outputToStdout(choice))
{
cout << "Opcodes: " << endl;
dev::operator<<(cout, m_compiler.getBytecode(_contract));
cout << eth::disassemble(m_compiler.getBytecode(_contract));
cout << endl;
}
if (outputToFile(choice))
{
ofstream outFile(_contract + ".opcode");
dev::operator<<(outFile, m_compiler.getBytecode(_contract));
outFile << eth::disassemble(m_compiler.getBytecode(_contract));
outFile.close();
}
}
void CommandLineInterface::handleBytecode(string const& _contract)
{
if (m_args.count("opcodes"))
if (m_args.count(g_argOpcodesStr))
handleOpcode(_contract);
if (m_args.count("binary"))
if (m_args.count(g_argBinaryStr))
handleBinary(_contract);
}
@ -145,17 +155,17 @@ void CommandLineInterface::handleJson(DocumentationType _type,
switch(_type)
{
case DocumentationType::ABI_INTERFACE:
argName = "abi";
argName = g_argAbiStr;
suffix = ".abi";
title = "Contract ABI";
break;
case DocumentationType::NATSPEC_USER:
argName = "natspec-user";
argName = "g_argNatspecUserStr";
suffix = ".docuser";
title = "User Documentation";
break;
case DocumentationType::NATSPEC_DEV:
argName = "natspec-dev";
argName = g_argNatspecDevStr;
suffix = ".docdev";
title = "Developer Documentation";
break;
@ -195,20 +205,20 @@ bool CommandLineInterface::parseArguments(int argc, char** argv)
("version", "Show version and exit")
("optimize", po::value<bool>()->default_value(false), "Optimize bytecode for size")
("input-file", po::value<vector<string>>(), "input file")
("ast", po::value<OutputType>(),
(g_argAstStr.c_str(), po::value<OutputType>(),
"Request to output the AST of the contract. " OUTPUT_TYPE_STR)
("asm", po::value<OutputType>(),
"Request to output the EVM assembly of the contract. " OUTPUT_TYPE_STR)
("opcodes", po::value<OutputType>(),
"Request to output the Opcodes of the contract. " OUTPUT_TYPE_STR)
("binary", po::value<OutputType>(),
"Request to output the contract in binary (hexadecimal). " OUTPUT_TYPE_STR)
("abi", po::value<OutputType>(),
"Request to output the contract's ABI interface. " OUTPUT_TYPE_STR)
("natspec-user", po::value<OutputType>(),
"Request to output the contract's Natspec user documentation. " OUTPUT_TYPE_STR)
("natspec-dev", po::value<OutputType>(),
"Request to output the contract's Natspec developer documentation. " OUTPUT_TYPE_STR);
(g_argAsmStr.c_str(), po::value<OutputType>(),
"Request to output the EVM assembly of the contract. " OUTPUT_TYPE_STR)
(g_argOpcodesStr.c_str(), po::value<OutputType>(),
"Request to output the Opcodes of the contract. " OUTPUT_TYPE_STR)
(g_argBinaryStr.c_str(), po::value<OutputType>(),
"Request to output the contract in binary (hexadecimal). " OUTPUT_TYPE_STR)
(g_argAbiStr.c_str(), po::value<OutputType>(),
"Request to output the contract's ABI interface. " OUTPUT_TYPE_STR)
(g_argNatspecUserStr.c_str(), po::value<OutputType>(),
"Request to output the contract's Natspec user documentation. " OUTPUT_TYPE_STR)
(g_argNatspecDevStr.c_str(), po::value<OutputType>(),
"Request to output the contract's Natspec developer documentation. " OUTPUT_TYPE_STR);
#undef OUTPUT_TYPE_STR
// All positional options should be interpreted as input files
@ -220,9 +230,9 @@ bool CommandLineInterface::parseArguments(int argc, char** argv)
{
po::store(po::command_line_parser(argc, argv).options(desc).positional(p).allow_unregistered().run(), m_args);
}
catch (po::error const& exception)
catch (po::error const& _exception)
{
cout << exception.what() << endl;
cout << _exception.what() << endl;
return false;
}
po::notify(m_args);
@ -279,34 +289,35 @@ bool CommandLineInterface::processInput()
// TODO: Perhaps we should not compile unless requested
m_compiler.compile(m_args["optimize"].as<bool>());
}
catch (ParserError const& exception)
catch (ParserError const& _exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Parser error", m_compiler);
SourceReferenceFormatter::printExceptionInformation(cerr, _exception, "Parser error", m_compiler);
return false;
}
catch (DeclarationError const& exception)
catch (DeclarationError const& _exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Declaration error", m_compiler);
SourceReferenceFormatter::printExceptionInformation(cerr, _exception, "Declaration error", m_compiler);
return false;
}
catch (TypeError const& exception)
catch (TypeError const& _exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Type error", m_compiler);
SourceReferenceFormatter::printExceptionInformation(cerr, _exception, "Type error", m_compiler);
return false;
}
catch (CompilerError const& exception)
catch (CompilerError const& _exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Compiler error", m_compiler);
SourceReferenceFormatter::printExceptionInformation(cerr, _exception, "Compiler error", m_compiler);
return false;
}
catch (InternalCompilerError const& exception)
catch (InternalCompilerError const& _exception)
{
SourceReferenceFormatter::printExceptionInformation(cerr, exception, "Internal compiler error", m_compiler);
cerr << "Internal compiler error during compilation:" << endl
<< boost::diagnostic_information(_exception);
return false;
}
catch (Exception const& exception)
catch (Exception const& _exception)
{
cerr << "Exception during compilation: " << boost::diagnostic_information(exception) << endl;
cerr << "Exception during compilation: " << boost::diagnostic_information(_exception) << endl;
return false;
}
catch (...)
@ -321,9 +332,9 @@ bool CommandLineInterface::processInput()
void CommandLineInterface::actOnInput()
{
// do we need AST output?
if (m_args.count("ast"))
if (m_args.count(g_argAstStr))
{
auto choice = m_args["ast"].as<OutputType>();
auto choice = m_args[g_argAstStr].as<OutputType>();
if (outputToStdout(choice))
{
cout << "Syntax trees:" << endl << endl;
@ -355,9 +366,9 @@ void CommandLineInterface::actOnInput()
cout << endl << "======= " << contract << " =======" << endl;
// do we need EVM assembly?
if (m_args.count("asm"))
if (m_args.count(g_argAsmStr))
{
auto choice = m_args["asm"].as<OutputType>();
auto choice = m_args[g_argAsmStr].as<OutputType>();
if (outputToStdout(choice))
{
cout << "EVM assembly:" << endl;

1
test/CMakeLists.txt

@ -3,6 +3,7 @@ cmake_policy(SET CMP0015 NEW)
aux_source_directory(. SRC_LIST)
list(REMOVE_ITEM SRC_LIST "./createRandomTest.cpp")
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${CRYPTOPP_INCLUDE_DIRS})
include_directories(${JSONCPP_INCLUDE_DIRS})
include_directories(${JSON_RPC_CPP_INCLUDE_DIRS})

13
test/solidityCompiler.cpp

@ -54,9 +54,18 @@ bytes compileContract(const string& _sourceCode)
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract));
}
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
BOOST_REQUIRE_NO_THROW(resolver.checkTypeRequirements(*contract));
}
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
Compiler compiler;
compiler.compileContract(*contract, {});
compiler.compileContract(*contract, {}, map<ContractDefinition const*, bytes const*>{});
// debug
//compiler.streamAssembly(cout);
return compiler.getAssembledBytecode();

117
test/solidityEndToEndTest.cpp

@ -176,6 +176,80 @@ BOOST_AUTO_TEST_CASE(nested_loops)
testSolidityAgainstCppOnRange(0, nested_loops_cpp, 0, 12);
}
BOOST_AUTO_TEST_CASE(for_loop)
{
char const* sourceCode = "contract test {\n"
" function f(uint n) returns(uint nfac) {\n"
" nfac = 1;\n"
" for (var i = 2; i <= n; i++)\n"
" nfac *= i;\n"
" }\n"
"}\n";
compileAndRun(sourceCode);
auto for_loop_cpp = [](u256 const& n) -> u256
{
u256 nfac = 1;
for (auto i = 2; i <= n; i++)
nfac *= i;
return nfac;
};
testSolidityAgainstCppOnRange(0, for_loop_cpp, 0, 5);
}
BOOST_AUTO_TEST_CASE(for_loop_empty)
{
char const* sourceCode = "contract test {\n"
" function f() returns(uint ret) {\n"
" ret = 1;\n"
" for (;;)\n"
" {\n"
" ret += 1;\n"
" if (ret >= 10) break;\n"
" }\n"
" }\n"
"}\n";
compileAndRun(sourceCode);
auto for_loop_empty_cpp = []() -> u256
{
u256 ret = 1;
for (;;)
{
ret += 1;
if (ret >= 10) break;
}
return ret;
};
testSolidityAgainstCpp(0, for_loop_empty_cpp);
}
BOOST_AUTO_TEST_CASE(for_loop_simple_init_expr)
{
char const* sourceCode = "contract test {\n"
" function f(uint n) returns(uint nfac) {\n"
" nfac = 1;\n"
" uint256 i;\n"
" for (i = 2; i <= n; i++)\n"
" nfac *= i;\n"
" }\n"
"}\n";
compileAndRun(sourceCode);
auto for_loop_simple_init_expr_cpp = [](u256 const& n) -> u256
{
u256 nfac = 1;
u256 i;
for (i = 2; i <= n; i++)
nfac *= i;
return nfac;
};
testSolidityAgainstCppOnRange(0, for_loop_simple_init_expr_cpp, 0, 5);
}
BOOST_AUTO_TEST_CASE(calling_other_functions)
{
// note that the index of a function is its index in the sorted sequence of functions
@ -1012,6 +1086,49 @@ BOOST_AUTO_TEST_CASE(strings_in_calls)
BOOST_CHECK(callContractFunction(0, bytes({0, 'a', 1})) == bytes({0, 'a', 0, 0, 0}));
}
BOOST_AUTO_TEST_CASE(constructor_arguments)
{
char const* sourceCode = R"(
contract Helper {
string3 name;
bool flag;
function Helper(string3 x, bool f) {
name = x;
flag = f;
}
function getName() returns (string3 ret) { return name; }
function getFlag() returns (bool ret) { return flag; }
}
contract Main {
Helper h;
function Main() {
h = new Helper("abc", true);
}
function getFlag() returns (bool ret) { return h.getFlag(); }
function getName() returns (string3 ret) { return h.getName(); }
})";
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(0) == bytes({byte(0x01)}));
BOOST_REQUIRE(callContractFunction(1) == bytes({'a', 'b', 'c'}));
}
BOOST_AUTO_TEST_CASE(functions_called_by_constructor)
{
char const* sourceCode = R"(
contract Test {
string3 name;
bool flag;
function Test() {
setName("abc");
}
function getName() returns (string3 ret) { return name; }
private:
function setName(string3 _name) { name = _name; }
})";
compileAndRun(sourceCode);
BOOST_REQUIRE(callContractFunction(0) == bytes({'a', 'b', 'c'}));
}
BOOST_AUTO_TEST_SUITE_END()
}

9
test/solidityExpressionCompiler.cpp

@ -97,6 +97,15 @@ bytes compileFirstExpression(const string& _sourceCode, vector<vector<string>> _
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract));
}
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
BOOST_REQUIRE_NO_THROW(resolver.checkTypeRequirements(*contract));
}
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
FirstExpressionExtractor extractor(*contract);
BOOST_REQUIRE(extractor.getExpression() != nullptr);

27
test/solidityNameAndTypeResolution.cpp

@ -47,6 +47,9 @@ void parseTextAndResolveNames(std::string const& _source)
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
resolver.resolveNamesAndTypes(*contract);
for (ASTPointer<ASTNode> const& node: sourceUnit->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
resolver.checkTypeRequirements(*contract);
}
}
@ -284,6 +287,30 @@ BOOST_AUTO_TEST_CASE(assignment_to_struct)
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(returns_in_constructor)
{
char const* text = "contract test {\n"
" function test() returns (uint a) {\n"
" }\n"
"}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_CASE(forward_function_reference)
{
char const* text = "contract First {\n"
" function fun() returns (bool ret) {\n"
" return Second(1).fun(1, true, 3) > 0;\n"
" }\n"
"}\n"
"contract Second {\n"
" function fun(uint a, bool b, uint c) returns (uint ret) {\n"
" if (First(2).fun() == true) return 1;\n"
" }\n"
"}\n";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_SUITE_END()
}

64
test/solidityParser.cpp

@ -49,6 +49,23 @@ ASTPointer<ContractDefinition> parseText(std::string const& _source)
BOOST_FAIL("No contract found in source.");
return ASTPointer<ContractDefinition>();
}
ASTPointer<ContractDefinition> parseTextExplainError(std::string const& _source)
{
try
{
return parseText(_source);
}
catch (Exception const& exception)
{
// LTODO: Print the error in a kind of a better way?
// In absence of CompilerStack we can't use SourceReferenceFormatter
cout << "Exception while parsing: " << diagnostic_information(exception);
// rethrow to signal test failure
throw exception;
}
}
}
@ -357,6 +374,53 @@ BOOST_AUTO_TEST_CASE(while_loop)
BOOST_CHECK_NO_THROW(parseText(text));
}
BOOST_AUTO_TEST_CASE(for_loop_vardef_initexpr)
{
char const* text = "contract test {\n"
" function fun(uint256 a) {\n"
" for (uint256 i = 0; i < 10; i++)\n"
" { uint256 x = i; break; continue; }\n"
" }\n"
"}\n";
BOOST_CHECK_NO_THROW(parseTextExplainError(text));
}
BOOST_AUTO_TEST_CASE(for_loop_simple_initexpr)
{
char const* text = "contract test {\n"
" function fun(uint256 a) {\n"
" uint256 i =0;\n"
" for (i = 0; i < 10; i++)\n"
" { uint256 x = i; break; continue; }\n"
" }\n"
"}\n";
BOOST_CHECK_NO_THROW(parseTextExplainError(text));
}
BOOST_AUTO_TEST_CASE(for_loop_simple_noexpr)
{
char const* text = "contract test {\n"
" function fun(uint256 a) {\n"
" uint256 i =0;\n"
" for (;;)\n"
" { uint256 x = i; break; continue; }\n"
" }\n"
"}\n";
BOOST_CHECK_NO_THROW(parseTextExplainError(text));
}
BOOST_AUTO_TEST_CASE(for_loop_single_stmt_body)
{
char const* text = "contract test {\n"
" function fun(uint256 a) {\n"
" uint256 i =0;\n"
" for (i = 0; i < 10; i++)\n"
" continue;\n"
" }\n"
"}\n";
BOOST_CHECK_NO_THROW(parseTextExplainError(text));
}
BOOST_AUTO_TEST_CASE(if_statement)
{
char const* text = "contract test {\n"

40
test/vm.cpp

@ -262,12 +262,44 @@ eth::OnOpFunc FakeExtVM::simpleTrace()
dev::LogOutputStream<eth::VMTraceChannel, false>(true) << o.str();
dev::LogOutputStream<eth::VMTraceChannel, false>(false) << " | " << std::dec << ext.depth << " | " << ext.myAddress << " | #" << steps << " | " << std::hex << std::setw(4) << std::setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " | " << std::dec << vm.gas() << " | -" << std::dec << gasCost << " | " << newMemSize << "x32" << " ]";
/*creates json stack trace*/
if (eth::VMTraceChannel::verbosity <= g_logVerbosity)
{
std::ofstream f;
f.open("./vmtrace.log", std::ofstream::app);
f << o.str();
f << " | " << std::dec << ext.depth << " | " << ext.myAddress << " | #" << steps << " | " << std::hex << std::setw(4) << std::setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " | " << std::dec << vm.gas() << " | -" << std::dec << gasCost << " | " << newMemSize << "x32";
Object o_step;
/*add the stack*/
Array a_stack;
for (auto i: vm.stack())
a_stack.push_back((string)i);
o_step.push_back(Pair( "stack", a_stack ));
/*add the memory*/
Array a_mem;
for(auto i: vm.memory())
a_mem.push_back(i);
o_step.push_back(Pair("memory", a_mem));
/*add the storage*/
Object storage;
for (auto const& i: std::get<2>(ext.addresses.find(ext.myAddress)->second))
storage.push_back(Pair( (string)i.first , (string)i.second));
/*add all the other details*/
o_step.push_back(Pair("storage", storage));
o_step.push_back(Pair("depth", to_string(ext.depth)));
o_step.push_back(Pair("gas", (string)vm.gas()));
o_step.push_back(Pair("address", "0x" + toString(ext.myAddress )));
o_step.push_back(Pair("step", steps ));
o_step.push_back(Pair("pc", (int)vm.curPC()));
o_step.push_back(Pair("opcode", instructionInfo(inst).name ));
/*append the JSON object to the log file*/
Value v(o_step);
ofstream os( "./stackTrace.json", ofstream::app);
os << write_string(v, true) << ",";
os.close();
}
};
}

2
test/vm.h

@ -26,7 +26,7 @@ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
#include <fstream>
#include <cstdint>
#include <boost/test/unit_test.hpp>
#include "JsonSpiritHeaders.h"
#include <json_spirit/json_spirit.h>
#include <libdevcore/Log.h>
#include <libdevcore/CommonIO.h>
#include <libevmcore/Instruction.h>

Loading…
Cancel
Save