Merge pull request #1107 from ethereum/develop

Version 0.4.2
This commit is contained in:
chriseth 2016-09-17 15:25:54 +02:00 committed by GitHub
commit af6afb0415
18 changed files with 276 additions and 50 deletions

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
.commit_hash.txt
.prerelease.txt
# Compiled Object files
*.slo
*.lo

View File

@ -8,7 +8,7 @@ include(EthPolicy)
eth_policy()
# project name and version should be set after cmake_policy CMP0048
set(PROJECT_VERSION "0.4.1")
set(PROJECT_VERSION "0.4.2")
project(solidity VERSION ${PROJECT_VERSION})
# Let's find our dependencies

View File

@ -1,3 +1,16 @@
### 0.4.2 (2016-09-17)
Bugfixes:
* Code Generator: Fix library functions being called from payable functions.
* Type Checker: Fixed a crash about invalid array types.
* Code Generator: Fixed a call gas bug that became visible after
version 0.4.0 for calls where the output is larger than the input.
### 0.4.1 (2016-09-09)
* Build System: Fixes to allow library compilation.
### 0.4.0 (2016-09-08)
This release deliberately breaks backwards compatibility mostly to

View File

@ -26,6 +26,7 @@ if (EXISTS ${ETH_SOURCE_DIR}/prerelease.txt)
string(STRIP "${SOL_VERSION_PRERELEASE}" SOL_VERSION_PRERELEASE)
else()
string(TIMESTAMP SOL_VERSION_PRERELEASE "develop.%Y.%m.%d" UTC)
string(REPLACE .0 . SOL_VERSION_PRERELEASE "${SOL_VERSION_PRERELEASE}")
endif()
if (EXISTS ${ETH_SOURCE_DIR}/commit_hash.txt)
@ -33,7 +34,7 @@ if (EXISTS ${ETH_SOURCE_DIR}/commit_hash.txt)
string(STRIP ${SOL_COMMIT_HASH} SOL_COMMIT_HASH)
else()
execute_process(
COMMAND git --git-dir=${ETH_SOURCE_DIR}/.git --work-tree=${ETH_SOURCE_DIR} rev-parse HEAD
COMMAND git --git-dir=${ETH_SOURCE_DIR}/.git --work-tree=${ETH_SOURCE_DIR} rev-parse --short=8 HEAD
OUTPUT_VARIABLE SOL_COMMIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET
)
execute_process(
@ -47,14 +48,17 @@ if (SOL_COMMIT_HASH)
string(SUBSTRING ${SOL_COMMIT_HASH} 0 8 SOL_COMMIT_HASH)
endif()
if (SOL_COMMIT_HASH AND SOL_LOCAL_CHANGES)
set(SOL_COMMIT_HASH "${SOL_COMMIT_HASH}.mod")
endif()
if (NOT SOL_COMMIT_HASH)
message(FATAL_ERROR "Unable to determine commit hash. Either compile from within git repository or "
"supply a file called commit_hash.txt")
endif()
if (NOT SOL_COMMIT_HASH MATCHES [a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9])
message(FATAL_ERROR "Malformed commit hash \"${SOL_COMMIT_HASH}\". It has to consist of exactly 8 hex digits.")
endif()
if (SOL_COMMIT_HASH AND SOL_LOCAL_CHANGES)
set(SOL_COMMIT_HASH "${SOL_COMMIT_HASH}.mod")
endif()
set(SOL_VERSION_BUILDINFO "commit.${SOL_COMMIT_HASH}.${ETH_BUILD_PLATFORM}")

View File

@ -56,9 +56,9 @@ copyright = '2016, Ethereum'
# built documents.
#
# The short X.Y version.
version = '0.2.0'
version = '0.4.1'
# The full version, including alpha/beta/rc tags.
release = '0.2.0'
release = '0.4.1-develop'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@ -6,6 +6,15 @@
Installing Solidity
###################
Versioning
==========
Solidity versions follow `semantic versioning <https://semver.org>` and in addition to
releases, **nightly development builds** are also made available. The nightly builds
are not guaranteed to be working and despite best efforts they might contain undocumented
and/or broken changes. We recommend to use the latest release. Package installers below
will use the latest release.
Browser-Solidity
================
@ -186,3 +195,20 @@ Alternatively, you can build for Windows on the command-line, like so:
.. code:: bash
cmake --build . --config RelWithDebInfo
Important information about versioning
======================================
After a release is made, the patch version level is bumped, because we assume that only
patch level changes follow. When changes are merged, the version should be bumped according
to semver and the severity of the change. Finally, a release is always made with the version
of the current nightly build, but without the ``prerelease`` specifier.
Example:
- 0) the 0.4.0 release is made
- 1) nightly build has a version of 0.4.1 from now on
- 2) non-breaking changes are introduced - no change in version
- 3) a breaking change is introduced - version is bumped to 0.5.0
- 4) the 0.5.0 release is made
This behaviour works well with the version pragma.

