Browse Source

Merge pull request #621 from chriseth/sol_createContracts

Contract creation ("new" operator).
cl-refactor
chriseth 10 years ago
parent
commit
47927f731e
  1. 27
      libevmcore/Assembly.cpp
  2. 5
      libevmcore/Assembly.h
  3. 49
      libsolidity/AST.cpp
  4. 32
      libsolidity/AST.h
  5. 1
      libsolidity/ASTForward.h
  6. 13
      libsolidity/ASTPrinter.cpp
  7. 2
      libsolidity/ASTPrinter.h
  8. 4
      libsolidity/ASTVisitor.h
  9. 20
      libsolidity/AST_accept.h
  10. 29
      libsolidity/Compiler.cpp
  11. 6
      libsolidity/Compiler.h
  12. 8
      libsolidity/CompilerContext.cpp
  13. 14
      libsolidity/CompilerContext.h
  14. 6
      libsolidity/CompilerStack.cpp
  15. 2
      libsolidity/CompilerStack.h
  16. 38
      libsolidity/ExpressionCompiler.cpp
  17. 1
      libsolidity/ExpressionCompiler.h
  18. 8
      libsolidity/InterfaceHandler.cpp
  19. 8
      libsolidity/InterfaceHandler.h
  20. 6
      libsolidity/NameAndTypeResolver.cpp
  21. 12
      libsolidity/Parser.cpp
  22. 13
      libsolidity/Types.cpp
  23. 11
      libsolidity/Types.h
  24. 5
      libsolidity/grammar.txt
  25. 2
      test/solidityCompiler.cpp
  26. 26
      test/solidityEndToEndTest.cpp
  27. 9
      test/solidityNameAndTypeResolution.cpp

27
libevmcore/Assembly.cpp

@ -39,6 +39,7 @@ unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const
case Push: case Push:
return 1 + max<unsigned>(1, dev::bytesRequired(m_data)); return 1 + max<unsigned>(1, dev::bytesRequired(m_data));
case PushSubSize: case PushSubSize:
case PushProgramSize:
return 4; // worst case: a 16MB program return 4; // worst case: a 16MB program
case PushTag: case PushTag:
case PushData: case PushData:
@ -59,7 +60,13 @@ int AssemblyItem::deposit() const
{ {
case Operation: case Operation:
return instructionInfo((Instruction)(byte)m_data).ret - instructionInfo((Instruction)(byte)m_data).args; return instructionInfo((Instruction)(byte)m_data).ret - instructionInfo((Instruction)(byte)m_data).args;
case Push: case PushString: case PushTag: case PushData: case PushSub: case PushSubSize: case Push:
case PushString:
case PushTag:
case PushData:
case PushSub:
case PushSubSize:
case PushProgramSize:
return 1; return 1;
case Tag: case Tag:
return 0; return 0;
@ -146,6 +153,9 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItemsConstRef _i)
case PushSubSize: case PushSubSize:
_out << " PUSHss[" << hex << h256(i.data()).abridged() << "]"; _out << " PUSHss[" << hex << h256(i.data()).abridged() << "]";
break; break;
case PushProgramSize:
_out << " PUSHSIZE";
break;
case NoOptimizeBegin: case NoOptimizeBegin:
_out << " DoNotOptimze{{"; _out << " DoNotOptimze{{";
break; break;
@ -185,6 +195,9 @@ ostream& Assembly::streamRLP(ostream& _out, string const& _prefix) const
case PushSubSize: case PushSubSize:
_out << _prefix << " PUSH #[$" << h256(i.m_data).abridged() << "]" << endl; _out << _prefix << " PUSH #[$" << h256(i.m_data).abridged() << "]" << endl;
break; break;
case PushProgramSize:
_out << _prefix << " PUSHSIZE" << endl;
break;
case Tag: case Tag:
_out << _prefix << "tag" << i.m_data << ": " << endl << _prefix << " JUMPDEST" << endl; _out << _prefix << "tag" << i.m_data << ": " << endl << _prefix << " JUMPDEST" << endl;
break; break;
@ -303,6 +316,7 @@ Assembly& Assembly::optimise(bool _enable)
{ { PushString, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, { { PushString, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushSub, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, { { PushSub, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushSubSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, { { PushSubSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { PushProgramSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
{ { Push, PushTag, Instruction::JUMPI }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].data()) return { m[1], Instruction::JUMP }; else return {}; } }, { { Push, PushTag, Instruction::JUMPI }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].data()) return { m[1], Instruction::JUMP }; else return {}; } },
{ { Instruction::ISZERO, Instruction::ISZERO }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, { { Instruction::ISZERO, Instruction::ISZERO }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } },
}; };
@ -468,6 +482,7 @@ bytes Assembly::assemble() const
vector<unsigned> tagPos(m_usedTags); vector<unsigned> tagPos(m_usedTags);
map<unsigned, unsigned> tagRef; map<unsigned, unsigned> tagRef;
multimap<h256, unsigned> dataRef; multimap<h256, unsigned> dataRef;
vector<unsigned> sizeRef; ///< Pointers to code locations where the size of the program is inserted
unsigned bytesPerTag = dev::bytesRequired(totalBytes); unsigned bytesPerTag = dev::bytesRequired(totalBytes);
byte tagPush = (byte)Instruction::PUSH1 - 1 + bytesPerTag; byte tagPush = (byte)Instruction::PUSH1 - 1 + bytesPerTag;
@ -526,6 +541,11 @@ bytes Assembly::assemble() const
toBigEndian(s, byr); toBigEndian(s, byr);
break; break;
} }
case PushProgramSize:
ret.push_back(tagPush);
sizeRef.push_back(ret.size());
ret.resize(ret.size() + bytesPerTag);
break;
case Tag: case Tag:
tagPos[(unsigned)i.m_data] = ret.size(); tagPos[(unsigned)i.m_data] = ret.size();
ret.push_back((byte)Instruction::JUMPDEST); ret.push_back((byte)Instruction::JUMPDEST);
@ -561,5 +581,10 @@ bytes Assembly::assemble() const
} }
} }
} }
for (unsigned pos: sizeRef)
{
bytesRef r(ret.data() + pos, bytesPerTag);
toBigEndian(ret.size(), r);
}
return ret; return ret;
} }

