Browse Source

Merge pull request #537 from chriseth/sol_blockchainAccess

Added "this" and address balance.
cl-refactor
chriseth 10 years ago
parent
commit
ad3aa732b5
  1. 63
      libsolidity/AST.cpp
  2. 43
      libsolidity/AST.h
  3. 1
      libsolidity/ASTForward.h
  4. 15
      libsolidity/Compiler.cpp
  5. 5
      libsolidity/Compiler.h
  6. 5
      libsolidity/CompilerContext.cpp
  7. 4
      libsolidity/CompilerContext.h
  8. 7
      libsolidity/CompilerStack.cpp
  9. 9
      libsolidity/CompilerStack.h
  10. 100
      libsolidity/ExpressionCompiler.cpp
  11. 13
      libsolidity/ExpressionCompiler.h
  12. 82
      libsolidity/GlobalContext.cpp
  13. 62
      libsolidity/GlobalContext.h
  14. 43
      libsolidity/NameAndTypeResolver.cpp
  15. 3
      libsolidity/NameAndTypeResolver.h
  16. 1
      libsolidity/Scanner.cpp
  17. 1
      libsolidity/Token.h
  18. 81
      libsolidity/Types.cpp
  19. 89
      libsolidity/Types.h
  20. 4
      test/solidityCompiler.cpp
  21. 57
      test/solidityEndToEndTest.cpp
  22. 2
      test/solidityExpressionCompiler.cpp
  23. 22
      test/solidityNameAndTypeResolution.cpp

63
libsolidity/AST.cpp

@ -51,6 +51,40 @@ void StructDefinition::accept(ASTVisitor& _visitor)
_visitor.endVisit(*this);
}
void StructDefinition::checkValidityOfMembers()
{
checkMemberTypes();
checkRecursion();
}
void StructDefinition::checkMemberTypes()
{
for (ASTPointer<VariableDeclaration> const& member: getMembers())
if (!member->getType()->canBeStored())
BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct."));
}
void StructDefinition::checkRecursion()
{
set<StructDefinition const*> definitionsSeen;
vector<StructDefinition const*> queue = {this};
while (!queue.empty())
{
StructDefinition const* def = queue.back();
queue.pop_back();
if (definitionsSeen.count(def))
BOOST_THROW_EXCEPTION(ParserError() << errinfo_sourceLocation(def->getLocation())
<< errinfo_comment("Recursive struct definition."));
definitionsSeen.insert(def);
for (ASTPointer<VariableDeclaration> const& member: def->getMembers())
if (member->getType()->getCategory() == Type::Category::STRUCT)
{
UserDefinedTypeName const& typeName = dynamic_cast<UserDefinedTypeName&>(*member->getTypeName());
queue.push_back(&dynamic_cast<StructDefinition const&>(*typeName.getReferencedDeclaration()));
}
}
}
void ParameterList::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
@ -258,7 +292,7 @@ void Literal::accept(ASTVisitor& _visitor)
_visitor.endVisit(*this);
}
TypeError ASTNode::createTypeError(string const& _description)
TypeError ASTNode::createTypeError(string const& _description) const
{
return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description);
}
@ -338,8 +372,6 @@ void VariableDefinition::checkTypeRequirements()
m_variable->setType(m_value->getType());
}
}
if (m_variable->getType() && !m_variable->getType()->canLiveOutsideStorage())
BOOST_THROW_EXCEPTION(m_variable->createTypeError("Type is required to live outside storage."));
}
void Assignment::checkTypeRequirements()
@ -432,8 +464,6 @@ void FunctionCall::checkTypeRequirements()
}
else
{
m_expression->requireLValue();
//@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
// function parameters
@ -461,29 +491,23 @@ bool FunctionCall::isTypeConversion() const
void MemberAccess::checkTypeRequirements()
{
m_expression->checkTypeRequirements();
m_expression->requireLValue();
if (m_expression->getType()->getCategory() != Type::Category::STRUCT)
BOOST_THROW_EXCEPTION(createTypeError("Member access to a non-struct (is " +
m_expression->getType()->toString() + ")"));
StructType const& type = dynamic_cast<StructType const&>(*m_expression->getType());
unsigned memberIndex = type.memberNameToIndex(*m_memberName);
if (memberIndex >= type.getMemberCount())
Type const& type = *m_expression->getType();
m_type = type.getMemberType(*m_memberName);
if (!m_type)
BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found in " + type.toString()));
m_type = type.getMemberByIndex(memberIndex).getType();
m_isLvalue = true;
m_isLvalue = (type.getCategory() == Type::Category::STRUCT && m_type->getCategory() != Type::Category::MAPPING);
}
void IndexAccess::checkTypeRequirements()
{
m_base->checkTypeRequirements();
m_base->requireLValue();
if (m_base->getType()->getCategory() != Type::Category::MAPPING)
BOOST_THROW_EXCEPTION(m_base->createTypeError("Indexed expression has to be a mapping (is " +
m_base->getType()->toString() + ")"));
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType());
m_index->expectType(*type.getKeyType());
m_type = type.getValueType();
m_isLvalue = true;
m_isLvalue = m_type->getCategory() != Type::Category::MAPPING;
}
void Identifier::checkTypeRequirements()
@ -515,7 +539,6 @@ void Identifier::checkTypeRequirements()
// Calling a function (e.g. function(12), otherContract.function(34)) does not do a type
// conversion.
m_type = make_shared<FunctionType>(*functionDef);
m_isLvalue = true;
return;
}
ContractDefinition* contractDef = dynamic_cast<ContractDefinition*>(m_referencedDeclaration);
@ -524,6 +547,12 @@ void Identifier::checkTypeRequirements()
m_type = make_shared<TypeType>(make_shared<ContractType>(*contractDef));
return;
}
MagicVariableDeclaration* magicVariable = dynamic_cast<MagicVariableDeclaration*>(m_referencedDeclaration);
if (magicVariable)
{
m_type = magicVariable->getType();
return;
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Declaration reference of unknown/forbidden type."));
}

