aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGav Wood <i@gavwood.com>2015-01-16 03:03:35 +0800
committerGav Wood <i@gavwood.com>2015-01-16 03:03:35 +0800
commita9ed77c22a03271c93ab70b8fcd58363c5e78ab0 (patch)
tree4dbe1c48ff4948e8750071a7627506f3076c8b63
parent911916beb7d8fd9c318d43fdf137727d364a95d2 (diff)
parentc6a47a4dc2e6b5a9fb03e8282b6b3ef108fdcbcb (diff)
downloaddexon-solidity-a9ed77c22a03271c93ab70b8fcd58363c5e78ab0.tar
dexon-solidity-a9ed77c22a03271c93ab70b8fcd58363c5e78ab0.tar.gz
dexon-solidity-a9ed77c22a03271c93ab70b8fcd58363c5e78ab0.tar.bz2
dexon-solidity-a9ed77c22a03271c93ab70b8fcd58363c5e78ab0.tar.lz
dexon-solidity-a9ed77c22a03271c93ab70b8fcd58363c5e78ab0.tar.xz
dexon-solidity-a9ed77c22a03271c93ab70b8fcd58363c5e78ab0.tar.zst
dexon-solidity-a9ed77c22a03271c93ab70b8fcd58363c5e78ab0.zip
Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop
Conflicts: libsolidity/Types.h
-rw-r--r--AST.cpp53
-rwxr-xr-xAST.h12
-rw-r--r--AST_accept.h6
-rw-r--r--Compiler.cpp5
-rw-r--r--Compiler.h2
-rw-r--r--CompilerContext.cpp5
-rw-r--r--CompilerContext.h3
-rw-r--r--CompilerStack.cpp13
-rw-r--r--CompilerStack.h8
-rw-r--r--ExpressionCompiler.cpp206
-rw-r--r--ExpressionCompiler.h18
-rw-r--r--InterfaceHandler.cpp4
-rw-r--r--Parser.cpp24
-rw-r--r--Token.h4
-rw-r--r--Types.cpp51
-rw-r--r--Types.h29
-rw-r--r--grammar.txt2
17 files changed, 292 insertions, 153 deletions
diff --git a/AST.cpp b/AST.cpp
index d171006a..7b633564 100644
--- a/AST.cpp
+++ b/AST.cpp
@@ -50,18 +50,27 @@ void ContractDefinition::checkTypeRequirements()
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
function->checkTypeRequirements();
+
+ // check for hash collisions in function signatures
+ vector<pair<FixedHash<4>, FunctionDefinition const*>> exportedFunctionList = getInterfaceFunctionList();
+ set<FixedHash<4>> hashes;
+ for (auto const& hashAndFunction: getInterfaceFunctionList())
+ {
+ FixedHash<4> const& hash = hashAndFunction.first;
+ if (hashes.count(hash))
+ BOOST_THROW_EXCEPTION(createTypeError("Function signature hash collision for " +
+ hashAndFunction.second->getCanonicalSignature()));
+ hashes.insert(hash);
+ }
}
map<FixedHash<4>, FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const
{
- map<FixedHash<4>, FunctionDefinition const*> exportedFunctions;
- for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
- if (f->isPublic() && f->getName() != getName())
- {
- FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
- auto res = exportedFunctions.insert(std::make_pair(hash,f.get()));
- solAssert(res.second, "Hash collision at Function Definition Hash calculation");
- }
+ vector<pair<FixedHash<4>, FunctionDefinition const*>> exportedFunctionList = getInterfaceFunctionList();
+ map<FixedHash<4>, FunctionDefinition const*> exportedFunctions(exportedFunctionList.begin(),
+ exportedFunctionList.end());
+ solAssert(exportedFunctionList.size() == exportedFunctions.size(),
+ "Hash collision at Function Definition Hash calculation");
return exportedFunctions;
}
@@ -74,6 +83,19 @@ FunctionDefinition const* ContractDefinition::getConstructor() const
return nullptr;
}
+vector<pair<FixedHash<4>, FunctionDefinition const*>> ContractDefinition::getInterfaceFunctionList() const
+{
+ vector<pair<FixedHash<4>, FunctionDefinition const*>> exportedFunctions;
+ for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
+ if (f->isPublic() && f->getName() != getName())
+ {
+ FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
+ exportedFunctions.push_back(make_pair(hash, f.get()));
+ }
+
+ return exportedFunctions;
+}
+
void StructDefinition::checkMemberTypes() const
{
for (ASTPointer<VariableDeclaration> const& member: getMembers())
@@ -309,20 +331,13 @@ bool FunctionCall::isTypeConversion() const
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>(*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."));
+ shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract);
+ TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes();
+ m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType},
+ FunctionType::Location::CREATION);
}
void MemberAccess::checkTypeRequirements()
diff --git a/AST.h b/AST.h
index 95121d4c..409aed44 100755
--- a/AST.h
+++ b/AST.h
@@ -191,6 +191,8 @@ public:
FunctionDefinition const* getConstructor() const;
private:
+ std::vector<std::pair<FixedHash<4>, FunctionDefinition const*>> getInterfaceFunctionList() const;
+
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
@@ -790,26 +792,22 @@ private:
};
/**
- * Expression that creates a new contract, e.g. "new SomeContract(1, 2)".
+ * Expression that creates a new contract, e.g. the "new SomeContract" part in "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) {}
+ NewExpression(Location const& _location, ASTPointer<Identifier> const& _contractName):
+ Expression(_location), m_contractName(_contractName) {}
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;
};
diff --git a/AST_accept.h b/AST_accept.h
index 0e5a71b6..7f3db85a 100644
--- a/AST_accept.h
+++ b/AST_accept.h
@@ -452,20 +452,14 @@ void FunctionCall::accept(ASTConstVisitor& _visitor) const
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);
}
diff --git a/Compiler.cpp b/Compiler.cpp
index 782a7efe..bd6571b9 100644
--- a/Compiler.cpp
+++ b/Compiler.cpp
@@ -50,10 +50,9 @@ void Compiler::compileContract(ContractDefinition const& _contract, vector<Magic
function->accept(*this);
// Swap the runtime context with the creation-time context
- CompilerContext runtimeContext;
- swap(m_context, runtimeContext);
+ swap(m_context, m_runtimeContext);
initializeContext(_contract, _magicGlobals, _contracts);
- packIntoContractCreator(_contract, runtimeContext);
+ packIntoContractCreator(_contract, m_runtimeContext);
}
void Compiler::initializeContext(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals,
diff --git a/Compiler.h b/Compiler.h
index e83d1ed3..c229a7a8 100644
--- a/Compiler.h
+++ b/Compiler.h
@@ -35,6 +35,7 @@ public:
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); }
+ bytes getRuntimeBytecode() { return m_runtimeContext.getAssembledBytecode(m_optimize);}
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
private:
@@ -70,6 +71,7 @@ private:
bool const m_optimize;
CompilerContext m_context;
+ CompilerContext m_runtimeContext;
std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement
eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement
diff --git a/CompilerContext.cpp b/CompilerContext.cpp
index 5d10a5f9..29e98eab 100644
--- a/CompilerContext.cpp
+++ b/CompilerContext.cpp
@@ -95,6 +95,11 @@ unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const
return _baseOffset + m_asm.deposit();
}
+unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const
+{
+ return -baseToCurrentStackOffset(-_offset);
+}
+
u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const
{
auto it = m_stateVariables.find(&_declaration);
diff --git a/CompilerContext.h b/CompilerContext.h
index 14672c95..cf505d65 100644
--- a/CompilerContext.h
+++ b/CompilerContext.h
@@ -62,6 +62,9 @@ public:
/// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns
/// the distance of that variable from the current top of the stack.
unsigned baseToCurrentStackOffset(unsigned _baseOffset) const;
+ /// Converts an offset relative to the current stack height to a value that can be used later
+ /// with baseToCurrentStackOffset to point to the same stack element.
+ unsigned currentToBaseStackOffset(unsigned _offset) const;
u256 getStorageLocationOfVariable(Declaration const& _declaration) const;
/// Appends a JUMPI instruction to a new tag and @returns the tag
diff --git a/CompilerStack.cpp b/CompilerStack.cpp
index 904c77c5..5532d74b 100644
--- a/CompilerStack.cpp
+++ b/CompilerStack.cpp
@@ -30,6 +30,8 @@
#include <libsolidity/CompilerStack.h>
#include <libsolidity/InterfaceHandler.h>
+#include <libdevcrypto/SHA3.h>
+
using namespace std;
namespace dev
@@ -117,6 +119,7 @@ void CompilerStack::compile(bool _optimize)
contractBytecode);
Contract& compiledContract = m_contracts[contract->getName()];
compiledContract.bytecode = compiler->getAssembledBytecode();
+ compiledContract.runtimeBytecode = compiler->getRuntimeBytecode();
compiledContract.compiler = move(compiler);
contractBytecode[compiledContract.contract] = &compiledContract.bytecode;
}
@@ -134,6 +137,16 @@ bytes const& CompilerStack::getBytecode(string const& _contractName) const
return getContract(_contractName).bytecode;
}
+bytes const& CompilerStack::getRuntimeBytecode(string const& _contractName) const
+{
+ return getContract(_contractName).runtimeBytecode;
+}
+
+dev::h256 CompilerStack::getContractCodeHash(string const& _contractName) const
+{
+ return dev::sha3(getRuntimeBytecode(_contractName));
+}
+
void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName) const
{
getContract(_contractName).compiler->streamAssembly(_outStream);
diff --git a/CompilerStack.h b/CompilerStack.h
index afc9a516..aa55abe5 100644
--- a/CompilerStack.h
+++ b/CompilerStack.h
@@ -28,6 +28,7 @@
#include <memory>
#include <boost/noncopyable.hpp>
#include <libdevcore/Common.h>
+#include <libdevcore/FixedHash.h>
namespace dev {
namespace solidity {
@@ -75,7 +76,13 @@ public:
/// @returns the compiled bytecode
bytes const& compile(std::string const& _sourceCode, bool _optimize = false);
+ /// @returns the assembled bytecode for a contract.
bytes const& getBytecode(std::string const& _contractName = "") const;
+ /// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
+ bytes const& getRuntimeBytecode(std::string const& _contractName = "") const;
+ /// @returns hash of the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
+ dev::h256 getContractCodeHash(std::string const& _contractName = "") const;
+
/// Streams a verbose version of the assembly to @a _outStream.
/// Prerequisite: Successful compilation.
void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "") const;
@@ -121,6 +128,7 @@ private:
ContractDefinition const* contract = nullptr;
std::shared_ptr<Compiler> compiler;
bytes bytecode;
+ bytes runtimeBytecode;
std::shared_ptr<InterfaceHandler> interfaceHandler;
mutable std::unique_ptr<std::string const> interface;
mutable std::unique_ptr<std::string const> solidityInterface;
diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp
index 29338b69..2b6e53cb 100644
--- a/ExpressionCompiler.cpp
+++ b/ExpressionCompiler.cpp
@@ -232,27 +232,71 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
}
case Location::EXTERNAL:
case Location::BARE:
+ _functionCall.getExpression().accept(*this);
+ appendExternalFunctionCall(function, arguments, function.getLocation() == Location::BARE);
+ break;
+ case Location::CREATION:
{
- FunctionCallOptions options;
- options.bare = function.getLocation() == Location::BARE;
- options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); };
- appendExternalFunctionCall(function, arguments, options);
+ _functionCall.getExpression().accept(*this);
+ solAssert(!function.gasSet(), "Gas limit set for contract creation.");
+ solAssert(function.getReturnParameterTypes().size() == 1, "");
+ ContractDefinition const& contract = dynamic_cast<ContractType const&>(
+ *function.getReturnParameterTypes().front()).getContractDefinition();
+ // copy the contract's code into memory
+ bytes const& bytecode = m_context.getCompiledContract(contract);
+ 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 length = bytecode.size();
+ length += appendArgumentCopyToMemory(function.getParameterTypes(), arguments, length);
+ // size, offset, endowment
+ m_context << u256(length) << u256(0);
+ if (function.valueSet())
+ m_context << eth::dupInstruction(3);
+ else
+ m_context << u256(0);
+ m_context << eth::Instruction::CREATE;
+ if (function.valueSet())
+ m_context << eth::swapInstruction(1) << eth::Instruction::POP;
break;
}
- case Location::SEND:
+ case Location::SET_GAS:
{
- FunctionCallOptions options;
- options.bare = true;
- options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); };
- options.obtainValue = [&]()
- {
- arguments.front()->accept(*this);
- appendTypeConversion(*arguments.front()->getType(),
- *function.getParameterTypes().front(), true);
- };
- appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{}, Location::EXTERNAL), {}, options);
+ // stack layout: contract_address function_id [gas] [value]
+ _functionCall.getExpression().accept(*this);
+ arguments.front()->accept(*this);
+ appendTypeConversion(*arguments.front()->getType(), IntegerType(256), true);
+ // Note that function is not the original function, but the ".gas" function.
+ // Its values of gasSet and valueSet is equal to the original function's though.
+ unsigned stackDepth = (function.gasSet() ? 1 : 0) + (function.valueSet() ? 1 : 0);
+ if (stackDepth > 0)
+ m_context << eth::swapInstruction(stackDepth);
+ if (function.gasSet())
+ m_context << eth::Instruction::POP;
break;
}
+ case Location::SET_VALUE:
+ // stack layout: contract_address function_id [gas] [value]
+ _functionCall.getExpression().accept(*this);
+ // Note that function is not the original function, but the ".value" function.
+ // Its values of gasSet and valueSet is equal to the original function's though.
+ if (function.valueSet())
+ m_context << eth::Instruction::POP;
+ arguments.front()->accept(*this);
+ break;
+ case Location::SEND:
+ _functionCall.getExpression().accept(*this);
+ m_context << u256(0); // 0 gas, we do not want to execute code
+ arguments.front()->accept(*this);
+ appendTypeConversion(*arguments.front()->getType(),
+ *function.getParameterTypes().front(), true);
+ appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{},
+ Location::EXTERNAL, true, true), {}, true);
+ break;
case Location::SUICIDE:
arguments.front()->accept(*this);
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
@@ -296,11 +340,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1},
{Location::SHA256, 2},
{Location::RIPEMD160, 3}};
- u256 contractAddress = contractAddresses.find(function.getLocation())->second;
- FunctionCallOptions options;
- options.bare = true;
- options.obtainAddress = [&]() { m_context << contractAddress; };
- appendExternalFunctionCall(function, arguments, options);
+ m_context << contractAddresses.find(function.getLocation())->second;
+ appendExternalFunctionCall(function, arguments, true);
break;
}
default:
@@ -310,40 +351,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
return false;
}
-bool ExpressionCompiler::visit(NewExpression const& _newExpression)
+bool ExpressionCompiler::visit(NewExpression const&)
{
- 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], true);
- unsigned const c_numBytes = types[i]->getCalldataEncodedSize();
- if (c_numBytes > 32)
- BOOST_THROW_EXCEPTION(CompilerError()
- << errinfo_sourceLocation(arguments[i]->getLocation())
- << errinfo_comment("Type " + types[i]->toString() + " not yet supported."));
- bool const c_leftAligned = types[i]->getCategory() == Type::Category::STRING;
- bool const c_padToWords = true;
- dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, c_numBytes,
- c_leftAligned, c_padToWords);
- }
- // size, offset, endowment
- m_context << u256(dataOffset) << u256(0) << u256(0) << eth::Instruction::CREATE;
+ // code is created for the function call (CREATION) only
return false;
}
@@ -377,6 +387,10 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
break;
+ case Type::Category::FUNCTION:
+ solAssert(!!_memberAccess.getExpression().getType()->getMemberType(member),
+ "Invalid member access to function.");
+ break;
case Type::Category::MAGIC:
// we can ignore the kind of magic and only look at the name of the member
if (member == "coinbase")
@@ -653,46 +667,60 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack)
void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functionType,
vector<ASTPointer<Expression const>> const& _arguments,
- FunctionCallOptions const& _options)
+ bool bare)
{
solAssert(_arguments.size() == _functionType.getParameterTypes().size(), "");
- _options.obtainAddress();
- if (!_options.bare)
- CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset);
+ // Assumed stack content here:
+ // <stack top>
+ // value [if _functionType.valueSet()]
+ // gas [if _functionType.gasSet()]
+ // function identifier [unless bare]
+ // contract address
- unsigned dataOffset = _options.bare ? 0 : CompilerUtils::dataStartOffset; // reserve 4 bytes for the function's hash identifier
- for (unsigned i = 0; i < _arguments.size(); ++i)
+ unsigned gasValueSize = (_functionType.gasSet() ? 1 : 0) + (_functionType.valueSet() ? 1 : 0);
+
+ unsigned contractStackPos = m_context.currentToBaseStackOffset(1 + gasValueSize + (bare ? 0 : 1));
+ unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize);
+ unsigned valueStackPos = m_context.currentToBaseStackOffset(1);
+
+ if (!bare)
{
- _arguments[i]->accept(*this);
- Type const& type = *_functionType.getParameterTypes()[i];
- appendTypeConversion(*_arguments[i]->getType(), type, true);
- unsigned const c_numBytes = type.getCalldataEncodedSize();
- if (c_numBytes == 0 || c_numBytes > 32)
- BOOST_THROW_EXCEPTION(CompilerError()
- << errinfo_sourceLocation(_arguments[i]->getLocation())
- << errinfo_comment("Type " + type.toString() + " not yet supported."));
- bool const c_leftAligned = type.getCategory() == Type::Category::STRING;
- bool const c_padToWords = true;
- dataOffset += CompilerUtils(m_context).storeInMemory(dataOffset, c_numBytes,
- c_leftAligned, c_padToWords);
+ // copy function identifier
+ m_context << eth::dupInstruction(gasValueSize + 1);
+ CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset);
}
+
+ // reserve space for the function identifier
+ unsigned dataOffset = bare ? 0 : CompilerUtils::dataStartOffset;
+ dataOffset += appendArgumentCopyToMemory(_functionType.getParameterTypes(), _arguments, dataOffset);
+
//@todo only return the first return value for now
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
_functionType.getReturnParameterTypes().front().get();
unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0;
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0);
- if (_options.obtainValue)
- _options.obtainValue();
+ if (_functionType.valueSet())
+ m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
else
m_context << u256(0);
- m_context << eth::dupInstruction(6); //copy contract address
+ m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos));
- m_context << u256(25) << eth::Instruction::GAS << eth::Instruction::SUB
- << eth::Instruction::CALL
- << eth::Instruction::POP // @todo do not ignore failure indicator
- << eth::Instruction::POP; // pop contract address
+ if (_functionType.gasSet())
+ m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
+ else
+ // send all gas except for the 21 needed to execute "SUB" and "CALL"
+ m_context << u256(21) << eth::Instruction::GAS << eth::Instruction::SUB;
+ m_context << eth::Instruction::CALL
+ << eth::Instruction::POP; // @todo do not ignore failure indicator
+ if (_functionType.valueSet())
+ m_context << eth::Instruction::POP;
+ if (_functionType.gasSet())
+ m_context << eth::Instruction::POP;
+ if (!bare)
+ m_context << eth::Instruction::POP;
+ m_context << eth::Instruction::POP; // pop contract address
if (retSize > 0)
{
@@ -701,6 +729,28 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
}
}
+unsigned ExpressionCompiler::appendArgumentCopyToMemory(TypePointers const& _types,
+ vector<ASTPointer<Expression const>> const& _arguments,
+ unsigned _memoryOffset)
+{
+ unsigned length = 0;
+ for (unsigned i = 0; i < _arguments.size(); ++i)
+ {
+ _arguments[i]->accept(*this);
+ appendTypeConversion(*_arguments[i]->getType(), *_types[i], true);
+ unsigned const c_numBytes = _types[i]->getCalldataEncodedSize();
+ if (c_numBytes == 0 || c_numBytes > 32)
+ BOOST_THROW_EXCEPTION(CompilerError()
+ << errinfo_sourceLocation(_arguments[i]->getLocation())
+ << errinfo_comment("Type " + _types[i]->toString() + " not yet supported."));
+ bool const c_leftAligned = _types[i]->getCategory() == Type::Category::STRING;
+ bool const c_padToWords = true;
+ length += CompilerUtils(m_context).storeInMemory(_memoryOffset + length, c_numBytes,
+ c_leftAligned, c_padToWords);
+ }
+ return length;
+}
+
ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,
unsigned _baseStackOffset):
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset),
diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h
index 98f58c85..4a696c39 100644
--- a/ExpressionCompiler.h
+++ b/ExpressionCompiler.h
@@ -87,21 +87,13 @@ private:
//// Appends code that cleans higher-order bits for integer types.
void appendHighBitsCleanup(IntegerType const& _typeOnStack);
- /// Additional options used in appendExternalFunctionCall.
- struct FunctionCallOptions
- {
- FunctionCallOptions() {}
- /// Invoked to copy the address to the stack
- std::function<void()> obtainAddress;
- /// Invoked to copy the ethe value to the stack (if not specified, value is 0).
- std::function<void()> obtainValue;
- /// If true, do not prepend function index to call data
- bool bare = false;
- };
-
/// Appends code to call a function of the given type with the given arguments.
void appendExternalFunctionCall(FunctionType const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments,
- FunctionCallOptions const& _options = FunctionCallOptions());
+ bool bare = false);
+ /// Appends code that copies the given arguments to memory (with optional offset).
+ /// @returns the number of bytes copied to memory
+ unsigned appendArgumentCopyToMemory(TypePointers const& _functionType, std::vector<ASTPointer<Expression const>> const& _arguments,
+ unsigned _memoryOffset = 0);
/**
* Helper class to store and retrieve lvalues to and from various locations.
diff --git a/InterfaceHandler.cpp b/InterfaceHandler.cpp
index a5c6f4a1..4c7ae5f4 100644
--- a/InterfaceHandler.cpp
+++ b/InterfaceHandler.cpp
@@ -106,7 +106,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
if (!m_notice.empty())
{// since @notice is the only user tag if missing function should not appear
user["notice"] = Json::Value(m_notice);
- methods[it.second->getName()] = user;
+ methods[it.second->getCanonicalSignature()] = user;
}
}
}
@@ -162,7 +162,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
method["return"] = m_return;
if (!method.empty()) // add the function, only if we have any documentation to add
- methods[it.second->getName()] = method;
+ methods[it.second->getCanonicalSignature()] = method;
}
}
doc["methods"] = methods;
diff --git a/Parser.cpp b/Parser.cpp
index e287319d..ebff3ba4 100644
--- a/Parser.cpp
+++ b/Parser.cpp
@@ -466,17 +466,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
{
ASTNodeFactory nodeFactory(*this);
Token::Value token = m_scanner->getCurrentToken();
- 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))
+ if (Token::isUnaryOp(token) || Token::isCountOp(token))
{
// prefix expression
m_scanner->next();
@@ -500,7 +490,17 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
ASTPointer<Expression> Parser::parseLeftHandSideExpression()
{
ASTNodeFactory nodeFactory(*this);
- ASTPointer<Expression> expression = parsePrimaryExpression();
+ ASTPointer<Expression> expression;
+ if (m_scanner->getCurrentToken() == Token::NEW)
+ {
+ expectToken(Token::NEW);
+ ASTPointer<Identifier> contractName = ASTNodeFactory(*this).createNode<Identifier>(expectIdentifierToken());
+ nodeFactory.markEndPosition();
+ expression = nodeFactory.createNode<NewExpression>(contractName);
+ }
+ else
+ expression = parsePrimaryExpression();
+
while (true)
{
switch (m_scanner->getCurrentToken())
diff --git a/Token.h b/Token.h
index 2d4441d0..e5c61c7e 100644
--- a/Token.h
+++ b/Token.h
@@ -47,6 +47,10 @@
#include <libsolidity/Utils.h>
#include <libsolidity/Exceptions.h>
+#if defined(DELETE)
+#error The macro "DELETE" from windows.h conflicts with this file. Please change the order of includes.
+#endif
+
namespace dev
{
namespace solidity
diff --git a/Types.cpp b/Types.cpp
index 07d6f6bd..ae87a088 100644
--- a/Types.cpp
+++ b/Types.cpp
@@ -546,7 +546,8 @@ u256 StructType::getStorageOffsetOfMember(string const& _name) const
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested."));
}
-FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal)
+FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
+ m_location(_isInternal ? Location::INTERNAL : Location::EXTERNAL)
{
TypePointers params;
TypePointers retParams;
@@ -558,7 +559,6 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
retParams.push_back(var->getType());
swap(params, m_parameterTypes);
swap(retParams, m_returnParameterTypes);
- m_location = _isInternal ? Location::INTERNAL : Location::EXTERNAL;
}
bool FunctionType::operator==(Type const& _other) const
@@ -580,6 +580,9 @@ bool FunctionType::operator==(Type const& _other) const
if (!equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(),
other.m_returnParameterTypes.cbegin(), typeCompare))
return false;
+ //@todo this is ugly, but cannot be prevented right now
+ if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet)
+ return false;
return true;
}
@@ -596,16 +599,44 @@ string FunctionType::toString() const
unsigned FunctionType::getSizeOnStack() const
{
+ unsigned size = 0;
+ if (m_location == Location::EXTERNAL)
+ size = 2;
+ else if (m_location == Location::INTERNAL || m_location == Location::BARE)
+ size = 1;
+ if (m_gasSet)
+ size++;
+ if (m_valueSet)
+ size++;
+ return size;
+}
+
+MemberList const& FunctionType::getMembers() const
+{
switch (m_location)
{
- case Location::INTERNAL:
- return 1;
case Location::EXTERNAL:
- return 2;
+ case Location::CREATION:
+ case Location::ECRECOVER:
+ case Location::SHA256:
+ case Location::RIPEMD160:
case Location::BARE:
- return 1;
+ if (!m_members)
+ {
+ map<string, TypePointer> members{
+ {"gas", make_shared<FunctionType>(parseElementaryTypeVector({"uint"}),
+ TypePointers{copyAndSetGasOrValue(true, false)},
+ Location::SET_GAS, m_gasSet, m_valueSet)},
+ {"value", make_shared<FunctionType>(parseElementaryTypeVector({"uint"}),
+ TypePointers{copyAndSetGasOrValue(false, true)},
+ Location::SET_VALUE, m_gasSet, m_valueSet)}};
+ if (m_location == Location::CREATION)
+ members.erase("gas");
+ m_members.reset(new MemberList(members));
+ }
+ return *m_members;
default:
- return 0;
+ return EmptyMemberList;
}
}
@@ -628,6 +659,12 @@ TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
return pointers;
}
+TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) const
+{
+ return make_shared<FunctionType>(m_parameterTypes, m_returnParameterTypes, m_location,
+ m_gasSet || _setGas, m_valueSet || _setValue);
+}
+
bool MappingType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
diff --git a/Types.h b/Types.h
index c011928a..b69bfd07 100644
--- a/Types.h
+++ b/Types.h
@@ -291,6 +291,8 @@ public:
virtual MemberList const& getMembers() const override;
+ ContractDefinition const& getContractDefinition() const { return m_contract; }
+
/// 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;
@@ -345,10 +347,15 @@ class FunctionType: public Type
{
public:
/// The meaning of the value(s) on the stack referencing the function:
- /// INTERNAL: jump tag, EXTERNAL: contract address + function index,
+ /// INTERNAL: jump tag, EXTERNAL: contract address + function identifier,
/// BARE: contract address (non-abi contract call)
/// OTHERS: special virtual function, nothing on the stack
- enum class Location { INTERNAL, EXTERNAL, SEND, SHA3, SUICIDE, ECRECOVER, SHA256, RIPEMD160, LOG0, LOG1, LOG2, LOG3, LOG4, BLOCKHASH, BARE };
+ enum class Location { INTERNAL, EXTERNAL, CREATION, SEND,
+ SHA3, SUICIDE,
+ ECRECOVER, SHA256, RIPEMD160,
+ LOG0, LOG1, LOG2, LOG3, LOG4,
+ SET_GAS, SET_VALUE, BLOCKHASH
+ BARE };
virtual Category getCategory() const override { return Category::FUNCTION; }
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
@@ -357,9 +364,10 @@ public:
FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes),
_location) {}
FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes,
- Location _location = Location::INTERNAL):
+ Location _location = Location::INTERNAL,
+ bool _gasSet = false, bool _valueSet = false):
m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes),
- m_location(_location) {}
+ m_location(_location), m_gasSet(_gasSet), m_valueSet(_valueSet) {}
TypePointers const& getParameterTypes() const { return m_parameterTypes; }
TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; }
@@ -370,16 +378,27 @@ public:
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); }
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned getSizeOnStack() const override;
+ virtual MemberList const& getMembers() const override;
Location const& getLocation() const { return m_location; }
std::string getCanonicalSignature() const;
+ bool gasSet() const { return m_gasSet; }
+ bool valueSet() const { return m_valueSet; }
+
+ /// @returns a copy of this type, where gas or value are set manually. This will never set one
+ /// of the parameters to fals.
+ TypePointer copyAndSetGasOrValue(bool _setGas, bool _setValue) const;
+
private:
static TypePointers parseElementaryTypeVector(strings const& _types);
TypePointers m_parameterTypes;
TypePointers m_returnParameterTypes;
- Location m_location;
+ Location const m_location;
+ bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack
+ bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
+ mutable std::unique_ptr<MemberList> m_members;
};
/**
diff --git a/grammar.txt b/grammar.txt
index 8c34997b..f06d4def 100644
--- a/grammar.txt
+++ b/grammar.txt
@@ -33,7 +33,7 @@ Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | NewE
// The expression syntax is actually much more complicated
Assignment = Expression (AssignmentOp Expression)
FunctionCall = Expression '(' Expression ( ',' Expression )* ')'
-NewExpression = 'new' Identifier '(' ( Expression ( ',' Expression )* ) ')'
+NewExpression = 'new' Identifier
MemberAccess = Expression '.' Identifier
IndexAccess = Expression '[' Expresison ']'
PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')'