5
libevmcore/Assembly.h

@ -32,7 +32,7 @@ namespace dev
namespace eth namespace eth
{ {
enum AssemblyItemType { UndefinedItem, Operation, Push, PushString, PushTag, PushSub, PushSubSize, Tag, PushData, NoOptimizeBegin, NoOptimizeEnd }; enum AssemblyItemType { UndefinedItem, Operation, Push, PushString, PushTag, PushSub, PushSubSize, PushProgramSize, Tag, PushData, NoOptimizeBegin, NoOptimizeEnd };
class Assembly; class Assembly;
@ -86,6 +86,9 @@ public:
AssemblyItem const& append(std::string const& _data) { return append(newPushString(_data)); } AssemblyItem const& append(std::string const& _data) { return append(newPushString(_data)); }
AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); } AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); }
AssemblyItem appendSubSize(Assembly const& _a) { auto ret = newSub(_a); append(newPushSubSize(ret.data())); return ret; } AssemblyItem appendSubSize(Assembly const& _a) { auto ret = newSub(_a); append(newPushSubSize(ret.data())); return ret; }
/// Pushes the final size of the current assembly itself. Use this when the code is modified
/// after compilation and CODESIZE is not an option.
void appendProgramSize() { append(AssemblyItem(PushProgramSize)); }
AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; } AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; }
AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; } AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; }

49
libsolidity/AST.cpp

@ -39,6 +39,17 @@ TypeError ASTNode::createTypeError(string const& _description) const
return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description); return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description);
} }
void ContractDefinition::checkTypeRequirements()
{
FunctionDefinition const* constructor = getConstructor();
if (constructor && !constructor->getReturnParameters().empty())
BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError(
"Non-empty \"returns\" directive for constructor."));
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
function->checkTypeRequirements();
}
vector<FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const vector<FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() const
{ {
vector<FunctionDefinition const*> exportedFunctions; vector<FunctionDefinition const*> exportedFunctions;
@ -54,6 +65,14 @@ vector<FunctionDefinition const*> ContractDefinition::getInterfaceFunctions() co
return exportedFunctions; return exportedFunctions;
} }
FunctionDefinition const* ContractDefinition::getConstructor() const
{
for (ASTPointer<FunctionDefinition> const& f: m_definedFunctions)
if (f->getName() == getName())
return f.get();
return nullptr;
}
void StructDefinition::checkMemberTypes() const void StructDefinition::checkMemberTypes() const
{ {
for (ASTPointer<VariableDeclaration> const& member: getMembers()) for (ASTPointer<VariableDeclaration> const& member: getMembers())
@ -235,13 +254,12 @@ void FunctionCall::checkTypeRequirements()
BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed.")); BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed."));
m_type = type.getActualType(); m_type = type.getActualType();
} }
else else if (FunctionType const* functionType = dynamic_cast<FunctionType const*>(expressionType))
{ {
//@todo would be nice to create a struct type from the arguments //@todo would be nice to create a struct type from the arguments
// and then ask if that is implicitly convertible to the struct represented by the // and then ask if that is implicitly convertible to the struct represented by the
// function parameters // function parameters
FunctionType const& functionType = dynamic_cast<FunctionType const&>(*expressionType); TypePointers const& parameterTypes = functionType->getParameterTypes();
TypePointers const& parameterTypes = functionType.getParameterTypes();
if (parameterTypes.size() != m_arguments.size()) if (parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call.")); BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call."));
for (size_t i = 0; i < m_arguments.size(); ++i) for (size_t i = 0; i < m_arguments.size(); ++i)
@ -249,11 +267,13 @@ void FunctionCall::checkTypeRequirements()
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call.")); BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in function call."));
// @todo actually the return type should be an anonymous struct, // @todo actually the return type should be an anonymous struct,
// but we change it to the type of the first return value until we have structs // but we change it to the type of the first return value until we have structs
if (functionType.getReturnParameterTypes().empty()) if (functionType->getReturnParameterTypes().empty())
m_type = make_shared<VoidType>(); m_type = make_shared<VoidType>();
else else
m_type = functionType.getReturnParameterTypes().front(); m_type = functionType->getReturnParameterTypes().front();
} }
else
BOOST_THROW_EXCEPTION(createTypeError("Type is not callable."));
} }
bool FunctionCall::isTypeConversion() const bool FunctionCall::isTypeConversion() const
@ -261,6 +281,25 @@ bool FunctionCall::isTypeConversion() const
return m_expression->getType()->getCategory() == Type::Category::TYPE; return m_expression->getType()->getCategory() == Type::Category::TYPE;
} }
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 const>(*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."));
}
void MemberAccess::checkTypeRequirements() void MemberAccess::checkTypeRequirements()
{ {
m_expression->checkTypeRequirements(); m_expression->checkTypeRequirements();

32
libsolidity/AST.h

@ -174,6 +174,10 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; } std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; }
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; } std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; }
/// Checks that the constructor does not have a "returns" statement and calls
/// checkTypeRequirements on all its functions.
void checkTypeRequirements();
/// @return A shared pointer of an ASTString. /// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation /// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; } ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
@ -181,6 +185,9 @@ public:
/// Returns the functions that make up the calling interface in the intended order. /// Returns the functions that make up the calling interface in the intended order.
std::vector<FunctionDefinition const*> getInterfaceFunctions() const; std::vector<FunctionDefinition const*> getInterfaceFunctions() const;
/// Returns the constructor or nullptr if no constructor was specified
FunctionDefinition const* getConstructor() const;
private: private:
std::vector<ASTPointer<StructDefinition>> m_definedStructs; std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables; std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
@ -740,6 +747,31 @@ private:
std::vector<ASTPointer<Expression>> m_arguments; std::vector<ASTPointer<Expression>> m_arguments;
}; };
/**
* Expression that creates a new contract, e.g. "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) {}
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 { if (asserts(m_contract)) BOOST_THROW_EXCEPTION(InternalCompilerError()); else return m_contract; }
private:
ASTPointer<Identifier> m_contractName;
std::vector<ASTPointer<Expression>> m_arguments;
ContractDefinition const* m_contract = nullptr;
};
/** /**
* Access to a member of an object. Example: x.name * Access to a member of an object. Example: x.name
*/ */