43
libsolidity/AST.h

@ -66,7 +66,7 @@ public:
/// Creates a @ref TypeError exception and decorates it with the location of the node and
/// the given description
TypeError createTypeError(std::string const& _description);
TypeError createTypeError(std::string const& _description) const;
///@{
///@name equality operators
@ -122,6 +122,7 @@ public:
/// Returns the functions that make up the calling interface in the intended order.
std::vector<FunctionDefinition const*> getInterfaceFunctions() const;
private:
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables;
@ -139,7 +140,14 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& getMembers() const { return m_members; }
/// Checks that the members do not include any recursive structs and have valid types
/// (e.g. no functions).
void checkValidityOfMembers();
private:
void checkMemberTypes();
void checkRecursion();
std::vector<ASTPointer<VariableDeclaration>> m_members;
};
@ -188,6 +196,7 @@ public:
/// Checks that all parameters have allowed types and calls checkTypeRequirements on the body.
void checkTypeRequirements();
private:
bool m_isPublic;
ASTPointer<ParameterList> m_parameters;
@ -210,7 +219,6 @@ public:
Declaration(_location, _name), m_typeName(_type) {}
virtual void accept(ASTVisitor& _visitor) override;
bool isTypeGivenExplicitly() const { return bool(m_typeName); }
TypeName* getTypeName() const { return m_typeName.get(); }
/// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly
@ -224,6 +232,27 @@ private:
std::shared_ptr<Type const> m_type; ///< derived type, initially empty
};
/**
* Pseudo AST node that is used as declaration for "this", "msg", "tx" and "block" when the
* identifier is encountered. Will never have a valid location in the source code.
*/
class MagicVariableDeclaration: public Declaration
{
public:
enum class VariableKind { THIS, MSG, TX, BLOCK };
MagicVariableDeclaration(VariableKind _kind, ASTString const& _name, std::shared_ptr<Type const> const& _type):
Declaration(Location(), std::make_shared<ASTString>(_name)), m_kind(_kind), m_type(_type) {}
virtual void accept(ASTVisitor&) override { BOOST_THROW_EXCEPTION(InternalCompilerError()
<< errinfo_comment("MagicVariableDeclaration used inside real AST.")); }
std::shared_ptr<Type const> const& getType() const { return m_type; }
VariableKind getKind() const { return m_kind; }
private:
VariableKind m_kind;
std::shared_ptr<Type const> m_type;
};
/// Types
/// @{
@ -238,6 +267,7 @@ public:
/// Retrieve the element of the type hierarchy this node refers to. Can return an empty shared
/// pointer until the types have been resolved using the @ref NameAndTypeResolver.
/// If it returns an empty shared pointer after that, this indicates that the type was not found.
virtual std::shared_ptr<Type> toType() const = 0;
};
@ -263,8 +293,7 @@ private:
};
/**
* Name referring to a user-defined type (i.e. a struct).
* @todo some changes are necessary if this is also used to refer to contract types later
* Name referring to a user-defined type (i.e. a struct, contract, etc.).
*/
class UserDefinedTypeName: public TypeName
{
@ -275,13 +304,13 @@ public:
virtual std::shared_ptr<Type> toType() const override { return Type::fromUserDefinedTypeName(*this); }
ASTString const& getName() const { return *m_name; }
void setReferencedStruct(StructDefinition& _referencedStruct) { m_referencedStruct = &_referencedStruct; }
StructDefinition const* getReferencedStruct() const { return m_referencedStruct; }
void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; }
private:
ASTPointer<ASTString> m_name;
StructDefinition* m_referencedStruct;
Declaration* m_referencedDeclaration;
};
/**

1
libsolidity/ASTForward.h

@ -40,6 +40,7 @@ class StructDefinition;
class ParameterList;
class FunctionDefinition;
class VariableDeclaration;
class MagicVariableDeclaration;
class TypeName;
class ElementaryTypeName;
class UserDefinedTypeName;

15
libsolidity/Compiler.cpp

@ -32,17 +32,13 @@ using namespace std;
namespace dev {
namespace solidity {
bytes Compiler::compile(ContractDefinition& _contract, bool _optimize)
{
Compiler compiler;
compiler.compileContract(_contract);
return compiler.m_context.getAssembledBytecode(_optimize);
}
void Compiler::compileContract(ContractDefinition& _contract)
void Compiler::compileContract(ContractDefinition& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals)
{
m_context = CompilerContext(); // clear it just in case
for (MagicVariableDeclaration const* variable: _magicGlobals)
m_context.addMagicGlobal(*variable);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
if (function->getName() != _contract.getName()) // don't add the constructor here
m_context.addFunction(*function);
@ -328,7 +324,8 @@ bool Compiler::visit(ExpressionStatement& _expressionStatement)
{
Expression& expression = _expressionStatement.getExpression();
ExpressionCompiler::compileExpression(m_context, expression);
if (expression.getType()->getCategory() != Type::Category::VOID)
Type::Category category = expression.getType()->getCategory();
if (category != Type::Category::VOID && category != Type::Category::MAGIC)
m_context << eth::Instruction::POP;
return false;
}

5
libsolidity/Compiler.h

@ -32,13 +32,10 @@ class Compiler: private ASTVisitor
public:
Compiler(): m_returnTag(m_context.newTag()) {}
void compileContract(ContractDefinition& _contract);
void compileContract(ContractDefinition& _contract, std::vector<MagicVariableDeclaration const*> const& _magicGlobals);
bytes getAssembledBytecode(bool _optimize = false) { return m_context.getAssembledBytecode(_optimize); }
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
/// Compile the given contract and return the EVM bytecode.
static bytes compile(ContractDefinition& _contract, bool _optimize);
private:
/// Creates a new compiler context / assembly, packs the current code into the data part and
/// adds the constructor code.

5
libsolidity/CompilerContext.cpp

@ -30,6 +30,11 @@ using namespace std;
namespace dev {
namespace solidity {
void CompilerContext::addMagicGlobal(MagicVariableDeclaration const& _declaration)
{
m_magicGlobals.insert(&_declaration);
}
void CompilerContext::addStateVariable(VariableDeclaration const& _declaration)
{
m_stateVariables[&_declaration] = m_stateVariablesSize;

4
libsolidity/CompilerContext.h

@ -40,6 +40,7 @@ class CompilerContext
public:
CompilerContext(): m_stateVariablesSize(0) {}
void addMagicGlobal(MagicVariableDeclaration const& _declaration);
void addStateVariable(VariableDeclaration const& _declaration);
void startNewFunction() { m_localVariables.clear(); m_asm.setDeposit(0); }
void initializeLocalVariables(unsigned _numVariables);
@ -48,6 +49,7 @@ public:
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration); }
bool isFunctionDefinition(Declaration const* _declaration) const { return m_functionEntryLabels.count(_declaration); }
bool isLocalVariable(Declaration const* _declaration) const;
bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration); }
@ -90,6 +92,8 @@ public:
private:
eth::Assembly m_asm;
/// Magic global variables like msg, tx or this, distinguished by type.
std::set<Declaration const*> m_magicGlobals;
/// Size of the state variables, offset of next variable to be added.
u256 m_stateVariablesSize;
/// Storage offsets of state variables

7
libsolidity/CompilerStack.cpp

@ -23,6 +23,7 @@
#include <libsolidity/AST.h>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
#include <libsolidity/GlobalContext.h>
#include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/Compiler.h>
#include <libsolidity/CompilerStack.h>
@ -45,7 +46,9 @@ void CompilerStack::parse()
if (!m_scanner)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source not available."));
m_contractASTNode = Parser().parse(m_scanner);
NameAndTypeResolver().resolveNamesAndTypes(*m_contractASTNode);
m_globalContext = make_shared<GlobalContext>();
m_globalContext->setCurrentContract(*m_contractASTNode);
NameAndTypeResolver(m_globalContext->getDeclarations()).resolveNamesAndTypes(*m_contractASTNode);
m_parseSuccessful = true;
}
@ -61,7 +64,7 @@ bytes const& CompilerStack::compile(bool _optimize)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
m_bytecode.clear();
m_compiler = make_shared<Compiler>();
m_compiler->compileContract(*m_contractASTNode);
m_compiler->compileContract(*m_contractASTNode, m_globalContext->getMagicVariables());
return m_bytecode = m_compiler->getAssembledBytecode(_optimize);
}

9
libsolidity/CompilerStack.h

@ -30,9 +30,11 @@
namespace dev {
namespace solidity {
class Scanner; // forward
class ContractDefinition; // forward
class Compiler; // forward
// forward declarations
class Scanner;
class ContractDefinition;
class Compiler;
class GlobalContext;
/**
* Easy to use and self-contained Solidity compiler with as few header dependencies as possible.
@ -71,6 +73,7 @@ public:
private:
std::shared_ptr<Scanner> m_scanner;
std::shared_ptr<GlobalContext> m_globalContext;
std::shared_ptr<ContractDefinition> m_contractASTNode;
bool m_parseSuccessful;
std::string m_interface;

100
libsolidity/ExpressionCompiler.cpp

@ -162,15 +162,25 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
{
if (_functionCall.isTypeConversion())
{
//@todo we only have integers and bools for now which cannot be explicitly converted
//@todo struct construction
if (asserts(_functionCall.getArguments().size() == 1))
BOOST_THROW_EXCEPTION(InternalCompilerError());
Expression& firstArgument = *_functionCall.getArguments().front();
firstArgument.accept(*this);
appendTypeConversion(*firstArgument.getType(), *_functionCall.getType());
if (firstArgument.getType()->getCategory() == Type::Category::CONTRACT &&
_functionCall.getType()->getCategory() == Type::Category::INTEGER)
{
// explicit type conversion contract -> address, nothing to do.
}
else
{
appendTypeConversion(*firstArgument.getType(), *_functionCall.getType());
}
}
else
{
//@todo: check for "external call" (to be stored in type)
// Calling convention: Caller pushes return address and arguments
// Callee removes them and pushes return values
FunctionDefinition const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType()).getFunction();
@ -185,9 +195,6 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
appendTypeConversion(*arguments[i]->getType(), *function.getParameters()[i]->getType());
}
_functionCall.getExpression().accept(*this);
if (asserts(m_currentLValue.isInCode()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Code reference expected."));
m_currentLValue.reset();
m_context.appendJump();
m_context << returnLabel;
@ -205,22 +212,36 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
{
if (asserts(m_currentLValue.isInStorage()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to a non-storage value."));
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
unsigned memberIndex = type.memberNameToIndex(_memberAccess.getMemberName());
if (asserts(memberIndex <= type.getMemberCount()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member not found in struct during compilation."));
m_context << type.getStorageOffsetOfMember(memberIndex) << eth::Instruction::ADD;
m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
switch (_memberAccess.getExpression().getType()->getCategory())
{
case Type::Category::INTEGER:
if (asserts(_memberAccess.getMemberName() == "balance"))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
m_context << eth::Instruction::BALANCE;
break;
case Type::Category::CONTRACT:
// call function
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Contract variables not yet implemented."));
break;
case Type::Category::MAGIC:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Magic variables not yet implemented."));
break;
case Type::Category::STRUCT:
{
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
m_context << type.getStorageOffsetOfMember(_memberAccess.getMemberName()) << eth::Instruction::ADD;
m_currentLValue = LValue(m_context, LValue::STORAGE);
m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
break;
}
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to unknown type."));
}
}
bool ExpressionCompiler::visit(IndexAccess& _indexAccess)
{
m_currentLValue.reset();
_indexAccess.getBaseExpression().accept(*this);
if (asserts(m_currentLValue.isInStorage()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Index access to a non-storage value."));
_indexAccess.getIndexExpression().accept(*this);
appendTypeConversion(*_indexAccess.getIndexExpression().getType(),
*dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType(),
@ -237,8 +258,25 @@ bool ExpressionCompiler::visit(IndexAccess& _indexAccess)
void ExpressionCompiler::endVisit(Identifier& _identifier)
{
m_currentLValue.fromDeclaration(_identifier, *_identifier.getReferencedDeclaration());
m_currentLValue.retrieveValueIfLValueNotRequested(_identifier);
Declaration* declaration = _identifier.getReferencedDeclaration();
if (MagicVariableDeclaration* magicVar = dynamic_cast<MagicVariableDeclaration*>(declaration))
{
if (magicVar->getKind() == MagicVariableDeclaration::VariableKind::THIS)
m_context << eth::Instruction::ADDRESS;
return;
}
if (FunctionDefinition* functionDef = dynamic_cast<FunctionDefinition*>(declaration))
{
m_context << m_context.getFunctionEntryLabel(*functionDef).pushTag();
return;
}
if (VariableDeclaration* varDef = dynamic_cast<VariableDeclaration*>(declaration))
{
m_currentLValue.fromIdentifier(_identifier, *_identifier.getReferencedDeclaration());
m_currentLValue.retrieveValueIfLValueNotRequested(_identifier);
return;
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context."));
}
void ExpressionCompiler::endVisit(Literal& _literal)
@ -405,9 +443,6 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
{
switch (m_type)
{
case CODE:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Tried to retrieve value of a function."));
break;
case STACK:
{
unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset));
@ -418,11 +453,15 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
break;
}
case STORAGE:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
if (!_remove)
*m_context << eth::Instruction::DUP1;
*m_context << eth::Instruction::SLOAD;
break;
case MEMORY:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Location type not yet implemented."));
break;
@ -450,15 +489,15 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
break;
}
case LValue::STORAGE:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
if (!_move)
*m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1;
*m_context << eth::Instruction::SSTORE;
break;
case LValue::CODE:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Location type does not support assignment."));
break;
case LValue::MEMORY:
if (!_expression.getType()->isValueType())
break; // no distinction between value and reference for non-value types
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
<< errinfo_comment("Location type not yet implemented."));
break;
@ -469,7 +508,7 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
}
}
void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(const Expression& _expression)
void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression const& _expression)
{
if (!_expression.lvalueRequested())
{
@ -478,7 +517,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(const Express
}
}
void ExpressionCompiler::LValue::fromDeclaration( Expression const& _expression, Declaration const& _declaration)
void ExpressionCompiler::LValue::fromIdentifier(Identifier const& _identifier, Declaration const& _declaration)
{
if (m_context->isLocalVariable(&_declaration))
{
@ -490,13 +529,8 @@ void ExpressionCompiler::LValue::fromDeclaration( Expression const& _expression,
m_type = STORAGE;
*m_context << m_context->getStorageLocationOfVariable(_declaration);
}
else if (m_context->isFunctionDefinition(&_declaration))
{
m_type = CODE;
*m_context << m_context->getFunctionEntryLabel(dynamic_cast<FunctionDefinition const&>(_declaration)).pushTag();
}
else
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_identifier.getLocation())
<< errinfo_comment("Identifier type not supported or identifier not found."));
}

13
libsolidity/ExpressionCompiler.h

@ -83,31 +83,30 @@ private:
/**
* Helper class to store and retrieve lvalues to and from various locations.
* All types except STACK store a reference in a slot on the stack, STACK just stores the
* base stack offset of the variable in @a m_baseStackOffset.
* All types except STACK store a reference in a slot on the stack, STACK just
* stores the base stack offset of the variable in @a m_baseStackOffset.
*/
class LValue
{
public:
enum LValueType { NONE, CODE, STACK, MEMORY, STORAGE };
enum LValueType { NONE, STACK, MEMORY, STORAGE };
explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); }
LValue(CompilerContext& _compilerContext, LValueType _type, unsigned _baseStackOffset = 0):
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset) {}
/// Set type according to the declaration and retrieve the reference.
/// @a _expression is the current expression, used for error reporting.
void fromDeclaration(Expression const& _expression, Declaration const& _declaration);
/// @a _expression is the current expression
void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration);
void reset() { m_type = NONE; m_baseStackOffset = 0; }
bool isValid() const { return m_type != NONE; }
bool isInCode() const { return m_type == CODE; }
bool isInOnStack() const { return m_type == STACK; }
bool isInMemory() const { return m_type == MEMORY; }
bool isInStorage() const { return m_type == STORAGE; }
/// @returns true if this lvalue reference type occupies a slot on the stack.
bool storesReferenceOnStack() const { return m_type == STORAGE || m_type == MEMORY || m_type == CODE; }
bool storesReferenceOnStack() const { return m_type == STORAGE || m_type == MEMORY; }
/// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true,
/// also removes the reference from the stack (note that is does not reset the type to @a NONE).

