From 27d20cd849d7404e69ee92588b35120265214eb6 Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 21 Nov 2014 19:14:56 +0100 Subject: [PATCH] Magic variables. --- libsolidity/AST.cpp | 17 +++--- libsolidity/AST.h | 22 +++++++ libsolidity/ASTForward.h | 1 + libsolidity/Compiler.cpp | 15 ++--- libsolidity/Compiler.h | 5 +- libsolidity/CompilerContext.cpp | 5 ++ libsolidity/CompilerContext.h | 4 ++ libsolidity/CompilerStack.cpp | 2 +- libsolidity/ExpressionCompiler.cpp | 85 +++++++++++++++++--------- libsolidity/ExpressionCompiler.h | 13 ++-- libsolidity/GlobalContext.cpp | 32 +++++++--- libsolidity/GlobalContext.h | 11 ++-- libsolidity/Types.cpp | 2 + libsolidity/Types.h | 10 ++- test/solidityCompiler.cpp | 2 +- test/solidityEndToEndTest.cpp | 2 +- test/solidityNameAndTypeResolution.cpp | 20 ++++++ test/solidityParser.cpp | 20 ------ 18 files changed, 172 insertions(+), 96 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 22a658d40..168a095c8 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -372,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() @@ -466,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 @@ -495,25 +491,23 @@ bool FunctionCall::isTypeConversion() const void MemberAccess::checkTypeRequirements() { m_expression->checkTypeRequirements(); - m_expression->requireLValue(); 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_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(*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() @@ -545,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(*functionDef); - m_isLvalue = true; return; } ContractDefinition* contractDef = dynamic_cast(m_referencedDeclaration); @@ -554,6 +547,12 @@ void Identifier::checkTypeRequirements() m_type = make_shared(make_shared(*contractDef)); return; } + MagicVariableDeclaration* magicVariable = dynamic_cast(m_referencedDeclaration); + if (magicVariable) + { + m_type = magicVariable->getType(); + return; + } BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Declaration reference of unknown/forbidden type.")); } diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 01e1b54f0..f8ff52749 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -230,6 +230,28 @@ private: std::shared_ptr 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 const& _type): + Declaration(Location(), std::make_shared(_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 const& getType() const { return m_type; } + VariableKind getKind() const { return m_kind; } + +private: + VariableKind m_kind; + std::shared_ptr m_type; +}; + /// Types /// @{ diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h index 2b0bd8869..a369c8a79 100644 --- a/libsolidity/ASTForward.h +++ b/libsolidity/ASTForward.h @@ -40,6 +40,7 @@ class StructDefinition; class ParameterList; class FunctionDefinition; class VariableDeclaration; +class MagicVariableDeclaration; class TypeName; class ElementaryTypeName; class UserDefinedTypeName; diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index a3865bc30..a82ecd95a 100644 --- a/libsolidity/Compiler.cpp +++ b/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 const& _magicGlobals) { m_context = CompilerContext(); // clear it just in case + for (MagicVariableDeclaration const* variable: _magicGlobals) + m_context.addMagicGlobal(*variable); + for (ASTPointer 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; } diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index 3887d3b5b..70e6c44f2 100644 --- a/libsolidity/Compiler.h +++ b/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 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. diff --git a/libsolidity/CompilerContext.cpp b/libsolidity/CompilerContext.cpp index 3c1acdfa7..b89a8e5b5 100644 --- a/libsolidity/CompilerContext.cpp +++ b/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; diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h index e624222dd..6a48e1485 100644 --- a/libsolidity/CompilerContext.h +++ b/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 m_magicGlobals; /// Size of the state variables, offset of next variable to be added. u256 m_stateVariablesSize; /// Storage offsets of state variables diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index f051d58fd..6535e00d4 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -64,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(); - m_compiler->compileContract(*m_contractASTNode); + m_compiler->compileContract(*m_contractASTNode, m_globalContext->getMagicVariables()); return m_bytecode = m_compiler->getAssembledBytecode(_optimize); } diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 4d175527a..b1a49457c 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -162,7 +162,7 @@ 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(); @@ -179,6 +179,8 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) } 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(*_functionCall.getExpression().getType()).getFunction(); @@ -193,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; @@ -213,19 +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(*_memberAccess.getExpression().getType()); - m_context << type.getStorageOffsetOfMember(_memberAccess.getMemberName()) << 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(*_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(*_indexAccess.getBaseExpression().getType()).getKeyType(), @@ -242,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(declaration)) + { + if (magicVar->getKind() == MagicVariableDeclaration::VariableKind::THIS) + m_context << eth::Instruction::ADDRESS; + return; + } + if (FunctionDefinition* functionDef = dynamic_cast(declaration)) + { + m_context << m_context.getFunctionEntryLabel(*functionDef).pushTag(); + return; + } + if (VariableDeclaration* varDef = dynamic_cast(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) @@ -410,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)); @@ -423,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; @@ -455,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; @@ -483,7 +517,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression co } } -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)) { @@ -495,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(_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.")); } diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index f52da29ec..3ed7848b5 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/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). diff --git a/libsolidity/GlobalContext.cpp b/libsolidity/GlobalContext.cpp index 6179722b2..f2e6d9ce9 100644 --- a/libsolidity/GlobalContext.cpp +++ b/libsolidity/GlobalContext.cpp @@ -45,25 +45,37 @@ GlobalContext::GlobalContext() void GlobalContext::setCurrentContract(ContractDefinition const& _contract) { - m_this = createVariable("this", make_shared(_contract)); + m_currentContract = &_contract; } vector GlobalContext::getDeclarations() const { vector declarations; - declarations.reserve(m_objects.size() + 1); - for (ASTPointer const& declaration: m_objects) - declarations.push_back(declaration.get()); - declarations.push_back(m_this.get()); + declarations.reserve(m_magicVariables.size() + 1); + for (ASTPointer const& variable: m_magicVariables) + declarations.push_back(variable.get()); + declarations.push_back(getCurrentThis()); return declarations; } -ASTPointer GlobalContext::createVariable(const string& _name, shared_ptr const& _type) +MagicVariableDeclaration*GlobalContext::getCurrentThis() const { - ASTPointer variable = make_shared(Location(), ASTPointer(), - make_shared(_name)); - variable->setType(_type); - return variable; + if (!m_thisPointer[m_currentContract]) + m_thisPointer[m_currentContract] = make_shared( + MagicVariableDeclaration::VariableKind::THIS, + "this", make_shared(*m_currentContract)); + return m_thisPointer[m_currentContract].get(); + +} + +vector GlobalContext::getMagicVariables() const +{ + vector declarations; + declarations.reserve(m_magicVariables.size() + 1); + for (ASTPointer const& variable: m_magicVariables) + declarations.push_back(variable.get()); + declarations.push_back(getCurrentThis()); + return declarations; } } diff --git a/libsolidity/GlobalContext.h b/libsolidity/GlobalContext.h index b6dea7d56..0166734ca 100644 --- a/libsolidity/GlobalContext.h +++ b/libsolidity/GlobalContext.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -47,14 +48,14 @@ public: GlobalContext(); void setCurrentContract(ContractDefinition const& _contract); + std::vector getMagicVariables() const; std::vector getDeclarations() const; private: - /// Creates a virtual variable declaration with the given name and type. - static ASTPointer createVariable(std::string const& _name, std::shared_ptr const& _type); - - std::vector> m_objects; - ASTPointer m_this; + MagicVariableDeclaration* getCurrentThis() const; + std::vector> m_magicVariables; + ContractDefinition const* m_currentContract; + std::map> mutable m_thisPointer; }; } diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index f9d3d90fa..8ded428f8 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -189,6 +189,8 @@ u256 IntegerType::literalValue(Literal const& _literal) const return u256(value); } +const MemberList IntegerType::AddressMemberList = MemberList({{"balance", std::make_shared(256)}}); + bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const { // conversion to integer is fine, but not to address diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 297284ba0..a8caf7154 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -73,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 }; ///@{ @@ -110,6 +110,9 @@ public: 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; } @@ -154,6 +157,9 @@ public: virtual bool operator==(Type const& _other) const override; 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; @@ -166,6 +172,7 @@ public: private: int m_bits; Modifier m_modifier; + static const MemberList AddressMemberList; }; /** @@ -186,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; diff --git a/test/solidityCompiler.cpp b/test/solidityCompiler.cpp index 1767a0f36..3e86919a6 100644 --- a/test/solidityCompiler.cpp +++ b/test/solidityCompiler.cpp @@ -52,7 +52,7 @@ bytes compileContract(const string& _sourceCode) BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); Compiler compiler; - compiler.compileContract(*contract); + compiler.compileContract(*contract, {}); // debug //compiler.streamAssembly(cout); return compiler.getAssembledBytecode(); diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index a7c6357bf..46081a63c 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -725,7 +725,7 @@ BOOST_AUTO_TEST_CASE(constructor) BOOST_AUTO_TEST_CASE(balance) { char const* sourceCode = "contract test {\n" - " function getBalance() returns (u256 balance) {\n" + " function getBalance() returns (uint256 balance) {\n" " return address(this).balance;\n" " }\n" "}\n"; diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index 61aabb346..a19e7450c 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -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() } diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index 0ee88b261..9319a02c5 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -221,26 +221,6 @@ BOOST_AUTO_TEST_CASE(statement_starting_with_type_conversion) BOOST_CHECK_NO_THROW(parseText(text)); } -BOOST_AUTO_TEST_CASE(blockchain_access) -{ - char const* text = "contract test {\n" - " function fun() {\n" - " u256 x = address(0).balance;\n" - " }\n" - "}\n"; - BOOST_CHECK_NO_THROW(parseText(text)); -} - -BOOST_AUTO_TEST_CASE(blockchain_access_invalid) -{ - char const* text = "contract test {\n" - " function fun() {\n" - " address(0).balance = 7;\n" - " }\n" - "}\n"; - BOOST_CHECK_THROW(parseText(text), TypeError); -} - BOOST_AUTO_TEST_SUITE_END() }