1
libsolidity/ASTForward.h

@ -62,6 +62,7 @@ class Assignment;
class UnaryOperation; class UnaryOperation;
class BinaryOperation; class BinaryOperation;
class FunctionCall; class FunctionCall;
class NewExpression;
class MemberAccess; class MemberAccess;
class IndexAccess; class IndexAccess;
class PrimaryExpression; class PrimaryExpression;

13
libsolidity/ASTPrinter.cpp

@ -226,6 +226,14 @@ bool ASTPrinter::visit(FunctionCall const& _node)
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(NewExpression const& _node)
{
writeLine("NewExpression");
printType(_node);
printSourcePart(_node);
return goDeeper();
}
bool ASTPrinter::visit(MemberAccess const& _node) bool ASTPrinter::visit(MemberAccess const& _node)
{ {
writeLine("MemberAccess to member " + _node.getMemberName()); writeLine("MemberAccess to member " + _node.getMemberName());
@ -402,6 +410,11 @@ void ASTPrinter::endVisit(FunctionCall const&)
m_indentation--; m_indentation--;
} }
void ASTPrinter::endVisit(NewExpression const&)
{
m_indentation--;
}
void ASTPrinter::endVisit(MemberAccess const&) void ASTPrinter::endVisit(MemberAccess const&)
{ {
m_indentation--; m_indentation--;

2
libsolidity/ASTPrinter.h

@ -67,6 +67,7 @@ public:
bool visit(UnaryOperation const& _node) override; bool visit(UnaryOperation const& _node) override;
bool visit(BinaryOperation const& _node) override; bool visit(BinaryOperation const& _node) override;
bool visit(FunctionCall const& _node) override; bool visit(FunctionCall const& _node) override;
bool visit(NewExpression const& _node) override;
bool visit(MemberAccess const& _node) override; bool visit(MemberAccess const& _node) override;
bool visit(IndexAccess const& _node) override; bool visit(IndexAccess const& _node) override;
bool visit(PrimaryExpression const& _node) override; bool visit(PrimaryExpression const& _node) override;
@ -99,6 +100,7 @@ public:
void endVisit(UnaryOperation const&) override; void endVisit(UnaryOperation const&) override;
void endVisit(BinaryOperation const&) override; void endVisit(BinaryOperation const&) override;
void endVisit(FunctionCall const&) override; void endVisit(FunctionCall const&) override;
void endVisit(NewExpression const&) override;
void endVisit(MemberAccess const&) override; void endVisit(MemberAccess const&) override;
void endVisit(IndexAccess const&) override; void endVisit(IndexAccess const&) override;
void endVisit(PrimaryExpression const&) override; void endVisit(PrimaryExpression const&) override;

4
libsolidity/ASTVisitor.h

@ -68,6 +68,7 @@ public:
virtual bool visit(UnaryOperation&) { return true; } virtual bool visit(UnaryOperation&) { return true; }
virtual bool visit(BinaryOperation&) { return true; } virtual bool visit(BinaryOperation&) { return true; }
virtual bool visit(FunctionCall&) { return true; } virtual bool visit(FunctionCall&) { return true; }
virtual bool visit(NewExpression&) { return true; }
virtual bool visit(MemberAccess&) { return true; } virtual bool visit(MemberAccess&) { return true; }
virtual bool visit(IndexAccess&) { return true; } virtual bool visit(IndexAccess&) { return true; }
virtual bool visit(PrimaryExpression&) { return true; } virtual bool visit(PrimaryExpression&) { return true; }
@ -102,6 +103,7 @@ public:
virtual void endVisit(UnaryOperation&) { } virtual void endVisit(UnaryOperation&) { }
virtual void endVisit(BinaryOperation&) { } virtual void endVisit(BinaryOperation&) { }
virtual void endVisit(FunctionCall&) { } virtual void endVisit(FunctionCall&) { }
virtual void endVisit(NewExpression&) { }
virtual void endVisit(MemberAccess&) { } virtual void endVisit(MemberAccess&) { }
virtual void endVisit(IndexAccess&) { } virtual void endVisit(IndexAccess&) { }
virtual void endVisit(PrimaryExpression&) { } virtual void endVisit(PrimaryExpression&) { }
@ -140,6 +142,7 @@ public:
virtual bool visit(UnaryOperation const&) { return true; } virtual bool visit(UnaryOperation const&) { return true; }
virtual bool visit(BinaryOperation const&) { return true; } virtual bool visit(BinaryOperation const&) { return true; }
virtual bool visit(FunctionCall const&) { return true; } virtual bool visit(FunctionCall const&) { return true; }
virtual bool visit(NewExpression const&) { return true; }
virtual bool visit(MemberAccess const&) { return true; } virtual bool visit(MemberAccess const&) { return true; }
virtual bool visit(IndexAccess const&) { return true; } virtual bool visit(IndexAccess const&) { return true; }
virtual bool visit(PrimaryExpression const&) { return true; } virtual bool visit(PrimaryExpression const&) { return true; }
@ -174,6 +177,7 @@ public:
virtual void endVisit(UnaryOperation const&) { } virtual void endVisit(UnaryOperation const&) { }
virtual void endVisit(BinaryOperation const&) { } virtual void endVisit(BinaryOperation const&) { }
virtual void endVisit(FunctionCall const&) { } virtual void endVisit(FunctionCall const&) { }
virtual void endVisit(NewExpression const&) { }
virtual void endVisit(MemberAccess const&) { } virtual void endVisit(MemberAccess const&) { }
virtual void endVisit(IndexAccess const&) { } virtual void endVisit(IndexAccess const&) { }
virtual void endVisit(PrimaryExpression const&) { } virtual void endVisit(PrimaryExpression const&) { }

20
libsolidity/AST_accept.h

@ -419,6 +419,26 @@ void FunctionCall::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
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);
}
void MemberAccess::accept(ASTVisitor& _visitor) void MemberAccess::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))