82
libsolidity/GlobalContext.cpp

@ -0,0 +1,82 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Container of the (implicit and explicit) global objects.
*/
#include <memory>
#include <libsolidity/GlobalContext.h>
#include <libsolidity/AST.h>
#include <libsolidity/Types.h>
using namespace std;
namespace dev
{
namespace solidity
{
GlobalContext::GlobalContext()
{
// CurrentContract this; // @todo type depends on context -> switch prior to entering contract
// Message msg;
// Transaction tx;
// Block block;
//@todo type will be a custom complex type, maybe the same type class for msg tx and block.
//addVariable("msg", );
}
void GlobalContext::setCurrentContract(ContractDefinition const& _contract)
{
m_currentContract = &_contract;
}
vector<Declaration*> GlobalContext::getDeclarations() const
{
vector<Declaration*> declarations;
declarations.reserve(m_magicVariables.size() + 1);
for (ASTPointer<Declaration> const& variable: m_magicVariables)
declarations.push_back(variable.get());
declarations.push_back(getCurrentThis());
return declarations;
}
MagicVariableDeclaration*GlobalContext::getCurrentThis() const
{
if (!m_thisPointer[m_currentContract])
m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(
MagicVariableDeclaration::VariableKind::THIS,
"this", make_shared<ContractType>(*m_currentContract));
return m_thisPointer[m_currentContract].get();
}
vector<MagicVariableDeclaration const*> GlobalContext::getMagicVariables() const
{
vector<MagicVariableDeclaration const*> declarations;
declarations.reserve(m_magicVariables.size() + 1);
for (ASTPointer<MagicVariableDeclaration const> const& variable: m_magicVariables)
declarations.push_back(variable.get());
declarations.push_back(getCurrentThis());
return declarations;
}
}
}

