mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop
Conflicts: libsolidity/Types.h
This commit is contained in:
commit
a9ed77c22a
53
AST.cpp
53
AST.cpp
@ -50,18 +50,27 @@ void ContractDefinition::checkTypeRequirements()
|
|||||||
|
|
||||||
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
|
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
|
||||||
function->checkTypeRequirements();
|
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*> ContractDefinition::getInterfaceFunctions() const
|
||||||
{
|
{
|
||||||
map<FixedHash<4>, FunctionDefinition const*> exportedFunctions;
|
vector<pair<FixedHash<4>, FunctionDefinition const*>> exportedFunctionList = getInterfaceFunctionList();
|
||||||
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
|
map<FixedHash<4>, FunctionDefinition const*> exportedFunctions(exportedFunctionList.begin(),
|
||||||
if (f->isPublic() && f->getName() != getName())
|
exportedFunctionList.end());
|
||||||
{
|
solAssert(exportedFunctionList.size() == exportedFunctions.size(),
|
||||||
FixedHash<4> hash(dev::sha3(f->getCanonicalSignature()));
|
"Hash collision at Function Definition Hash calculation");
|
||||||
auto res = exportedFunctions.insert(std::make_pair(hash,f.get()));
|
|
||||||
solAssert(res.second, "Hash collision at Function Definition Hash calculation");
|
|
||||||
}
|
|
||||||
|
|
||||||
return exportedFunctions;
|
return exportedFunctions;
|
||||||
}
|
}
|
||||||
@ -74,6 +83,19 @@ FunctionDefinition const* ContractDefinition::getConstructor() const
|
|||||||
return nullptr;
|
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
|
void StructDefinition::checkMemberTypes() const
|
||||||
{
|
{
|
||||||
for (ASTPointer<VariableDeclaration> const& member: getMembers())
|
for (ASTPointer<VariableDeclaration> const& member: getMembers())
|
||||||
@ -309,20 +331,13 @@ bool FunctionCall::isTypeConversion() const
|
|||||||
void NewExpression::checkTypeRequirements()
|
void NewExpression::checkTypeRequirements()
|
||||||
{
|
{
|
||||||
m_contractName->checkTypeRequirements();
|
m_contractName->checkTypeRequirements();
|
||||||
for (ASTPointer<Expression> const& argument: m_arguments)
|
|
||||||
argument->checkTypeRequirements();
|
|
||||||
|
|
||||||
m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration());
|
m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration());
|
||||||
if (!m_contract)
|
if (!m_contract)
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract."));
|
BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract."));
|
||||||
shared_ptr<ContractType const> type = make_shared<ContractType>(*m_contract);
|
shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract);
|
||||||
m_type = type;
|
TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes();
|
||||||
TypePointers const& parameterTypes = type->getConstructorType()->getParameterTypes();
|
m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType},
|
||||||
if (parameterTypes.size() != m_arguments.size())
|
FunctionType::Location::CREATION);
|
||||||
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()
|
void MemberAccess::checkTypeRequirements()
|
||||||
|
12
AST.h
12
AST.h
@ -191,6 +191,8 @@ public:
|
|||||||
FunctionDefinition const* getConstructor() const;
|
FunctionDefinition const* getConstructor() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::vector<std::pair<FixedHash<4>, FunctionDefinition const*>> getInterfaceFunctionList() const;
|
||||||
|
|
||||||
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
|
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
|
||||||
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
|
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
|
||||||
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
|
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
|
class NewExpression: public Expression
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NewExpression(Location const& _location, ASTPointer<Identifier> const& _contractName,
|
NewExpression(Location const& _location, ASTPointer<Identifier> const& _contractName):
|
||||||
std::vector<ASTPointer<Expression>> const& _arguments):
|
Expression(_location), m_contractName(_contractName) {}
|
||||||
Expression(_location), m_contractName(_contractName), m_arguments(_arguments) {}
|
|
||||||
virtual void accept(ASTVisitor& _visitor) override;
|
virtual void accept(ASTVisitor& _visitor) override;
|
||||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||||
virtual void checkTypeRequirements() override;
|
virtual void checkTypeRequirements() override;
|
||||||
|
|
||||||
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.
|
/// Returns the referenced contract. Can only be called after type checking.
|
||||||
ContractDefinition const* getContract() const { solAssert(m_contract, ""); return m_contract; }
|
ContractDefinition const* getContract() const { solAssert(m_contract, ""); return m_contract; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ASTPointer<Identifier> m_contractName;
|
ASTPointer<Identifier> m_contractName;
|
||||||
std::vector<ASTPointer<Expression>> m_arguments;
|
|
||||||
|
|
||||||
ContractDefinition const* m_contract = nullptr;
|
ContractDefinition const* m_contract = nullptr;
|
||||||
};
|
};
|
||||||
|
@ -452,20 +452,14 @@ void FunctionCall::accept(ASTConstVisitor& _visitor) const
|
|||||||
void NewExpression::accept(ASTVisitor& _visitor)
|
void NewExpression::accept(ASTVisitor& _visitor)
|
||||||
{
|
{
|
||||||
if (_visitor.visit(*this))
|
if (_visitor.visit(*this))
|
||||||
{
|
|
||||||
m_contractName->accept(_visitor);
|
m_contractName->accept(_visitor);
|
||||||
listAccept(m_arguments, _visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
_visitor.endVisit(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NewExpression::accept(ASTConstVisitor& _visitor) const
|
void NewExpression::accept(ASTConstVisitor& _visitor) const
|
||||||
{
|
{
|
||||||
if (_visitor.visit(*this))
|
if (_visitor.visit(*this))
|
||||||
{
|
|
||||||
m_contractName->accept(_visitor);
|
m_contractName->accept(_visitor);
|
||||||
listAccept(m_arguments, _visitor);
|
|
||||||
}
|
|
||||||
_visitor.endVisit(*this);
|
_visitor.endVisit(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,10 +50,9 @@ void Compiler::compileContract(ContractDefinition const& _contract, vector<Magic
|
|||||||
function->accept(*this);
|
function->accept(*this);
|
||||||
|
|
||||||
// Swap the runtime context with the creation-time context
|
// Swap the runtime context with the creation-time context
|
||||||
CompilerContext runtimeContext;
|
swap(m_context, m_runtimeContext);
|
||||||
swap(m_context, runtimeContext);
|
|
||||||
initializeContext(_contract, _magicGlobals, _contracts);
|
initializeContext(_contract, _magicGlobals, _contracts);
|
||||||
packIntoContractCreator(_contract, runtimeContext);
|
packIntoContractCreator(_contract, m_runtimeContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::initializeContext(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals,
|
void Compiler::initializeContext(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals,
|
||||||
|
@ -35,6 +35,7 @@ public:
|
|||||||
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);
|
std::map<ContractDefinition const*, bytes const*> const& _contracts);
|
||||||
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); }
|
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); }
|
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -70,6 +71,7 @@ private:
|
|||||||
|
|
||||||
bool const m_optimize;
|
bool const m_optimize;
|
||||||
CompilerContext m_context;
|
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_breakTags; ///< tag to jump to for a "break" statement
|
||||||
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" 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
|
eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement
|
||||||
|
@ -95,6 +95,11 @@ unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const
|
|||||||
return _baseOffset + m_asm.deposit();
|
return _baseOffset + m_asm.deposit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const
|
||||||
|
{
|
||||||
|
return -baseToCurrentStackOffset(-_offset);
|
||||||
|
}
|
||||||
|
|
||||||
u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const
|
u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const
|
||||||
{
|
{
|
||||||
auto it = m_stateVariables.find(&_declaration);
|
auto it = m_stateVariables.find(&_declaration);
|
||||||
|
@ -62,6 +62,9 @@ public:
|
|||||||
/// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns
|
/// If supplied by a value returned by @ref getBaseStackOffsetOfVariable(variable), returns
|
||||||
/// the distance of that variable from the current top of the stack.
|
/// the distance of that variable from the current top of the stack.
|
||||||
unsigned baseToCurrentStackOffset(unsigned _baseOffset) const;
|
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;
|
u256 getStorageLocationOfVariable(Declaration const& _declaration) const;
|
||||||
|
|
||||||
/// Appends a JUMPI instruction to a new tag and @returns the tag
|
/// Appends a JUMPI instruction to a new tag and @returns the tag
|
||||||
|
@ -30,6 +30,8 @@
|
|||||||
#include <libsolidity/CompilerStack.h>
|
#include <libsolidity/CompilerStack.h>
|
||||||
#include <libsolidity/InterfaceHandler.h>
|
#include <libsolidity/InterfaceHandler.h>
|
||||||
|
|
||||||
|
#include <libdevcrypto/SHA3.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
@ -117,6 +119,7 @@ void CompilerStack::compile(bool _optimize)
|
|||||||
contractBytecode);
|
contractBytecode);
|
||||||
Contract& compiledContract = m_contracts[contract->getName()];
|
Contract& compiledContract = m_contracts[contract->getName()];
|
||||||
compiledContract.bytecode = compiler->getAssembledBytecode();
|
compiledContract.bytecode = compiler->getAssembledBytecode();
|
||||||
|
compiledContract.runtimeBytecode = compiler->getRuntimeBytecode();
|
||||||
compiledContract.compiler = move(compiler);
|
compiledContract.compiler = move(compiler);
|
||||||
contractBytecode[compiledContract.contract] = &compiledContract.bytecode;
|
contractBytecode[compiledContract.contract] = &compiledContract.bytecode;
|
||||||
}
|
}
|
||||||
@ -134,6 +137,16 @@ bytes const& CompilerStack::getBytecode(string const& _contractName) const
|
|||||||
return getContract(_contractName).bytecode;
|
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
|
void CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName) const
|
||||||
{
|
{
|
||||||
getContract(_contractName).compiler->streamAssembly(_outStream);
|
getContract(_contractName).compiler->streamAssembly(_outStream);
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <libdevcore/Common.h>
|
#include <libdevcore/Common.h>
|
||||||
|
#include <libdevcore/FixedHash.h>
|
||||||
|
|
||||||
namespace dev {
|
namespace dev {
|
||||||
namespace solidity {
|
namespace solidity {
|
||||||
@ -75,7 +76,13 @@ public:
|
|||||||
/// @returns the compiled bytecode
|
/// @returns the compiled bytecode
|
||||||
bytes const& compile(std::string const& _sourceCode, bool _optimize = false);
|
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;
|
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.
|
/// Streams a verbose version of the assembly to @a _outStream.
|
||||||
/// Prerequisite: Successful compilation.
|
/// Prerequisite: Successful compilation.
|
||||||
void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "") const;
|
void streamAssembly(std::ostream& _outStream, std::string const& _contractName = "") const;
|
||||||
@ -121,6 +128,7 @@ private:
|
|||||||
ContractDefinition const* contract = nullptr;
|
ContractDefinition const* contract = nullptr;
|
||||||
std::shared_ptr<Compiler> compiler;
|
std::shared_ptr<Compiler> compiler;
|
||||||
bytes bytecode;
|
bytes bytecode;
|
||||||
|
bytes runtimeBytecode;
|
||||||
std::shared_ptr<InterfaceHandler> interfaceHandler;
|
std::shared_ptr<InterfaceHandler> interfaceHandler;
|
||||||
mutable std::unique_ptr<std::string const> interface;
|
mutable std::unique_ptr<std::string const> interface;
|
||||||
mutable std::unique_ptr<std::string const> solidityInterface;
|
mutable std::unique_ptr<std::string const> solidityInterface;
|
||||||
|
@ -232,27 +232,71 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
case Location::EXTERNAL:
|
case Location::EXTERNAL:
|
||||||
case Location::BARE:
|
case Location::BARE:
|
||||||
|
_functionCall.getExpression().accept(*this);
|
||||||
|
appendExternalFunctionCall(function, arguments, function.getLocation() == Location::BARE);
|
||||||
|
break;
|
||||||
|
case Location::CREATION:
|
||||||
{
|
{
|
||||||
FunctionCallOptions options;
|
_functionCall.getExpression().accept(*this);
|
||||||
options.bare = function.getLocation() == Location::BARE;
|
solAssert(!function.gasSet(), "Gas limit set for contract creation.");
|
||||||
options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); };
|
solAssert(function.getReturnParameterTypes().size() == 1, "");
|
||||||
appendExternalFunctionCall(function, arguments, options);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
case Location::SET_GAS:
|
||||||
|
{
|
||||||
|
// 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:
|
case Location::SEND:
|
||||||
{
|
_functionCall.getExpression().accept(*this);
|
||||||
FunctionCallOptions options;
|
m_context << u256(0); // 0 gas, we do not want to execute code
|
||||||
options.bare = true;
|
arguments.front()->accept(*this);
|
||||||
options.obtainAddress = [&]() { _functionCall.getExpression().accept(*this); };
|
appendTypeConversion(*arguments.front()->getType(),
|
||||||
options.obtainValue = [&]()
|
*function.getParameterTypes().front(), true);
|
||||||
{
|
appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{},
|
||||||
arguments.front()->accept(*this);
|
Location::EXTERNAL, true, true), {}, true);
|
||||||
appendTypeConversion(*arguments.front()->getType(),
|
|
||||||
*function.getParameterTypes().front(), true);
|
|
||||||
};
|
|
||||||
appendExternalFunctionCall(FunctionType(TypePointers{}, TypePointers{}, Location::EXTERNAL), {}, options);
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case Location::SUICIDE:
|
case Location::SUICIDE:
|
||||||
arguments.front()->accept(*this);
|
arguments.front()->accept(*this);
|
||||||
appendTypeConversion(*arguments.front()->getType(), *function.getParameterTypes().front(), true);
|
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},
|
static const map<Location, u256> contractAddresses{{Location::ECRECOVER, 1},
|
||||||
{Location::SHA256, 2},
|
{Location::SHA256, 2},
|
||||||
{Location::RIPEMD160, 3}};
|
{Location::RIPEMD160, 3}};
|
||||||
u256 contractAddress = contractAddresses.find(function.getLocation())->second;
|
m_context << contractAddresses.find(function.getLocation())->second;
|
||||||
FunctionCallOptions options;
|
appendExternalFunctionCall(function, arguments, true);
|
||||||
options.bare = true;
|
|
||||||
options.obtainAddress = [&]() { m_context << contractAddress; };
|
|
||||||
appendExternalFunctionCall(function, arguments, options);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -310,40 +351,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExpressionCompiler::visit(NewExpression const& _newExpression)
|
bool ExpressionCompiler::visit(NewExpression const&)
|
||||||
{
|
{
|
||||||
ContractType const* type = dynamic_cast<ContractType const*>(_newExpression.getType().get());
|
// code is created for the function call (CREATION) only
|
||||||
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;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,6 +387,10 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
|||||||
else
|
else
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
|
||||||
break;
|
break;
|
||||||
|
case Type::Category::FUNCTION:
|
||||||
|
solAssert(!!_memberAccess.getExpression().getType()->getMemberType(member),
|
||||||
|
"Invalid member access to function.");
|
||||||
|
break;
|
||||||
case Type::Category::MAGIC:
|
case Type::Category::MAGIC:
|
||||||
// we can ignore the kind of magic and only look at the name of the member
|
// we can ignore the kind of magic and only look at the name of the member
|
||||||
if (member == "coinbase")
|
if (member == "coinbase")
|
||||||
@ -653,46 +667,60 @@ void ExpressionCompiler::appendHighBitsCleanup(IntegerType const& _typeOnStack)
|
|||||||
|
|
||||||
void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functionType,
|
void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functionType,
|
||||||
vector<ASTPointer<Expression const>> const& _arguments,
|
vector<ASTPointer<Expression const>> const& _arguments,
|
||||||
FunctionCallOptions const& _options)
|
bool bare)
|
||||||
{
|
{
|
||||||
solAssert(_arguments.size() == _functionType.getParameterTypes().size(), "");
|
solAssert(_arguments.size() == _functionType.getParameterTypes().size(), "");
|
||||||
|
|
||||||
_options.obtainAddress();
|
// Assumed stack content here:
|
||||||
if (!_options.bare)
|
// <stack top>
|
||||||
CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset);
|
// 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
|
unsigned gasValueSize = (_functionType.gasSet() ? 1 : 0) + (_functionType.valueSet() ? 1 : 0);
|
||||||
for (unsigned i = 0; i < _arguments.size(); ++i)
|
|
||||||
|
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);
|
// copy function identifier
|
||||||
Type const& type = *_functionType.getParameterTypes()[i];
|
m_context << eth::dupInstruction(gasValueSize + 1);
|
||||||
appendTypeConversion(*_arguments[i]->getType(), type, true);
|
CompilerUtils(m_context).storeInMemory(0, CompilerUtils::dataStartOffset);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
//@todo only return the first return value for now
|
||||||
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
|
Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr :
|
||||||
_functionType.getReturnParameterTypes().front().get();
|
_functionType.getReturnParameterTypes().front().get();
|
||||||
unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0;
|
unsigned retSize = firstType ? CompilerUtils::getPaddedSize(firstType->getCalldataEncodedSize()) : 0;
|
||||||
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
|
// CALL arguments: outSize, outOff, inSize, inOff, value, addr, gas (stack top)
|
||||||
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0);
|
m_context << u256(retSize) << u256(0) << u256(dataOffset) << u256(0);
|
||||||
if (_options.obtainValue)
|
if (_functionType.valueSet())
|
||||||
_options.obtainValue();
|
m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
|
||||||
else
|
else
|
||||||
m_context << u256(0);
|
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
|
if (_functionType.gasSet())
|
||||||
<< eth::Instruction::CALL
|
m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
|
||||||
<< eth::Instruction::POP // @todo do not ignore failure indicator
|
else
|
||||||
<< eth::Instruction::POP; // pop contract address
|
// 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)
|
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,
|
ExpressionCompiler::LValue::LValue(CompilerContext& _compilerContext, LValueType _type, Type const& _dataType,
|
||||||
unsigned _baseStackOffset):
|
unsigned _baseStackOffset):
|
||||||
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset),
|
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset),
|
||||||
|
@ -87,21 +87,13 @@ private:
|
|||||||
//// Appends code that cleans higher-order bits for integer types.
|
//// Appends code that cleans higher-order bits for integer types.
|
||||||
void appendHighBitsCleanup(IntegerType const& _typeOnStack);
|
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.
|
/// 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,
|
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.
|
* Helper class to store and retrieve lvalues to and from various locations.
|
||||||
|
@ -106,7 +106,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
|
|||||||
if (!m_notice.empty())
|
if (!m_notice.empty())
|
||||||
{// since @notice is the only user tag if missing function should not appear
|
{// since @notice is the only user tag if missing function should not appear
|
||||||
user["notice"] = Json::Value(m_notice);
|
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;
|
method["return"] = m_return;
|
||||||
|
|
||||||
if (!method.empty()) // add the function, only if we have any documentation to add
|
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;
|
doc["methods"] = methods;
|
||||||
|
24
Parser.cpp
24
Parser.cpp
@ -466,17 +466,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
|
|||||||
{
|
{
|
||||||
ASTNodeFactory nodeFactory(*this);
|
ASTNodeFactory nodeFactory(*this);
|
||||||
Token::Value token = m_scanner->getCurrentToken();
|
Token::Value token = m_scanner->getCurrentToken();
|
||||||
if (token == Token::NEW)
|
if (Token::isUnaryOp(token) || Token::isCountOp(token))
|
||||||
{
|
|
||||||
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
|
// prefix expression
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
@ -500,7 +490,17 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
|
|||||||
ASTPointer<Expression> Parser::parseLeftHandSideExpression()
|
ASTPointer<Expression> Parser::parseLeftHandSideExpression()
|
||||||
{
|
{
|
||||||
ASTNodeFactory nodeFactory(*this);
|
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)
|
while (true)
|
||||||
{
|
{
|
||||||
switch (m_scanner->getCurrentToken())
|
switch (m_scanner->getCurrentToken())
|
||||||
|
4
Token.h
4
Token.h
@ -47,6 +47,10 @@
|
|||||||
#include <libsolidity/Utils.h>
|
#include <libsolidity/Utils.h>
|
||||||
#include <libsolidity/Exceptions.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 dev
|
||||||
{
|
{
|
||||||
namespace solidity
|
namespace solidity
|
||||||
|
51
Types.cpp
51
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."));
|
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 params;
|
||||||
TypePointers retParams;
|
TypePointers retParams;
|
||||||
@ -558,7 +559,6 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
|
|||||||
retParams.push_back(var->getType());
|
retParams.push_back(var->getType());
|
||||||
swap(params, m_parameterTypes);
|
swap(params, m_parameterTypes);
|
||||||
swap(retParams, m_returnParameterTypes);
|
swap(retParams, m_returnParameterTypes);
|
||||||
m_location = _isInternal ? Location::INTERNAL : Location::EXTERNAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FunctionType::operator==(Type const& _other) const
|
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(),
|
if (!equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(),
|
||||||
other.m_returnParameterTypes.cbegin(), typeCompare))
|
other.m_returnParameterTypes.cbegin(), typeCompare))
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -595,17 +598,45 @@ string FunctionType::toString() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsigned FunctionType::getSizeOnStack() 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)
|
switch (m_location)
|
||||||
{
|
{
|
||||||
case Location::INTERNAL:
|
|
||||||
return 1;
|
|
||||||
case Location::EXTERNAL:
|
case Location::EXTERNAL:
|
||||||
return 2;
|
case Location::CREATION:
|
||||||
|
case Location::ECRECOVER:
|
||||||
|
case Location::SHA256:
|
||||||
|
case Location::RIPEMD160:
|
||||||
case Location::BARE:
|
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:
|
default:
|
||||||
return 0;
|
return EmptyMemberList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -628,6 +659,12 @@ TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
|
|||||||
return pointers;
|
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
|
bool MappingType::operator==(Type const& _other) const
|
||||||
{
|
{
|
||||||
if (_other.getCategory() != getCategory())
|
if (_other.getCategory() != getCategory())
|
||||||
|
29
Types.h
29
Types.h
@ -291,6 +291,8 @@ public:
|
|||||||
|
|
||||||
virtual MemberList const& getMembers() const override;
|
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
|
/// 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.
|
/// is not used, as this type cannot be the type of a variable or expression.
|
||||||
std::shared_ptr<FunctionType const> const& getConstructorType() const;
|
std::shared_ptr<FunctionType const> const& getConstructorType() const;
|
||||||
@ -345,10 +347,15 @@ class FunctionType: public Type
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// The meaning of the value(s) on the stack referencing the function:
|
/// 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)
|
/// BARE: contract address (non-abi contract call)
|
||||||
/// OTHERS: special virtual function, nothing on the stack
|
/// 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; }
|
virtual Category getCategory() const override { return Category::FUNCTION; }
|
||||||
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
|
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
|
||||||
@ -357,9 +364,10 @@ public:
|
|||||||
FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes),
|
FunctionType(parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_returnParameterTypes),
|
||||||
_location) {}
|
_location) {}
|
||||||
FunctionType(TypePointers const& _parameterTypes, TypePointers const& _returnParameterTypes,
|
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_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& getParameterTypes() const { return m_parameterTypes; }
|
||||||
TypePointers const& getReturnParameterTypes() const { return m_returnParameterTypes; }
|
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 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 bool canLiveOutsideStorage() const override { return false; }
|
||||||
virtual unsigned getSizeOnStack() const override;
|
virtual unsigned getSizeOnStack() const override;
|
||||||
|
virtual MemberList const& getMembers() const override;
|
||||||
|
|
||||||
Location const& getLocation() const { return m_location; }
|
Location const& getLocation() const { return m_location; }
|
||||||
std::string getCanonicalSignature() const;
|
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:
|
private:
|
||||||
static TypePointers parseElementaryTypeVector(strings const& _types);
|
static TypePointers parseElementaryTypeVector(strings const& _types);
|
||||||
|
|
||||||
TypePointers m_parameterTypes;
|
TypePointers m_parameterTypes;
|
||||||
TypePointers m_returnParameterTypes;
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,7 +33,7 @@ Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | NewE
|
|||||||
// The expression syntax is actually much more complicated
|
// The expression syntax is actually much more complicated
|
||||||
Assignment = Expression (AssignmentOp Expression)
|
Assignment = Expression (AssignmentOp Expression)
|
||||||
FunctionCall = Expression '(' Expression ( ',' Expression )* ')'
|
FunctionCall = Expression '(' Expression ( ',' Expression )* ')'
|
||||||
NewExpression = 'new' Identifier '(' ( Expression ( ',' Expression )* ) ')'
|
NewExpression = 'new' Identifier
|
||||||
MemberAccess = Expression '.' Identifier
|
MemberAccess = Expression '.' Identifier
|
||||||
IndexAccess = Expression '[' Expresison ']'
|
IndexAccess = Expression '[' Expresison ']'
|
||||||
PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')'
|
PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')'
|
||||||
|
Loading…
Reference in New Issue
Block a user