29
libsolidity/Compiler.cpp

@ -33,9 +33,11 @@ using namespace std;
namespace dev { namespace dev {
namespace solidity { namespace solidity {
void Compiler::compileContract(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals) void Compiler::compileContract(ContractDefinition const& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals,
map<ContractDefinition const*, bytes const*> const& _contracts)
{ {
m_context = CompilerContext(); // clear it just in case m_context = CompilerContext(); // clear it just in case
m_context.setCompiledContracts(_contracts);
for (MagicVariableDeclaration const* variable: _magicGlobals) for (MagicVariableDeclaration const* variable: _magicGlobals)
m_context.addMagicGlobal(*variable); m_context.addMagicGlobal(*variable);
@ -50,12 +52,14 @@ void Compiler::compileContract(ContractDefinition const& _contract, vector<Magic
if (function->getName() != _contract.getName()) // don't add the constructor here if (function->getName() != _contract.getName()) // don't add the constructor here
function->accept(*this); function->accept(*this);
packIntoContractCreator(_contract); packIntoContractCreator(_contract, _contracts);
} }
void Compiler::packIntoContractCreator(ContractDefinition const& _contract) void Compiler::packIntoContractCreator(ContractDefinition const& _contract,
map<ContractDefinition const*, bytes const*> const& _contracts)
{ {
CompilerContext runtimeContext; CompilerContext runtimeContext;
runtimeContext.setCompiledContracts(_contracts);
swap(m_context, runtimeContext); swap(m_context, runtimeContext);
registerStateVariables(_contract); registerStateVariables(_contract);
@ -67,22 +71,32 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract)
constructor = function.get(); constructor = function.get();
break; break;
} }
eth::AssemblyItem sub = m_context.addSubroutine(runtimeContext.getAssembly());
// stack contains sub size
if (constructor) if (constructor)
{ {
eth::AssemblyItem returnTag = m_context.pushNewTag(); eth::AssemblyItem returnTag = m_context.pushNewTag();
m_context.addFunction(*constructor); // note that it cannot be called due to syntactic reasons m_context.addFunction(*constructor); // note that it cannot be called due to syntactic reasons
//@todo copy constructor arguments from calldata to memory prior to this // copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
unsigned argumentSize = 0;
for (ASTPointer<VariableDeclaration> const& var: constructor->getParameters())
argumentSize += var->getType()->getCalldataEncodedSize();
if (argumentSize > 0)
{
m_context << u256(argumentSize);
m_context.appendProgramSize();
m_context << u256(1); // copy it to byte one as expected for ABI calls
m_context << eth::Instruction::CODECOPY;
appendCalldataUnpacker(*constructor, true);
}
//@todo calling other functions inside the constructor should either trigger a parse error //@todo calling other functions inside the constructor should either trigger a parse error
//or we should copy them here (register them above and call "accept") - detecting which //or we should copy them here (register them above and call "accept") - detecting which
// functions are referenced / called needs to be done in a recursive way. // functions are referenced / called needs to be done in a recursive way.
appendCalldataUnpacker(*constructor, true);
m_context.appendJumpTo(m_context.getFunctionEntryLabel(*constructor)); m_context.appendJumpTo(m_context.getFunctionEntryLabel(*constructor));
constructor->accept(*this); constructor->accept(*this);
m_context << returnTag; m_context << returnTag;
} }
eth::AssemblyItem sub = m_context.addSubroutine(runtimeContext.getAssembly());
// stack contains sub size
m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY; m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY;
m_context << u256(0) << eth::Instruction::RETURN; m_context << u256(0) << eth::Instruction::RETURN;
} }
@ -130,7 +144,6 @@ unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, b
{ {
// We do not check the calldata size, everything is zero-padded. // We do not check the calldata size, everything is zero-padded.
unsigned dataOffset = 1; unsigned dataOffset = 1;
//@todo this can be done more efficiently, saving some CALLDATALOAD calls //@todo this can be done more efficiently, saving some CALLDATALOAD calls
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters()) for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
{ {

6
libsolidity/Compiler.h

@ -32,14 +32,16 @@ class Compiler: private ASTConstVisitor
public: public:
explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_returnTag(m_context.newTag()) {} explicit Compiler(bool _optimize = false): m_optimize(_optimize), m_returnTag(m_context.newTag()) {}
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);
bytes getAssembledBytecode() { return m_context.getAssembledBytecode(m_optimize); } bytes getAssembledBytecode() { return m_context.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:
/// Creates a new compiler context / assembly, packs the current code into the data part and /// Creates a new compiler context / assembly, packs the current code into the data part and
/// adds the constructor code. /// adds the constructor code.
void packIntoContractCreator(ContractDefinition const& _contract); void packIntoContractCreator(ContractDefinition const& _contract,
std::map<ContractDefinition const*, bytes const*> const& _contracts);
void appendFunctionSelector(ContractDefinition const& _contract); void appendFunctionSelector(ContractDefinition const& _contract);
/// Creates code that unpacks the arguments for the given function, from memory if /// Creates code that unpacks the arguments for the given function, from memory if
/// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes. /// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes.

8
libsolidity/CompilerContext.cpp

@ -62,6 +62,14 @@ void CompilerContext::addFunction(FunctionDefinition const& _function)
m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag())); m_functionEntryLabels.insert(std::make_pair(&_function, m_asm.newTag()));
} }
bytes const& CompilerContext::getCompiledContract(const ContractDefinition& _contract) const
{
auto ret = m_compiledContracts.find(&_contract);
if (asserts(ret != m_compiledContracts.end()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Compiled contract not found."));
return *ret->second;
}
bool CompilerContext::isLocalVariable(Declaration const* _declaration) const bool CompilerContext::isLocalVariable(Declaration const* _declaration) const
{ {
return m_localVariables.count(_declaration) > 0; return m_localVariables.count(_declaration) > 0;

14
libsolidity/CompilerContext.h

@ -39,8 +39,6 @@ namespace solidity {
class CompilerContext class CompilerContext
{ {
public: public:
CompilerContext(): m_stateVariablesSize(0) {}
void addMagicGlobal(MagicVariableDeclaration const& _declaration); void addMagicGlobal(MagicVariableDeclaration const& _declaration);
void addStateVariable(VariableDeclaration const& _declaration); void addStateVariable(VariableDeclaration const& _declaration);
void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); } void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); }
@ -48,6 +46,9 @@ public:
void addAndInitializeVariable(VariableDeclaration const& _declaration); void addAndInitializeVariable(VariableDeclaration const& _declaration);
void addFunction(FunctionDefinition const& _function); void addFunction(FunctionDefinition const& _function);
void setCompiledContracts(std::map<ContractDefinition const*, bytes const*> const& _contracts) { m_compiledContracts = _contracts; }
bytes const& getCompiledContract(ContractDefinition const& _contract) const;
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); } void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration); } bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration); }
@ -80,6 +81,10 @@ public:
/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag) /// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
/// on the stack. @returns the assembly item corresponding to the pushed subroutine, i.e. its offset. /// on the stack. @returns the assembly item corresponding to the pushed subroutine, i.e. its offset.
eth::AssemblyItem addSubroutine(eth::Assembly const& _assembly) { return m_asm.appendSubSize(_assembly); } eth::AssemblyItem addSubroutine(eth::Assembly const& _assembly) { return m_asm.appendSubSize(_assembly); }
/// Pushes the size of the final program
void appendProgramSize() { return m_asm.appendProgramSize(); }
/// Adds data to the data section, pushes a reference to the stack
eth::AssemblyItem appendData(bytes const& _data) { return m_asm.append(_data); }
/// Append elements to the current instruction list and adjust @a m_stackOffset. /// Append elements to the current instruction list and adjust @a m_stackOffset.
CompilerContext& operator<<(eth::AssemblyItem const& _item) { m_asm.append(_item); return *this; } CompilerContext& operator<<(eth::AssemblyItem const& _item) { m_asm.append(_item); return *this; }
@ -90,13 +95,16 @@ public:
eth::Assembly const& getAssembly() const { return m_asm; } eth::Assembly const& getAssembly() const { return m_asm; }
void streamAssembly(std::ostream& _stream) const { _stream << m_asm; } void streamAssembly(std::ostream& _stream) const { _stream << m_asm; }
bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); } bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); }
private: private:
eth::Assembly m_asm; eth::Assembly m_asm;
/// Magic global variables like msg, tx or this, distinguished by type. /// Magic global variables like msg, tx or this, distinguished by type.
std::set<Declaration const*> m_magicGlobals; std::set<Declaration const*> m_magicGlobals;
/// Other already compiled contracts to be used in contract creation calls.
std::map<ContractDefinition const*, bytes const*> m_compiledContracts;
/// Size of the state variables, offset of next variable to be added. /// Size of the state variables, offset of next variable to be added.
u256 m_stateVariablesSize; u256 m_stateVariablesSize = 0;
/// Storage offsets of state variables /// Storage offsets of state variables
std::map<Declaration const*, u256> m_stateVariables; std::map<Declaration const*, u256> m_stateVariables;
/// Offsets of local variables on the stack (relative to stack base). /// Offsets of local variables on the stack (relative to stack base).