62
libsolidity/GlobalContext.h

@ -0,0 +1,62 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Container of the (implicit and explicit) global objects.
*/
#pragma once
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <boost/noncopyable.hpp>
#include <libsolidity/ASTForward.h>
namespace dev
{
namespace solidity
{
class Type; // forward
/**
* Container for all global objects which look like AST nodes, but are not part of the AST
* that is currently being compiled.
* @note must not be destroyed or moved during compilation as its objects can be referenced from
* other objects.
*/
class GlobalContext: private boost::noncopyable
{
public:
GlobalContext();
void setCurrentContract(ContractDefinition const& _contract);
std::vector<MagicVariableDeclaration const*> getMagicVariables() const;
std::vector<Declaration*> getDeclarations() const;
private:
MagicVariableDeclaration* getCurrentThis() const;
std::vector<std::shared_ptr<MagicVariableDeclaration>> m_magicVariables;
ContractDefinition const* m_currentContract;
std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration>> mutable m_thisPointer;
};
}
}

43
libsolidity/NameAndTypeResolver.cpp

@ -32,15 +32,20 @@ namespace solidity
{
NameAndTypeResolver::NameAndTypeResolver(std::vector<Declaration*> const& _globals)
{
for (Declaration* declaration: _globals)
m_scopes[nullptr].registerDeclaration(*declaration);
}
void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
{
reset();
DeclarationRegistrationHelper registrar(m_scopes, _contract);
m_currentScope = &m_scopes[&_contract];
for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs())
ReferencesResolver resolver(*structDef, *this, nullptr);
for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs())
checkForRecursion(*structDef);
structDef->checkValidityOfMembers();
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
ReferencesResolver resolver(*variable, *this, nullptr);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
@ -73,30 +78,6 @@ Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name
return m_currentScope->resolveName(_name, _recursive);
}
void NameAndTypeResolver::checkForRecursion(StructDefinition const& _struct)
{
set<StructDefinition const*> definitionsSeen;
vector<StructDefinition const*> queue = {&_struct};
while (!queue.empty())
{
StructDefinition const* def = queue.back();
queue.pop_back();
if (definitionsSeen.count(def))
BOOST_THROW_EXCEPTION(ParserError() << errinfo_sourceLocation(def->getLocation())
<< errinfo_comment("Recursive struct definition."));
definitionsSeen.insert(def);
for (ASTPointer<VariableDeclaration> const& member: def->getMembers())
if (member->getType()->getCategory() == Type::Category::STRUCT)
queue.push_back(dynamic_cast<UserDefinedTypeName&>(*member->getTypeName()).getReferencedStruct());
}
}
void NameAndTypeResolver::reset()
{
m_scopes.clear();
m_currentScope = nullptr;
}
DeclarationRegistrationHelper::DeclarationRegistrationHelper(map<ASTNode const*, Scope>& _scopes,
ASTNode& _astRoot):
m_scopes(_scopes), m_currentScope(&m_scopes[nullptr])
@ -195,7 +176,11 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
// endVisit because the internal type needs resolving if it is a user defined type
// or mapping
if (_variable.getTypeName())
{
_variable.setType(_variable.getTypeName()->toType());
if (!_variable.getType())
BOOST_THROW_EXCEPTION(_variable.getTypeName()->createTypeError("Invalid type name"));
}
else if (!m_allowLazyTypes)
BOOST_THROW_EXCEPTION(_variable.createTypeError("Explicit type needed."));
// otherwise we have a "var"-declaration whose type is resolved by the first assignment
@ -221,11 +206,7 @@ bool ReferencesResolver::visit(UserDefinedTypeName& _typeName)
if (!declaration)
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation())
<< errinfo_comment("Undeclared identifier."));
StructDefinition* referencedStruct = dynamic_cast<StructDefinition*>(declaration);
//@todo later, contracts are also valid types
if (!referencedStruct)
BOOST_THROW_EXCEPTION(_typeName.createTypeError("Identifier does not name a type name."));
_typeName.setReferencedStruct(*referencedStruct);
_typeName.setReferencedDeclaration(*declaration);
return false;
}