View File

@ -73,7 +73,7 @@ inline bool assertEqualAux(A const& _a, B const& _b, char const* _aStr, char con
/// Use it as assertThrow(1 == 1, ExceptionType, "Mathematics is wrong.");
/// Do NOT supply an exception object as the second parameter.
#define assertThrow(_condition, _ExceptionType, _description) \
::dev::assertThrowAux<_ExceptionType>(_condition, _description, __LINE__, __FILE__, ETH_FUNC)
::dev::assertThrowAux<_ExceptionType>(!!(_condition), _description, __LINE__, __FILE__, ETH_FUNC)
using errinfo_comment = boost::error_info<struct tag_comment, std::string>;
@ -96,16 +96,4 @@ inline void assertThrowAux(
);
}
template <class _ExceptionType>
inline void assertThrowAux(
void const* _pointer,
::std::string const& _errorDescription,
unsigned _line,
char const* _file,
char const* _function
)
{
assertThrowAux<_ExceptionType>(_pointer != nullptr, _errorDescription, _line, _file, _function);
}
}

View File

@ -1438,7 +1438,7 @@ bool TypeChecker::visit(IndexAccess const& _access)
length->literalValue(nullptr)
));
else
typeError(index->location(), "Integer constant expected.");
fatalTypeError(index->location(), "Integer constant expected.");
}
break;
}

View File

@ -263,7 +263,9 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
CompilerContext::LocationSetter locationSetter(m_context, functionType->declaration());
m_context << callDataUnpackerEntryPoints.at(it.first);
if (!functionType->isPayable())
// We have to allow this for libraries, because value of the previous
// call is still visible in the delegatecall.
if (!functionType->isPayable() && !_contract.isLibrary())
{
// Throw if function is not payable but call contained ether.
m_context << Instruction::CALLVALUE;

View File

@ -1476,6 +1476,18 @@ void ExpressionCompiler::appendExternalFunctionCall(
utils().storeFreeMemoryPointer();
}
// Touch the end of the output area so that we do not pay for memory resize during the call
// (which we would have to subtract from the gas left)
// We could also just use MLOAD; POP right before the gas calculation, but the optimizer
// would remove that, so we use MSTORE here.
if (!_functionType.gasSet() && retSize > 0)
{
m_context << u256(0);
utils().fetchFreeMemoryPointer();
// This touches too much, but that way we save some rounding arithmetics
m_context << u256(retSize) << Instruction::ADD << Instruction::MSTORE;
}
// Copy function identifier to memory.
utils().fetchFreeMemoryPointer();
if (!_functionType.isBareCall() || manualFunctionId)
@ -1551,10 +1563,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
gasNeededByCaller += eth::GasCosts::callValueTransferGas;
if (!isCallCode && !isDelegateCall && !existenceChecked)
gasNeededByCaller += eth::GasCosts::callNewAccountGas; // we never know
m_context <<
gasNeededByCaller <<
Instruction::GAS <<
Instruction::SUB;
m_context << gasNeededByCaller << Instruction::GAS << Instruction::SUB;
}
if (isDelegateCall)
m_context << Instruction::DELEGATECALL;

View File