6
libsolidity/CompilerStack.cpp

@ -96,16 +96,20 @@ void CompilerStack::compile(bool _optimize)
{ {
if (!m_parseSuccessful) if (!m_parseSuccessful)
parse(); parse();
map<ContractDefinition const*, bytes const*> contractBytecode;
for (Source const* source: m_sourceOrder) for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->getNodes()) for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{ {
m_globalContext->setCurrentContract(*contract); m_globalContext->setCurrentContract(*contract);
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize); shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
compiler->compileContract(*contract, m_globalContext->getMagicVariables()); compiler->compileContract(*contract, m_globalContext->getMagicVariables(),
contractBytecode);
Contract& compiledContract = m_contracts[contract->getName()]; Contract& compiledContract = m_contracts[contract->getName()];
compiledContract.bytecode = compiler->getAssembledBytecode(); compiledContract.bytecode = compiler->getAssembledBytecode();
compiledContract.compiler = move(compiler); compiledContract.compiler = move(compiler);
contractBytecode[compiledContract.contract] = &compiledContract.bytecode;
} }
} }

2
libsolidity/CompilerStack.h

@ -108,7 +108,7 @@ private:
struct Contract struct Contract
{ {
ContractDefinition* contract; ContractDefinition const* contract;
std::shared_ptr<Compiler> compiler; std::shared_ptr<Compiler> compiler;
bytes bytecode; bytes bytecode;
std::shared_ptr<InterfaceHandler> interfaceHandler; std::shared_ptr<InterfaceHandler> interfaceHandler;

38
libsolidity/ExpressionCompiler.cpp

@ -279,6 +279,44 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
return false; return false;
} }
bool ExpressionCompiler::visit(NewExpression const& _newExpression)
{
ContractType const* type = dynamic_cast<ContractType const*>(_newExpression.getType().get());
if (asserts(type))
BOOST_THROW_EXCEPTION(InternalCompilerError());
TypePointers const& types = type->getConstructorType()->getParameterTypes();
vector<ASTPointer<Expression const>> arguments = _newExpression.getArguments();
if (asserts(arguments.size() == types.size()))
BOOST_THROW_EXCEPTION(InternalCompilerError());
// 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]);
unsigned const numBytes = types[i]->getCalldataEncodedSize();
if (numBytes > 32)
BOOST_THROW_EXCEPTION(CompilerError()
<< errinfo_sourceLocation(arguments[i]->getLocation())
<< errinfo_comment("Type " + types[i]->toString() + " not yet supported."));
bool const leftAligned = types[i]->getCategory() == Type::Category::STRING;
CompilerUtils(m_context).storeInMemory(dataOffset, numBytes, leftAligned);
dataOffset += numBytes;
}
// size, offset, endowment
m_context << u256(dataOffset) << u256(0) << u256(0) << eth::Instruction::CREATE;
return false;
}
void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
{ {
ASTString const& member = _memberAccess.getMemberName(); ASTString const& member = _memberAccess.getMemberName();

1
libsolidity/ExpressionCompiler.h

@ -60,6 +60,7 @@ private:
virtual void endVisit(UnaryOperation const& _unaryOperation) override; virtual void endVisit(UnaryOperation const& _unaryOperation) override;
virtual bool visit(BinaryOperation const& _binaryOperation) override; virtual bool visit(BinaryOperation const& _binaryOperation) override;
virtual bool visit(FunctionCall const& _functionCall) override; virtual bool visit(FunctionCall const& _functionCall) override;
virtual bool visit(NewExpression const& _newExpression) override;
virtual void endVisit(MemberAccess const& _memberAccess) override; virtual void endVisit(MemberAccess const& _memberAccess) override;
virtual bool visit(IndexAccess const& _indexAccess) override; virtual bool visit(IndexAccess const& _indexAccess) override;
virtual void endVisit(Identifier const& _identifier) override; virtual void endVisit(Identifier const& _identifier) override;

8
libsolidity/InterfaceHandler.cpp

@ -15,7 +15,7 @@ InterfaceHandler::InterfaceHandler()
m_lastTag = DocTagType::NONE; m_lastTag = DocTagType::NONE;
} }
std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefinition& _contractDef, std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefinition const& _contractDef,
DocumentationType _type) DocumentationType _type)
{ {
switch(_type) switch(_type)
@ -32,7 +32,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDocumentation(ContractDefiniti
return nullptr; return nullptr;
} }
std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinition& _contractDef) std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinition const& _contractDef)
{ {
Json::Value methods(Json::arrayValue); Json::Value methods(Json::arrayValue);
@ -63,7 +63,7 @@ std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinitio
return std::unique_ptr<std::string>(new std::string(m_writer.write(methods))); return std::unique_ptr<std::string>(new std::string(m_writer.write(methods)));
} }
std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefinition& _contractDef) std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefinition const& _contractDef)
{ {
Json::Value doc; Json::Value doc;
Json::Value methods(Json::objectValue); Json::Value methods(Json::objectValue);
@ -88,7 +88,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
return std::unique_ptr<std::string>(new std::string(m_writer.write(doc))); return std::unique_ptr<std::string>(new std::string(m_writer.write(doc)));
} }
std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefinition& _contractDef) std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefinition const& _contractDef)
{ {
// LTODO: Somewhere in this function warnings for mismatch of param names // LTODO: Somewhere in this function warnings for mismatch of param names
// should be thrown // should be thrown

8
libsolidity/InterfaceHandler.h

@ -67,23 +67,23 @@ public:
/// types provided by @c DocumentationType /// types provided by @c DocumentationType
/// @return A unique pointer contained string with the json /// @return A unique pointer contained string with the json
/// representation of provided type /// representation of provided type
std::unique_ptr<std::string> getDocumentation(ContractDefinition& _contractDef, std::unique_ptr<std::string> getDocumentation(ContractDefinition const& _contractDef,
DocumentationType _type); DocumentationType _type);
/// Get the ABI Interface of the contract /// Get the ABI Interface of the contract
/// @param _contractDef The contract definition /// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json /// @return A unique pointer contained string with the json
/// representation of the contract's ABI Interface /// representation of the contract's ABI Interface
std::unique_ptr<std::string> getABIInterface(ContractDefinition& _contractDef); std::unique_ptr<std::string> getABIInterface(ContractDefinition const& _contractDef);
/// Get the User documentation of the contract /// Get the User documentation of the contract
/// @param _contractDef The contract definition /// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json /// @return A unique pointer contained string with the json
/// representation of the contract's user documentation /// representation of the contract's user documentation
std::unique_ptr<std::string> getUserDocumentation(ContractDefinition& _contractDef); std::unique_ptr<std::string> getUserDocumentation(ContractDefinition const& _contractDef);
/// Get the Developer's documentation of the contract /// Get the Developer's documentation of the contract
/// @param _contractDef The contract definition /// @param _contractDef The contract definition
/// @return A unique pointer contained string with the json /// @return A unique pointer contained string with the json
/// representation of the contract's developer documentation /// representation of the contract's developer documentation
std::unique_ptr<std::string> getDevDocumentation(ContractDefinition& _contractDef); std::unique_ptr<std::string> getDevDocumentation(ContractDefinition const& _contractDef);
private: private:
void resetUser(); void resetUser();

6
libsolidity/NameAndTypeResolver.cpp

@ -62,11 +62,7 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
// First, the parameter types of all functions need to be resolved before we can check // First, the parameter types of all functions need to be resolved before we can check
// the types, since it is possible to call functions that are only defined later // the types, since it is possible to call functions that are only defined later
// in the source. // in the source.
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions()) _contract.checkTypeRequirements();
{
m_currentScope = &m_scopes[function.get()];
function->checkTypeRequirements();
}
m_currentScope = &m_scopes[nullptr]; m_currentScope = &m_scopes[nullptr];
} }

