aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AST.cpp15
-rwxr-xr-xAST.h10
-rw-r--r--AST_accept.h6
-rw-r--r--ExpressionCompiler.cpp102
-rw-r--r--ExpressionCompiler.h4
-rw-r--r--Parser.cpp24
-rw-r--r--Types.cpp3
-rw-r--r--Types.h4
-rw-r--r--grammar.txt2
9 files changed, 85 insertions, 85 deletions
diff --git a/AST.cpp b/AST.cpp
index d171006a..66de5995 100644
--- a/AST.cpp
+++ b/AST.cpp
@@ -309,20 +309,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..048b808a 100755
--- a/AST.h
+++ b/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
{
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/ExpressionCompiler.cpp b/ExpressionCompiler.cpp
index 8cecbb1b..ff08a326 100644
--- a/ExpressionCompiler.cpp
+++ b/ExpressionCompiler.cpp
@@ -235,6 +235,36 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
_functionCall.getExpression().accept(*this);
appendExternalFunctionCall(function, arguments, function.getLocation() == Location::BARE);
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:
{
// stack layout: contract_address function_id [gas] [value]
@@ -316,38 +346,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
bool ExpressionCompiler::visit(NewExpression const& _newExpression)
{
- ContractType const* type = dynamic_cast<ContractType const*>(_newExpression.getType().get());
- solAssert(type, "");
- TypePointers const& types = type->getConstructorType()->getParameterTypes();
- vector<ASTPointer<Expression const>> arguments = _newExpression.getArguments();
- solAssert(arguments.size() == types.size(), "");
-
- // copy the contracts code into memory
- bytes const& bytecode = m_context.getCompiledContract(*_newExpression.getContract());
- m_context << u256(bytecode.size());
- //@todo could be done by actually appending the Assembly, but then we probably need to compile
- // multiple times. Will revisit once external fuctions are inlined.
- m_context.appendData(bytecode);
- //@todo copy to memory position 0, shift as soon as we use memory
- m_context << u256(0) << eth::Instruction::CODECOPY;
-
- unsigned dataOffset = bytecode.size();
- for (unsigned i = 0; i < arguments.size(); ++i)
- {
- arguments[i]->accept(*this);
- appendTypeConversion(*arguments[i]->getType(), *types[i], 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;
}
@@ -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
- for (unsigned i = 0; i < _arguments.size(); ++i)
- {
- _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);
- }
+ 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();
@@ -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,
unsigned _baseStackOffset):
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset),
diff --git a/ExpressionCompiler.h b/ExpressionCompiler.h
index 024c4644..4a696c39 100644
--- a/ExpressionCompiler.h
+++ b/ExpressionCompiler.h
@@ -90,6 +90,10 @@ private:
/// 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,
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/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/Types.cpp b/Types.cpp
index 59b8c31b..ea2da0b6 100644
--- a/Types.cpp
+++ b/Types.cpp
@@ -631,6 +631,7 @@ MemberList const& FunctionType::getMembers() const
switch (m_location)
{
case Location::EXTERNAL:
+ case Location::CREATION:
case Location::ECRECOVER:
case Location::SHA256:
case Location::RIPEMD160:
@@ -644,6 +645,8 @@ MemberList const& FunctionType::getMembers() const
{"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;
diff --git a/Types.h b/Types.h
index 2060987c..c0ffe864 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;
@@ -348,7 +350,7 @@ public:
/// 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,
+ enum class Location { INTERNAL, EXTERNAL, CREATION, SEND,
SHA3, SUICIDE,
ECRECOVER, SHA256, RIPEMD160,
LOG0, LOG1, LOG2, LOG3, LOG4,
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 ')'