@ -36,6 +36,10 @@ bool Why3Translator::process(SourceUnit const& _source)
appendPreface();
_source.accept(*this);
}
catch (NoFormalType&)
{
solAssert(false, "There is a call to toFormalType() that does not catch NoFormalType exceptions.");
}
catch (FatalError& /*_e*/)
{
solAssert(m_errorOccured, "");
@ -77,14 +81,30 @@ string Why3Translator::toFormalType(Type const& _type) const
return "uint256";
}
else if (auto type = dynamic_cast<ArrayType const*>(&_type))
{
if (!type->isByteArray() && type->isDynamicallySized() && type->dataStoredIn(DataLocation::Memory))
{
// Not catching NoFormalType exception. Let the caller deal with it.
string base = toFormalType(*type->baseType());
if (!base.empty())
return "array " + base;
return "array " + base;
}
}
else if (auto mappingType = dynamic_cast<MappingType const*>(&_type))
{
solAssert(mappingType->keyType(), "A mappingType misses a keyType.");
if (dynamic_cast<IntegerType const*>(&*mappingType->keyType()))
{
//@TODO Use the information from the key type and specify the length of the array as an invariant.
// Also the constructor need to specify the length of the array.
solAssert(mappingType->valueType(), "A mappingType misses a valueType.");
// Not catching NoFormalType exception. Let the caller deal with it.
string valueTypeFormal = toFormalType(*mappingType->valueType());
return "array " + valueTypeFormal;
}
}
return "";
BOOST_THROW_EXCEPTION(NoFormalType()
<< errinfo_noFormalTypeFrom(_type.toString(true)));
}
void Why3Translator::addLine(string const& _line)
@ -142,9 +162,17 @@ bool Why3Translator::visit(ContractDefinition const& _contract)
m_currentContract.stateVariables = _contract.stateVariables();
for (VariableDeclaration const* variable: m_currentContract.stateVariables)
{
string varType = toFormalType(*variable->annotation().type);
if (varType.empty())
fatalError(*variable, "Type not supported for state variable.");
string varType;
try
{
varType = toFormalType(*variable->annotation().type);
}
catch (NoFormalType &err)
{
string const* typeNamePtr = boost::get_error_info<errinfo_noFormalTypeFrom>(err);
string typeName = typeNamePtr ? " \"" + *typeNamePtr + "\"" : "";
fatalError(*variable, "Type" + typeName + " not supported for state variable.");
}
addLine("mutable _" + variable->name() + ": " + varType);
}
unindent();
@ -218,9 +246,16 @@ bool Why3Translator::visit(FunctionDefinition const& _function)
add(" (this: account)");
for (auto const& param: _function.parameters())
{
string paramType = toFormalType(*param->annotation().type);
if (paramType.empty())
error(*param, "Parameter type not supported.");
string paramType;
try
{
paramType = toFormalType(*param->annotation().type);
}
catch (NoFormalType &err)
{
string const* typeName = boost::get_error_info<errinfo_noFormalTypeFrom>(err);
error(*param, "Parameter type \"" + (typeName ? *typeName : "") + "\" not supported.");
}
if (param->name().empty())
error(*param, "Anonymous function parameters not supported.");
add(" (arg_" + param->name() + ": " + paramType + ")");
@ -232,9 +267,16 @@ bool Why3Translator::visit(FunctionDefinition const& _function)
string retString = "(";
for (auto const& retParam: _function.returnParameters())
{
string paramType = toFormalType(*retParam->annotation().type);
if (paramType.empty())
error(*retParam, "Parameter type not supported.");
string paramType;
try
{
paramType = toFormalType(*retParam->annotation().type);
}
catch (NoFormalType &err)
{
string const* typeName = boost::get_error_info<errinfo_noFormalTypeFrom>(err);
error(*retParam, "Parameter type " + (typeName ? *typeName : "") + " not supported.");
}
if (retString.size() != 1)
retString += ", ";
retString += paramType;
@ -264,14 +306,32 @@ bool Why3Translator::visit(FunctionDefinition const& _function)
{
if (variable->name().empty())
error(*variable, "Unnamed return variables not yet supported.");
string varType = toFormalType(*variable->annotation().type);
string varType;
try
{
varType = toFormalType(*variable->annotation().type);
}
catch (NoFormalType &err)
{
string const* typeNamePtr = boost::get_error_info<errinfo_noFormalTypeFrom>(err);
error(*variable, "Type " + (typeNamePtr ? *typeNamePtr : "") + "in return parameter not yet supported.");
}
addLine("let _" + variable->name() + ": ref " + varType + " = ref (of_int 0) in");
}
for (VariableDeclaration const* variable: _function.localVariables())
{
if (variable->name().empty())
error(*variable, "Unnamed variables not yet supported.");
string varType = toFormalType(*variable->annotation().type);
string varType;
try
{
varType = toFormalType(*variable->annotation().type);
}
catch (NoFormalType &err)
{
string const* typeNamePtr = boost::get_error_info<errinfo_noFormalTypeFrom>(err);
error(*variable, "Type " + (typeNamePtr ? *typeNamePtr : "") + "in variable declaration not yet supported.");
}
addLine("let _" + variable->name() + ": ref " + varType + " = ref (of_int 0) in");
}
addLine("try");
@ -434,8 +494,15 @@ bool Why3Translator::visit(TupleExpression const& _node)
bool Why3Translator::visit(UnaryOperation const& _unaryOperation)
{
if (toFormalType(*_unaryOperation.annotation().type).empty())
error(_unaryOperation, "Type not supported in unary operation.");
try
{
toFormalType(*_unaryOperation.annotation().type);
}
catch (NoFormalType &err)
{
string const* typeNamePtr = boost::get_error_info<errinfo_noFormalTypeFrom>(err);
error(_unaryOperation, "Type \"" + (typeNamePtr ? *typeNamePtr : "") + "\" supported in unary operation.");
}
switch (_unaryOperation.getOperator())
{
@ -798,5 +865,14 @@ module UInt256
type t = uint256,
constant max = max_uint256
end
module Address
use import mach.int.Unsigned
type address
constant max_address: int = 0xffffffffffffffffffffffffffffffffffffffff (* 160 bit = 40 f's *)
clone export mach.int.Unsigned with
type t = address,
constant max = max_address
end
)", 0});
}

View File

@ -60,9 +60,10 @@ private:
/// Appends imports and constants use throughout the formal code.
void appendPreface();
/// @returns a string representation of the corresponding formal type or the empty string
/// if the type is not supported.
/// @returns a string representation of the corresponding formal type or throws NoFormalType exception.
std::string toFormalType(Type const& _type) const;
using errinfo_noFormalTypeFrom = boost::error_info<struct tag_noFormalTypeFrom, std::string /* name of the type that cannot be translated */ >;
struct NoFormalType: virtual Exception {};
void indent() { newLine(); m_lines.back().indentation++; }
void unindent();

View File

@ -29,7 +29,7 @@
set -e
if [[ "$OSTYPE" != "darwin"* ]]; then
date -u +"nightly.%Y.%m.%d" > prerelease.txt
date -u +"nightly.%Y.%-m.%-d" > prerelease.txt
./scripts/travis-emscripten/install_deps.sh
docker run -v $(pwd):/src trzeci/emscripten:sdk-tag-1.35.4-64bit ./scripts/travis-emscripten/build_emscripten.sh
fi

View File

@ -56,6 +56,8 @@ detect_linux_distro() {
elif [ -f /etc/os-release ]; then
# extract 'foo' from NAME=foo, only on the line with NAME=foo
DISTRO=$(sed -n -e 's/^NAME="\(.*\)\"/\1/p' /etc/os-release)
elif [ -f /etc/centos-release ]; then
DISTRO=CentOS
else
DISTRO=''
fi
@ -329,6 +331,51 @@ case $(uname -s) in
sudo apt-get -y install eth
;;
#------------------------------------------------------------------------------
# CentOS
# CentOS needs some more testing. This is the general idea of packages
# needed, but some tweaking/improvements can definitely happen
#------------------------------------------------------------------------------
CentOS)
read -p "This script will heavily modify your system in order to allow for compilation of Solidity. Are you sure? [Y/N]" -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
# Make Sure we have the EPEL repos
sudo yum -y install epel-release
# Get g++ 4.8
sudo rpm --import http://ftp.scientificlinux.org/linux/scientific/5x/x86_64/RPM-GPG-KEYs/RPM-GPG-KEY-cern
wget -O /etc/yum.repos.d/slc6-devtoolset.repo http://linuxsoft.cern.ch/cern/devtoolset/slc6-devtoolset.repo
sudo yum -y install devtoolset-2-gcc devtoolset-2-gcc-c++ devtoolset-2-binutils
# Enable the devtoolset2 usage so global gcc/g++ become the 4.8 one.
# As per https://gist.github.com/stephenturner/e3bc5cfacc2dc67eca8b, what you should do afterwards is
# to add this line:
# source /opt/rh/devtoolset-2/enable
# to your bashrc so that this happens automatically at login
scl enable devtoolset-2 bash
# Get cmake
sudo yum -y remove cmake
sudo yum -y install cmake3
sudo ln -s /usr/bin/cmake3 /usr/bin/cmake
# Get latest boost thanks to this guy: http://vicendominguez.blogspot.de/2014/04/boost-c-library-rpm-packages-for-centos.html
sudo yum -y remove boost-devel
sudo wget http://repo.enetres.net/enetres.repo -O /etc/yum.repos.d/enetres.repo
sudo yum install boost-devel
# And finally jsoncpp
sudo yum -y install jsoncpp-devel
else
echo "Aborted CentOS Solidity Dependency Installation";
exit 1
fi
;;
*)
#------------------------------------------------------------------------------

View File

@ -52,9 +52,9 @@ mv solidity solc
# Determine version
cd solc
version=`grep -oP "PROJECT_VERSION \"?\K[0-9.]+(?=\")"? CMakeLists.txt`
commithash=`git rev-parse --short HEAD`
commithash=`git rev-parse --short=8 HEAD`
committimestamp=`git show --format=%ci HEAD | head -n 1`
commitdate=`git show --format=%ci HEAD | head -n 1 | cut - -b1-10`
commitdate=`git show --format=%ci HEAD | head -n 1 | cut - -b1-10 | sed -e 's/-0?/./' | sed -e 's/-0?/./'`
echo "$commithash" > commit_hash.txt
if [ $branch = develop ]

View File

@ -33,8 +33,11 @@ set -e
VER=$(cat CMakeLists.txt | grep 'set(PROJECT_VERSION' | sed -e 's/.*set(PROJECT_VERSION "\(.*\)".*/\1/')
test -n "$VER"
VER="v$VER"
COMMIT=$(git rev-parse --short HEAD)
DATE=$(date --date="$(git log -1 --date=iso --format=%ad HEAD)" --utc +%Y.%m.%d)
COMMIT=$(git rev-parse --short=8 HEAD)
DATE=$(date --date="$(git log -1 --date=iso --format=%ad HEAD)" --utc +%Y.%-m.%-d)
# remove leading zeros in components - they are not semver-compatible
COMMIT=$(echo "$COMMIT" | sed -e 's/^0*//')
ENCRYPTED_KEY_VAR="encrypted_${ENCRYPTION_LABEL}_key"
ENCRYPTED_IV_VAR="encrypted_${ENCRYPTION_LABEL}_iv"

View File

@ -7144,6 +7144,23 @@ BOOST_AUTO_TEST_CASE(payable_function)
BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 27 + 27);
}
BOOST_AUTO_TEST_CASE(payable_function_calls_library)
{
char const* sourceCode = R"(
library L {
function f() returns (uint) { return 7; }
}
contract C {
function f() payable returns (uint) {
return L.f();
}
}
)";
compileAndRun(sourceCode, 0, "L");
compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"L", m_contractAddress}});
BOOST_CHECK(callContractFunctionWithValue("f()", 27) == encodeArgs(u256(7)));
}
BOOST_AUTO_TEST_CASE(non_payable_throw)
{
char const* sourceCode = R"(
@ -7186,6 +7203,33 @@ BOOST_AUTO_TEST_CASE(no_nonpayable_circumvention_by_modifier)
BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0);
}
BOOST_AUTO_TEST_CASE(mem_resize_is_not_paid_at_call)
{
// This tests that memory resize for return values is not paid during the call, which would
// make the gas calculation overly complex. We access the end of the output area before
// the call is made.
// Tests that this also survives the optimizer.
char const* sourceCode = R"(
contract C {
function f() returns (uint[200]) {}
}
contract D {
function f(C c) returns (uint) { c.f(); return 7; }
}
)";
compileAndRun(sourceCode, 0, "C");
u160 cAddr = m_contractAddress;
compileAndRun(sourceCode, 0, "D");
BOOST_CHECK(callContractFunction("f(address)", cAddr) == encodeArgs(u256(7)));
m_optimize = true;
compileAndRun(sourceCode, 0, "C");
u160 cAddrOpt = m_contractAddress;
compileAndRun(sourceCode, 0, "D");
BOOST_CHECK(callContractFunction("f(address)", cAddrOpt) == encodeArgs(u256(7)));
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -4009,6 +4009,16 @@ BOOST_AUTO_TEST_CASE(external_constructor)
BOOST_CHECK(expectError(text, false) == Error::Type::TypeError);
}
BOOST_AUTO_TEST_CASE(invalid_array_as_statement)
{
char const* text = R"(
contract test {
struct S { uint x; }
function test(uint k) { S[k]; }
}
)";
BOOST_CHECK(expectError(text, false) == Error::Type::TypeError);
}
BOOST_AUTO_TEST_SUITE_END()