12
libsolidity/Parser.cpp

@ -437,7 +437,17 @@ ASTPointer<Expression> Parser::parseUnaryExpression()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
Token::Value token = m_scanner->getCurrentToken(); Token::Value token = m_scanner->getCurrentToken();
if (Token::isUnaryOp(token) || Token::isCountOp(token)) 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))
{ {
// prefix expression // prefix expression
m_scanner->next(); m_scanner->next();

13
libsolidity/Types.cpp

@ -310,6 +310,19 @@ MemberList const& ContractType::getMembers() const
return *m_members; return *m_members;
} }
shared_ptr<FunctionType const> const& ContractType::getConstructorType() const
{
if (!m_constructorType)
{
FunctionDefinition const* constructor = m_contract.getConstructor();
if (constructor)
m_constructorType = make_shared<FunctionType const>(*constructor);
else
m_constructorType = make_shared<FunctionType const>(TypePointers(), TypePointers());
}
return m_constructorType;
}
unsigned ContractType::getFunctionIndex(string const& _functionName) const unsigned ContractType::getFunctionIndex(string const& _functionName) const
{ {
unsigned index = 0; unsigned index = 0;

11
libsolidity/Types.h

@ -39,6 +39,7 @@ namespace solidity
// @todo realMxN, dynamic strings, text, arrays // @todo realMxN, dynamic strings, text, arrays
class Type; // forward class Type; // forward
class FunctionType; // forward
using TypePointer = std::shared_ptr<Type const>; using TypePointer = std::shared_ptr<Type const>;
using TypePointers = std::vector<TypePointer>; using TypePointers = std::vector<TypePointer>;
@ -249,10 +250,16 @@ public:
virtual MemberList const& getMembers() const override; virtual MemberList const& getMembers() const override;
/// 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;
unsigned getFunctionIndex(std::string const& _functionName) const; unsigned getFunctionIndex(std::string const& _functionName) const;
private: private:
ContractDefinition const& m_contract; ContractDefinition const& m_contract;
/// Type of the constructor, @see getConstructorType. Lazily initialized.
mutable std::shared_ptr<FunctionType const> m_constructorType;
/// List of member types, will be lazy-initialized because of recursive references. /// List of member types, will be lazy-initialized because of recursive references.
mutable std::unique_ptr<MemberList> m_members; mutable std::unique_ptr<MemberList> m_members;
}; };
@ -339,8 +346,8 @@ public:
virtual std::string toString() const override; virtual std::string toString() const override;
virtual bool canLiveOutsideStorage() const override { return false; } virtual bool canLiveOutsideStorage() const override { return false; }
TypePointer getKeyType() const { return m_keyType; } TypePointer const& getKeyType() const { return m_keyType; }
TypePointer getValueType() const { return m_valueType; } TypePointer const& getValueType() const { return m_valueType; }
private: private:
TypePointer m_keyType; TypePointer m_keyType;