3
libsolidity/NameAndTypeResolver.h

@ -41,8 +41,7 @@ namespace solidity
class NameAndTypeResolver: private boost::noncopyable
{
public:
NameAndTypeResolver() {}
explicit NameAndTypeResolver(std::vector<Declaration*> const& _globals);
void resolveNamesAndTypes(ContractDefinition& _contract);
/// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted,

1
libsolidity/Scanner.cpp

@ -683,7 +683,6 @@ Token::Value Scanner::scanNumber(char _charSeen)
KEYWORD("switch", Token::SWITCH) \
KEYWORD_GROUP('t') \
KEYWORD("text", Token::TEXT) \
KEYWORD("this", Token::THIS) \
KEYWORD("true", Token::TRUE_LITERAL) \
KEYWORD_GROUP('u') \
KEYWORD("uint", Token::UINT) \

1
libsolidity/Token.h

@ -160,7 +160,6 @@ namespace solidity
K(RETURNS, "returns", 0) \
K(STRUCT, "struct", 0) \
K(SWITCH, "switch", 0) \
K(THIS, "this", 0) \
K(VAR, "var", 0) \
K(WHILE, "while", 0) \
\

81
libsolidity/Types.cpp

@ -60,13 +60,24 @@ shared_ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken)
shared_ptr<Type> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
{
return make_shared<StructType>(*_typeName.getReferencedStruct());
Declaration const* declaration = _typeName.getReferencedDeclaration();
if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
return make_shared<StructType>(*structDef);
else if (FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(declaration))
return make_shared<FunctionType>(*function);
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
return make_shared<ContractType>(*contract);
return shared_ptr<Type>();
}
shared_ptr<Type> Type::fromMapping(Mapping const& _typeName)
{
shared_ptr<Type const> keyType = _typeName.getKeyType().toType();
if (!keyType)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name."));
shared_ptr<Type const> valueType = _typeName.getValueType().toType();
if (!valueType)
BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name"));
return make_shared<MappingType>(keyType, valueType);
}
@ -86,6 +97,8 @@ shared_ptr<Type> Type::forLiteral(Literal const& _literal)
}
}
const MemberList Type::EmptyMemberList = MemberList();
shared_ptr<IntegerType> IntegerType::smallestTypeForLiteral(string const& _literal)
{
bigint value(_literal);
@ -176,6 +189,8 @@ u256 IntegerType::literalValue(Literal const& _literal) const
return u256(value);
}
const MemberList IntegerType::AddressMemberList = MemberList({{"balance", std::make_shared<IntegerType const>(256)}});
bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
// conversion to integer is fine, but not to address
@ -199,6 +214,15 @@ u256 BoolType::literalValue(Literal const& _literal) const
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal."));
}
bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (isImplicitlyConvertibleTo(_convertTo))
return true;
if (_convertTo.getCategory() == Category::INTEGER)
return dynamic_cast<IntegerType const&>(_convertTo).isAddress();
return false;
}
bool ContractType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
@ -215,6 +239,11 @@ u256 ContractType::getStorageSize() const
return max<u256>(1, size);
}
string ContractType::toString() const
{
return "contract " + m_contract.getName();
}
bool StructType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
@ -226,15 +255,15 @@ bool StructType::operator==(Type const& _other) const
u256 StructType::getStorageSize() const
{
u256 size = 0;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
size += variable->getType()->getStorageSize();
for (pair<string, shared_ptr<Type const>> const& member: getMembers())
size += member.second->getStorageSize();
return max<u256>(1, size);
}
bool StructType::canLiveOutsideStorage() const
{
for (unsigned i = 0; i < getMemberCount(); ++i)
if (!getMemberByIndex(i).getType()->canLiveOutsideStorage())
for (pair<string, shared_ptr<Type const>> const& member: getMembers())
if (!member.second->canLiveOutsideStorage())
return false;
return true;
}
@ -244,33 +273,30 @@ string StructType::toString() const
return string("struct ") + m_struct.getName();
}
unsigned StructType::getMemberCount() const
MemberList const& StructType::getMembers() const
{
return m_struct.getMembers().size();
}
unsigned StructType::memberNameToIndex(string const& _name) const
{
vector<ASTPointer<VariableDeclaration>> const& members = m_struct.getMembers();
for (unsigned index = 0; index < members.size(); ++index)
if (members[index]->getName() == _name)
return index;
return unsigned(-1);
}
VariableDeclaration const& StructType::getMemberByIndex(unsigned _index) const
{
return *m_struct.getMembers()[_index];
// We need to lazy-initialize it because of recursive references.
if (!m_members)
{
map<string, shared_ptr<Type const>> members;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
members[variable->getName()] = variable->getType();
m_members.reset(new MemberList(members));
}
return *m_members;
}
u256 StructType::getStorageOffsetOfMember(unsigned _index) const
u256 StructType::getStorageOffsetOfMember(string const& _name) const
{
//@todo cache member offset?
u256 offset;
// vector<ASTPointer<VariableDeclaration>> const& members = m_struct.getMembers();
for (unsigned index = 0; index < _index; ++index)
offset += getMemberByIndex(index).getType()->getStorageSize();
return offset;
for (ASTPointer<VariableDeclaration> variable: m_struct.getMembers())
{
offset += variable->getType()->getStorageSize();
if (variable->getName() == _name)
return offset;
}
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested."));
}
bool FunctionType::operator==(Type const& _other) const
@ -283,8 +309,7 @@ bool FunctionType::operator==(Type const& _other) const
string FunctionType::toString() const
{
//@todo nice string for function types
return "function(...)returns(...)";
return "function " + m_function.getName();
}
bool MappingType::operator==(Type const& _other) const

