mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Specify value for contract creation.
This commit is contained in:
parent
ec022783c4
commit
b440d7e321
15
AST.cpp
15
AST.cpp
@ -309,20 +309,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()
|
||||||
|
10
AST.h
10
AST.h
@ -790,26 +790,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,6 +235,36 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
_functionCall.getExpression().accept(*this);
|
_functionCall.getExpression().accept(*this);
|
||||||
appendExternalFunctionCall(function, arguments, function.getLocation() == Location::BARE);
|
appendExternalFunctionCall(function, arguments, function.getLocation() == Location::BARE);
|
||||||
break;
|
break;
|
||||||
|
case Location::CREATION:
|
||||||
|
{
|
||||||
|
_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;
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Location::SET_GAS:
|
case Location::SET_GAS:
|
||||||
{
|
{
|
||||||
// stack layout: contract_address function_id [gas] [value]
|
// stack layout: contract_address function_id [gas] [value]
|
||||||
@ -316,38 +346,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
|
|
||||||
bool ExpressionCompiler::visit(NewExpression const& _newExpression)
|
bool ExpressionCompiler::visit(NewExpression const& _newExpression)
|
||||||
{
|
{
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -680,21 +679,8 @@ void ExpressionCompiler::appendExternalFunctionCall(FunctionType const& _functio
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsigned dataOffset = bare ? 0 : CompilerUtils::dataStartOffset; // reserve 4 bytes for the function's hash identifier
|
unsigned dataOffset = bare ? 0 : CompilerUtils::dataStartOffset; // reserve 4 bytes for the function's hash identifier
|
||||||
for (unsigned i = 0; i < _arguments.size(); ++i)
|
dataOffset += appendArgumentCopyToMemory(_functionType.getParameterTypes(), _arguments, dataOffset);
|
||||||
{
|
|
||||||
_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);
|
|
||||||
}
|
|
||||||
//@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();
|
||||||
@ -728,6 +714,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),
|
||||||
|
@ -90,6 +90,10 @@ private:
|
|||||||
/// 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,
|
||||||
bool bare = false);
|
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.
|
||||||
|
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())
|
||||||
|
@ -631,6 +631,7 @@ MemberList const& FunctionType::getMembers() const
|
|||||||
switch (m_location)
|
switch (m_location)
|
||||||
{
|
{
|
||||||
case Location::EXTERNAL:
|
case Location::EXTERNAL:
|
||||||
|
case Location::CREATION:
|
||||||
case Location::ECRECOVER:
|
case Location::ECRECOVER:
|
||||||
case Location::SHA256:
|
case Location::SHA256:
|
||||||
case Location::RIPEMD160:
|
case Location::RIPEMD160:
|
||||||
@ -644,6 +645,8 @@ MemberList const& FunctionType::getMembers() const
|
|||||||
{"value", make_shared<FunctionType>(parseElementaryTypeVector({"uint"}),
|
{"value", make_shared<FunctionType>(parseElementaryTypeVector({"uint"}),
|
||||||
TypePointers{copyAndSetGasOrValue(false, true)},
|
TypePointers{copyAndSetGasOrValue(false, true)},
|
||||||
Location::SET_VALUE, m_gasSet, m_valueSet)}};
|
Location::SET_VALUE, m_gasSet, m_valueSet)}};
|
||||||
|
if (m_location == Location::CREATION)
|
||||||
|
members.erase("gas");
|
||||||
m_members.reset(new MemberList(members));
|
m_members.reset(new MemberList(members));
|
||||||
}
|
}
|
||||||
return *m_members;
|
return *m_members;
|
||||||
|
4
Types.h
4
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;
|
||||||
@ -348,7 +350,7 @@ public:
|
|||||||
/// INTERNAL: jump tag, EXTERNAL: contract address + function identifier,
|
/// 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,
|
enum class Location { INTERNAL, EXTERNAL, CREATION, SEND,
|
||||||
SHA3, SUICIDE,
|
SHA3, SUICIDE,
|
||||||
ECRECOVER, SHA256, RIPEMD160,
|
ECRECOVER, SHA256, RIPEMD160,
|
||||||
LOG0, LOG1, LOG2, LOG3, LOG4,
|
LOG0, LOG1, LOG2, LOG3, LOG4,
|
||||||
|
@ -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