5
libsolidity/grammar.txt

@ -25,11 +25,12 @@ Break = 'break' ';'
Return = 'return' Expression? ';' Return = 'return' Expression? ';'
VariableDefinition = VariableDeclaration ( = Expression )? ';' VariableDefinition = VariableDeclaration ( = Expression )? ';'
Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | IndexAccess | Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | NewExpression | IndexAccess |
MemberAccess | PrimaryExpression MemberAccess | PrimaryExpression
// 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 )* ) ')'
MemberAccess = Expression '.' Identifier MemberAccess = Expression '.' Identifier
IndexAccess = Expression '[' Expresison ']' IndexAccess = Expression '[' Expresison ']'
PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')' PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')'

2
test/solidityCompiler.cpp

@ -56,7 +56,7 @@ bytes compileContract(const string& _sourceCode)
BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract));
Compiler compiler; Compiler compiler;
compiler.compileContract(*contract, {}); compiler.compileContract(*contract, {}, {});
// debug // debug
//compiler.streamAssembly(cout); //compiler.streamAssembly(cout);
return compiler.getAssembledBytecode(); return compiler.getAssembledBytecode();

26
test/solidityEndToEndTest.cpp

@ -1012,6 +1012,32 @@ BOOST_AUTO_TEST_CASE(strings_in_calls)
BOOST_CHECK(callContractFunction(0, bytes({0, 'a', 1})) == bytes({0, 'a', 0, 0, 0})); BOOST_CHECK(callContractFunction(0, bytes({0, 'a', 1})) == bytes({0, 'a', 0, 0, 0}));
} }
BOOST_AUTO_TEST_CASE(constructor_arguments)
{
char const* sourceCode = R"(
contract Helper {
string3 name;
bool flag;
function Helper(string3 x, bool f) {
name = x;
flag = f;
}
function getName() returns (string3 ret) { return name; }
function getFlag() returns (bool ret) { return flag; }
}
contract Main {
Helper h;
function Main() {
h = new Helper("abc", true);
}
function getFlag() returns (bool ret) { return h.getFlag(); }
function getName() returns (string3 ret) { return h.getName(); }
})";
compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction(0) == bytes({0x01}));
BOOST_REQUIRE(callContractFunction(1) == bytes({'a', 'b', 'c'}));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

9
test/solidityNameAndTypeResolution.cpp

@ -284,6 +284,15 @@ BOOST_AUTO_TEST_CASE(assignment_to_struct)
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
} }
BOOST_AUTO_TEST_CASE(returns_in_constructor)
{
char const* text = "contract test {\n"
" function test() returns (uint a) {\n"
" }\n"
"}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

Loading…
Cancel
Save