89
libsolidity/Types.h

@ -24,6 +24,7 @@
#include <memory>
#include <string>
#include <map>
#include <boost/noncopyable.hpp>
#include <libdevcore/Common.h>
#include <libsolidity/Exceptions.h>
@ -37,6 +38,33 @@ namespace solidity
// @todo realMxN, string<N>
class Type; // forward
/**
* List of members of a type.
*/
class MemberList
{
public:
using TypePointer = std::shared_ptr<Type const>;
using MemberMap = std::map<std::string, TypePointer>;
MemberList() {}
explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {}
TypePointer getMemberType(std::string const& _name) const
{
auto it = m_memberTypes.find(_name);
return it != m_memberTypes.end() ? it->second : std::shared_ptr<Type const>();
}
MemberMap::const_iterator begin() const { return m_memberTypes.begin(); }
MemberMap::const_iterator end() const { return m_memberTypes.end(); }
private:
MemberMap m_memberTypes;
};
/**
* Abstract base class that forms the root of the type hierarchy.
*/
@ -45,7 +73,7 @@ class Type: private boost::noncopyable
public:
enum class Category
{
INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE
INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE, MAGIC
};
///@{
@ -78,8 +106,18 @@ public:
/// @returns number of bytes required to hold this value in storage.
/// For dynamically "allocated" types, it returns the size of the statically allocated head,
virtual u256 getStorageSize() const { return 1; }
/// Returns true if the type can be stored in storage.
virtual bool canBeStored() const { return true; }
/// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping.
virtual bool canLiveOutsideStorage() const { return true; }
/// Returns true if the type can be stored as a value (as opposed to a reference) on the stack,
/// i.e. it behaves differently in lvalue context and in value context.
virtual bool isValueType() const { return false; }
/// Returns the list of all members of this type. Default implementation: no members.
virtual MemberList const& getMembers() const { return EmptyMemberList; }
/// Convenience method, returns the type of the given named member or an empty pointer if no such member exists.
std::shared_ptr<Type const> getMemberType(std::string const& _name) const { return getMembers().getMemberType(_name); }
virtual std::string toString() const = 0;
virtual u256 literalValue(Literal const&) const
@ -87,6 +125,10 @@ public:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Literal value requested "
"for type without literals."));
}
protected:
/// Convenience object used when returning an empty member list.
static const MemberList EmptyMemberList;
};
/**
@ -114,7 +156,10 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual unsigned getCalldataEncodedSize() const { return m_bits / 8; }
virtual unsigned getCalldataEncodedSize() const override { return m_bits / 8; }
virtual bool isValueType() const override { return true; }
virtual MemberList const& getMembers() const { return isAddress() ? AddressMemberList : EmptyMemberList; }
virtual std::string toString() const override;
virtual u256 literalValue(Literal const& _literal) const override;
@ -127,6 +172,7 @@ public:
private:
int m_bits;
Modifier m_modifier;
static const MemberList AddressMemberList;
};
/**
@ -147,6 +193,7 @@ public:
}
virtual unsigned getCalldataEncodedSize() const { return 1; }
virtual bool isValueType() const override { return true; }
virtual std::string toString() const override { return "bool"; }
virtual u256 literalValue(Literal const& _literal) const override;
@ -160,10 +207,11 @@ class ContractType: public Type
public:
virtual Category getCategory() const override { return Category::CONTRACT; }
ContractType(ContractDefinition const& _contract): m_contract(_contract) {}
/// Contracts can be converted to themselves and to addresses.
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const;
virtual std::string toString() const override { return "contract{...}"; }
virtual u256 getStorageSize() const override;
virtual std::string toString() const override;
private:
ContractDefinition const& m_contract;
@ -183,18 +231,18 @@ public:
}
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const;
virtual bool canLiveOutsideStorage() const;
virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override;
virtual std::string toString() const override;
unsigned getMemberCount() const;
/// Returns the index of the member with name @a _name or unsigned(-1) if it does not exist.
unsigned memberNameToIndex(std::string const& _name) const;
VariableDeclaration const& getMemberByIndex(unsigned _index) const;
u256 getStorageOffsetOfMember(unsigned _index) const;
virtual MemberList const& getMembers() const override;
u256 getStorageOffsetOfMember(std::string const& _name) const;
private:
StructDefinition const& m_struct;
/// List of member types, will be lazy-initialized because of recursive references.
mutable std::unique_ptr<MemberList> m_members;
};
/**
@ -210,8 +258,9 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override;
virtual u256 getStorageSize() const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); }
virtual bool canLiveOutsideStorage() const { return false; }
virtual bool canBeStored() const override { return false; }
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; }
private:
FunctionDefinition const& m_function;
@ -229,7 +278,7 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual std::string toString() const override;
virtual bool canLiveOutsideStorage() const { return false; }
virtual bool canLiveOutsideStorage() const override { return false; }
std::shared_ptr<Type const> getKeyType() const { return m_keyType; }
std::shared_ptr<Type const> getValueType() const { return m_valueType; }
@ -250,8 +299,9 @@ public:
VoidType() {}
virtual std::string toString() const override { return "void"; }
virtual u256 getStorageSize() const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); }
virtual bool canLiveOutsideStorage() const { return false; }
virtual bool canBeStored() const override { return false; }
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); }
virtual bool canLiveOutsideStorage() const override { return false; }
};
/**
@ -267,8 +317,9 @@ public:
std::shared_ptr<Type const> const& getActualType() const { return m_actualType; }
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); }
virtual bool canLiveOutsideStorage() const { return false; }
virtual bool canBeStored() const override { return false; }
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); }
virtual bool canLiveOutsideStorage() const override { return false; }
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
private:

4
test/solidityCompiler.cpp

@ -48,11 +48,11 @@ bytes compileContract(const string& _sourceCode)
Parser parser;
ASTPointer<ContractDefinition> contract;
BOOST_REQUIRE_NO_THROW(contract = parser.parse(make_shared<Scanner>(CharStream(_sourceCode))));
NameAndTypeResolver resolver;
NameAndTypeResolver resolver({});
BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract));
Compiler compiler;
compiler.compileContract(*contract);
compiler.compileContract(*contract, {});
// debug
//compiler.streamAssembly(cout);
return compiler.getAssembledBytecode();

57
test/solidityEndToEndTest.cpp

@ -45,17 +45,17 @@ class ExecutionFramework
public:
ExecutionFramework() { g_logVerbosity = 0; }
bytes const& compileAndRun(string const& _sourceCode)
bytes const& compileAndRun(string const& _sourceCode, u256 const& _value = 0)
{
bytes code = dev::solidity::CompilerStack::staticCompile(_sourceCode);
sendMessage(code, true);
sendMessage(code, true, _value);
BOOST_REQUIRE(!m_output.empty());
return m_output;
}
bytes const& callContractFunction(byte _index, bytes const& _data = bytes())
bytes const& callContractFunction(byte _index, bytes const& _data = bytes(), u256 const& _value = 0)
{
sendMessage(bytes(1, _index) + _data, false);
sendMessage(bytes(1, _index) + _data, false, _value);
return m_output;
}
@ -111,11 +111,11 @@ private:
return toBigEndian(_cppFunction(_arguments...));
}
void sendMessage(bytes const& _data, bool _isCreation)
void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0)
{
eth::Executive executive(m_state);
eth::Transaction t = _isCreation ? eth::Transaction(0, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec())
: eth::Transaction(0, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec());
eth::Transaction t = _isCreation ? eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec())
: eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec());
bytes transactionRLP = t.rlp();
try
{
@ -125,7 +125,7 @@ private:
catch (...) {}
if (_isCreation)
{
BOOST_REQUIRE(!executive.create(Address(), 0, m_gasPrice, m_gas, &_data, Address()));
BOOST_REQUIRE(!executive.create(Address(), _value, m_gasPrice, m_gas, &_data, Address()));
m_contractAddress = executive.newAddress();
BOOST_REQUIRE(m_contractAddress);
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress));
@ -133,7 +133,7 @@ private:
else
{
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress));
BOOST_REQUIRE(!executive.call(m_contractAddress, Address(), 0, m_gasPrice, &_data, m_gas, Address()));
BOOST_REQUIRE(!executive.call(m_contractAddress, Address(), _value, m_gasPrice, &_data, m_gas, Address()));
}
BOOST_REQUIRE(executive.go());
executive.finalize();
@ -700,6 +700,34 @@ BOOST_AUTO_TEST_CASE(structs)
BOOST_CHECK(callContractFunction(0) == bytes({0x01}));
}
BOOST_AUTO_TEST_CASE(struct_reference)
{
char const* sourceCode = "contract test {\n"
" struct s2 {\n"
" uint32 z;\n"
" mapping(uint8 => s2) recursive;\n"
" }\n"
" s2 data;\n"
" function check() returns (bool ok) {\n"
" return data.z == 2 && \n"
" data.recursive[0].z == 3 && \n"
" data.recursive[0].recursive[1].z == 0 && \n"
" data.recursive[0].recursive[0].z == 1;\n"
" }\n"
" function set() {\n"
" data.z = 2;\n"
" var map = data.recursive;\n"
" s2 inner = map[0];\n"
" inner.z = 3;\n"
" inner.recursive[0].z = inner.recursive[1].z + 1;\n"
" }\n"
"}\n";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction(0) == bytes({0x00}));
BOOST_CHECK(callContractFunction(1) == bytes());
BOOST_CHECK(callContractFunction(0) == bytes({0x01}));
}
BOOST_AUTO_TEST_CASE(constructor)
{
char const* sourceCode = "contract test {\n"
@ -722,6 +750,17 @@ BOOST_AUTO_TEST_CASE(constructor)
testSolidityAgainstCpp(0, get, u256(7));
}
BOOST_AUTO_TEST_CASE(balance)
{
char const* sourceCode = "contract test {\n"
" function getBalance() returns (uint256 balance) {\n"
" return address(this).balance;\n"
" }\n"
"}\n";
compileAndRun(sourceCode, 23);
BOOST_CHECK(callContractFunction(0) == toBigEndian(u256(23)));
}
BOOST_AUTO_TEST_SUITE_END()
}

2
test/solidityExpressionCompiler.cpp

@ -88,7 +88,7 @@ bytes compileFirstExpression(const string& _sourceCode, vector<vector<string>> _
Parser parser;
ASTPointer<ContractDefinition> contract;
BOOST_REQUIRE_NO_THROW(contract = parser.parse(make_shared<Scanner>(CharStream(_sourceCode))));
NameAndTypeResolver resolver;
NameAndTypeResolver resolver({});
BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract));
FirstExpressionExtractor extractor(*contract);
BOOST_REQUIRE(extractor.getExpression() != nullptr);

22
test/solidityNameAndTypeResolution.cpp

@ -43,7 +43,7 @@ void parseTextAndResolveNames(std::string const& _source)
Parser parser;
ASTPointer<ContractDefinition> contract = parser.parse(
std::make_shared<Scanner>(CharStream(_source)));
NameAndTypeResolver resolver;
NameAndTypeResolver resolver({});
resolver.resolveNamesAndTypes(*contract);
}
}
@ -224,6 +224,26 @@ BOOST_AUTO_TEST_CASE(type_inference_explicit_conversion)
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(balance)
{
char const* text = "contract test {\n"
" function fun() {\n"
" uint256 x = address(0).balance;\n"
" }\n"
"}\n";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
}
BOOST_AUTO_TEST_CASE(balance_invalid)
{
char const* text = "contract test {\n"
" function fun() {\n"
" address(0).balance = 7;\n"
" }\n"
"}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_SUITE_END()
}

Loading…
Cancel
Save