mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Magic variables.
This commit is contained in:
parent
c50cd646ce
commit
583a315d3d
17
AST.cpp
17
AST.cpp
@ -372,8 +372,6 @@ void VariableDefinition::checkTypeRequirements()
|
|||||||
m_variable->setType(m_value->getType());
|
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()
|
void Assignment::checkTypeRequirements()
|
||||||
@ -466,8 +464,6 @@ void FunctionCall::checkTypeRequirements()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_expression->requireLValue();
|
|
||||||
|
|
||||||
//@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
|
||||||
@ -495,25 +491,23 @@ bool FunctionCall::isTypeConversion() const
|
|||||||
void MemberAccess::checkTypeRequirements()
|
void MemberAccess::checkTypeRequirements()
|
||||||
{
|
{
|
||||||
m_expression->checkTypeRequirements();
|
m_expression->checkTypeRequirements();
|
||||||
m_expression->requireLValue();
|
|
||||||
Type const& type = *m_expression->getType();
|
Type const& type = *m_expression->getType();
|
||||||
m_type = type.getMemberType(*m_memberName);
|
m_type = type.getMemberType(*m_memberName);
|
||||||
if (!m_type)
|
if (!m_type)
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found in " + type.toString()));
|
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()
|
void IndexAccess::checkTypeRequirements()
|
||||||
{
|
{
|
||||||
m_base->checkTypeRequirements();
|
m_base->checkTypeRequirements();
|
||||||
m_base->requireLValue();
|
|
||||||
if (m_base->getType()->getCategory() != Type::Category::MAPPING)
|
if (m_base->getType()->getCategory() != Type::Category::MAPPING)
|
||||||
BOOST_THROW_EXCEPTION(m_base->createTypeError("Indexed expression has to be a mapping (is " +
|
BOOST_THROW_EXCEPTION(m_base->createTypeError("Indexed expression has to be a mapping (is " +
|
||||||
m_base->getType()->toString() + ")"));
|
m_base->getType()->toString() + ")"));
|
||||||
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType());
|
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType());
|
||||||
m_index->expectType(*type.getKeyType());
|
m_index->expectType(*type.getKeyType());
|
||||||
m_type = type.getValueType();
|
m_type = type.getValueType();
|
||||||
m_isLvalue = true;
|
m_isLvalue = m_type->getCategory() != Type::Category::MAPPING;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Identifier::checkTypeRequirements()
|
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
|
// Calling a function (e.g. function(12), otherContract.function(34)) does not do a type
|
||||||
// conversion.
|
// conversion.
|
||||||
m_type = make_shared<FunctionType>(*functionDef);
|
m_type = make_shared<FunctionType>(*functionDef);
|
||||||
m_isLvalue = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ContractDefinition* contractDef = dynamic_cast<ContractDefinition*>(m_referencedDeclaration);
|
ContractDefinition* contractDef = dynamic_cast<ContractDefinition*>(m_referencedDeclaration);
|
||||||
@ -554,6 +547,12 @@ void Identifier::checkTypeRequirements()
|
|||||||
m_type = make_shared<TypeType>(make_shared<ContractType>(*contractDef));
|
m_type = make_shared<TypeType>(make_shared<ContractType>(*contractDef));
|
||||||
return;
|
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."));
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Declaration reference of unknown/forbidden type."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
22
AST.h
22
AST.h
@ -230,6 +230,28 @@ private:
|
|||||||
std::shared_ptr<Type const> m_type; ///< derived type, initially empty
|
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
|
/// Types
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ class StructDefinition;
|
|||||||
class ParameterList;
|
class ParameterList;
|
||||||
class FunctionDefinition;
|
class FunctionDefinition;
|
||||||
class VariableDeclaration;
|
class VariableDeclaration;
|
||||||
|
class MagicVariableDeclaration;
|
||||||
class TypeName;
|
class TypeName;
|
||||||
class ElementaryTypeName;
|
class ElementaryTypeName;
|
||||||
class UserDefinedTypeName;
|
class UserDefinedTypeName;
|
||||||
|
15
Compiler.cpp
15
Compiler.cpp
@ -32,17 +32,13 @@ using namespace std;
|
|||||||
namespace dev {
|
namespace dev {
|
||||||
namespace solidity {
|
namespace solidity {
|
||||||
|
|
||||||
bytes Compiler::compile(ContractDefinition& _contract, bool _optimize)
|
void Compiler::compileContract(ContractDefinition& _contract, vector<MagicVariableDeclaration const*> const& _magicGlobals)
|
||||||
{
|
|
||||||
Compiler compiler;
|
|
||||||
compiler.compileContract(_contract);
|
|
||||||
return compiler.m_context.getAssembledBytecode(_optimize);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Compiler::compileContract(ContractDefinition& _contract)
|
|
||||||
{
|
{
|
||||||
m_context = CompilerContext(); // clear it just in case
|
m_context = CompilerContext(); // clear it just in case
|
||||||
|
|
||||||
|
for (MagicVariableDeclaration const* variable: _magicGlobals)
|
||||||
|
m_context.addMagicGlobal(*variable);
|
||||||
|
|
||||||
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
|
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
|
||||||
if (function->getName() != _contract.getName()) // don't add the constructor here
|
if (function->getName() != _contract.getName()) // don't add the constructor here
|
||||||
m_context.addFunction(*function);
|
m_context.addFunction(*function);
|
||||||
@ -328,7 +324,8 @@ bool Compiler::visit(ExpressionStatement& _expressionStatement)
|
|||||||
{
|
{
|
||||||
Expression& expression = _expressionStatement.getExpression();
|
Expression& expression = _expressionStatement.getExpression();
|
||||||
ExpressionCompiler::compileExpression(m_context, expression);
|
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;
|
m_context << eth::Instruction::POP;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -32,13 +32,10 @@ class Compiler: private ASTVisitor
|
|||||||
public:
|
public:
|
||||||
Compiler(): m_returnTag(m_context.newTag()) {}
|
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); }
|
bytes getAssembledBytecode(bool _optimize = false) { return m_context.getAssembledBytecode(_optimize); }
|
||||||
void streamAssembly(std::ostream& _stream) const { m_context.streamAssembly(_stream); }
|
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:
|
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.
|
||||||
|
@ -30,6 +30,11 @@ using namespace std;
|
|||||||
namespace dev {
|
namespace dev {
|
||||||
namespace solidity {
|
namespace solidity {
|
||||||
|
|
||||||
|
void CompilerContext::addMagicGlobal(MagicVariableDeclaration const& _declaration)
|
||||||
|
{
|
||||||
|
m_magicGlobals.insert(&_declaration);
|
||||||
|
}
|
||||||
|
|
||||||
void CompilerContext::addStateVariable(VariableDeclaration const& _declaration)
|
void CompilerContext::addStateVariable(VariableDeclaration const& _declaration)
|
||||||
{
|
{
|
||||||
m_stateVariables[&_declaration] = m_stateVariablesSize;
|
m_stateVariables[&_declaration] = m_stateVariablesSize;
|
||||||
|
@ -40,6 +40,7 @@ class CompilerContext
|
|||||||
public:
|
public:
|
||||||
CompilerContext(): m_stateVariablesSize(0) {}
|
CompilerContext(): m_stateVariablesSize(0) {}
|
||||||
|
|
||||||
|
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); }
|
||||||
void initializeLocalVariables(unsigned _numVariables);
|
void initializeLocalVariables(unsigned _numVariables);
|
||||||
@ -48,6 +49,7 @@ public:
|
|||||||
|
|
||||||
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 isFunctionDefinition(Declaration const* _declaration) const { return m_functionEntryLabels.count(_declaration); }
|
bool isFunctionDefinition(Declaration const* _declaration) const { return m_functionEntryLabels.count(_declaration); }
|
||||||
bool isLocalVariable(Declaration const* _declaration) const;
|
bool isLocalVariable(Declaration const* _declaration) const;
|
||||||
bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration); }
|
bool isStateVariable(Declaration const* _declaration) const { return m_stateVariables.count(_declaration); }
|
||||||
@ -90,6 +92,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
eth::Assembly m_asm;
|
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.
|
/// Size of the state variables, offset of next variable to be added.
|
||||||
u256 m_stateVariablesSize;
|
u256 m_stateVariablesSize;
|
||||||
/// Storage offsets of state variables
|
/// Storage offsets of state variables
|
||||||
|
@ -64,7 +64,7 @@ bytes const& CompilerStack::compile(bool _optimize)
|
|||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
||||||
m_bytecode.clear();
|
m_bytecode.clear();
|
||||||
m_compiler = make_shared<Compiler>();
|
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);
|
return m_bytecode = m_compiler->getAssembledBytecode(_optimize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
|
|||||||
{
|
{
|
||||||
if (_functionCall.isTypeConversion())
|
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))
|
if (asserts(_functionCall.getArguments().size() == 1))
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError());
|
BOOST_THROW_EXCEPTION(InternalCompilerError());
|
||||||
Expression& firstArgument = *_functionCall.getArguments().front();
|
Expression& firstArgument = *_functionCall.getArguments().front();
|
||||||
@ -179,6 +179,8 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
//@todo: check for "external call" (to be stored in type)
|
||||||
|
|
||||||
// Calling convention: Caller pushes return address and arguments
|
// Calling convention: Caller pushes return address and arguments
|
||||||
// Callee removes them and pushes return values
|
// Callee removes them and pushes return values
|
||||||
FunctionDefinition const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType()).getFunction();
|
FunctionDefinition const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType()).getFunction();
|
||||||
@ -193,9 +195,6 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
|
|||||||
appendTypeConversion(*arguments[i]->getType(), *function.getParameters()[i]->getType());
|
appendTypeConversion(*arguments[i]->getType(), *function.getParameters()[i]->getType());
|
||||||
}
|
}
|
||||||
_functionCall.getExpression().accept(*this);
|
_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.appendJump();
|
||||||
m_context << returnLabel;
|
m_context << returnLabel;
|
||||||
@ -213,19 +212,36 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
|
|||||||
|
|
||||||
void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
|
void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
|
||||||
{
|
{
|
||||||
if (asserts(m_currentLValue.isInStorage()))
|
switch (_memberAccess.getExpression().getType()->getCategory())
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to a non-storage value."));
|
{
|
||||||
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
|
case Type::Category::INTEGER:
|
||||||
m_context << type.getStorageOffsetOfMember(_memberAccess.getMemberName()) << eth::Instruction::ADD;
|
if (asserts(_memberAccess.getMemberName() == "balance"))
|
||||||
m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
|
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)
|
bool ExpressionCompiler::visit(IndexAccess& _indexAccess)
|
||||||
{
|
{
|
||||||
m_currentLValue.reset();
|
|
||||||
_indexAccess.getBaseExpression().accept(*this);
|
_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);
|
_indexAccess.getIndexExpression().accept(*this);
|
||||||
appendTypeConversion(*_indexAccess.getIndexExpression().getType(),
|
appendTypeConversion(*_indexAccess.getIndexExpression().getType(),
|
||||||
*dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType(),
|
*dynamic_cast<MappingType const&>(*_indexAccess.getBaseExpression().getType()).getKeyType(),
|
||||||
@ -242,8 +258,25 @@ bool ExpressionCompiler::visit(IndexAccess& _indexAccess)
|
|||||||
|
|
||||||
void ExpressionCompiler::endVisit(Identifier& _identifier)
|
void ExpressionCompiler::endVisit(Identifier& _identifier)
|
||||||
{
|
{
|
||||||
m_currentLValue.fromDeclaration(_identifier, *_identifier.getReferencedDeclaration());
|
Declaration* declaration = _identifier.getReferencedDeclaration();
|
||||||
m_currentLValue.retrieveValueIfLValueNotRequested(_identifier);
|
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)
|
void ExpressionCompiler::endVisit(Literal& _literal)
|
||||||
@ -410,9 +443,6 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
|
|||||||
{
|
{
|
||||||
switch (m_type)
|
switch (m_type)
|
||||||
{
|
{
|
||||||
case CODE:
|
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Tried to retrieve value of a function."));
|
|
||||||
break;
|
|
||||||
case STACK:
|
case STACK:
|
||||||
{
|
{
|
||||||
unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset));
|
unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset));
|
||||||
@ -423,11 +453,15 @@ void ExpressionCompiler::LValue::retrieveValue(Expression const& _expression, bo
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case STORAGE:
|
case STORAGE:
|
||||||
|
if (!_expression.getType()->isValueType())
|
||||||
|
break; // no distinction between value and reference for non-value types
|
||||||
if (!_remove)
|
if (!_remove)
|
||||||
*m_context << eth::Instruction::DUP1;
|
*m_context << eth::Instruction::DUP1;
|
||||||
*m_context << eth::Instruction::SLOAD;
|
*m_context << eth::Instruction::SLOAD;
|
||||||
break;
|
break;
|
||||||
case MEMORY:
|
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())
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
|
||||||
<< errinfo_comment("Location type not yet implemented."));
|
<< errinfo_comment("Location type not yet implemented."));
|
||||||
break;
|
break;
|
||||||
@ -455,15 +489,15 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LValue::STORAGE:
|
case LValue::STORAGE:
|
||||||
|
if (!_expression.getType()->isValueType())
|
||||||
|
break; // no distinction between value and reference for non-value types
|
||||||
if (!_move)
|
if (!_move)
|
||||||
*m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1;
|
*m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1;
|
||||||
*m_context << eth::Instruction::SSTORE;
|
*m_context << eth::Instruction::SSTORE;
|
||||||
break;
|
break;
|
||||||
case LValue::CODE:
|
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
|
|
||||||
<< errinfo_comment("Location type does not support assignment."));
|
|
||||||
break;
|
|
||||||
case LValue::MEMORY:
|
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())
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.getLocation())
|
||||||
<< errinfo_comment("Location type not yet implemented."));
|
<< errinfo_comment("Location type not yet implemented."));
|
||||||
break;
|
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))
|
if (m_context->isLocalVariable(&_declaration))
|
||||||
{
|
{
|
||||||
@ -495,13 +529,8 @@ void ExpressionCompiler::LValue::fromDeclaration(Expression const& _expression,
|
|||||||
m_type = STORAGE;
|
m_type = STORAGE;
|
||||||
*m_context << m_context->getStorageLocationOfVariable(_declaration);
|
*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
|
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."));
|
<< errinfo_comment("Identifier type not supported or identifier not found."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,31 +83,30 @@ private:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to store and retrieve lvalues to and from various locations.
|
* 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
|
* All types except STACK store a reference in a slot on the stack, STACK just
|
||||||
* base stack offset of the variable in @a m_baseStackOffset.
|
* stores the base stack offset of the variable in @a m_baseStackOffset.
|
||||||
*/
|
*/
|
||||||
class LValue
|
class LValue
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum LValueType { NONE, CODE, STACK, MEMORY, STORAGE };
|
enum LValueType { NONE, STACK, MEMORY, STORAGE };
|
||||||
|
|
||||||
explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); }
|
explicit LValue(CompilerContext& _compilerContext): m_context(&_compilerContext) { reset(); }
|
||||||
LValue(CompilerContext& _compilerContext, LValueType _type, unsigned _baseStackOffset = 0):
|
LValue(CompilerContext& _compilerContext, LValueType _type, unsigned _baseStackOffset = 0):
|
||||||
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset) {}
|
m_context(&_compilerContext), m_type(_type), m_baseStackOffset(_baseStackOffset) {}
|
||||||
|
|
||||||
/// Set type according to the declaration and retrieve the reference.
|
/// Set type according to the declaration and retrieve the reference.
|
||||||
/// @a _expression is the current expression, used for error reporting.
|
/// @a _expression is the current expression
|
||||||
void fromDeclaration(Expression const& _expression, Declaration const& _declaration);
|
void fromIdentifier(Identifier const& _identifier, Declaration const& _declaration);
|
||||||
void reset() { m_type = NONE; m_baseStackOffset = 0; }
|
void reset() { m_type = NONE; m_baseStackOffset = 0; }
|
||||||
|
|
||||||
bool isValid() const { return m_type != NONE; }
|
bool isValid() const { return m_type != NONE; }
|
||||||
bool isInCode() const { return m_type == CODE; }
|
|
||||||
bool isInOnStack() const { return m_type == STACK; }
|
bool isInOnStack() const { return m_type == STACK; }
|
||||||
bool isInMemory() const { return m_type == MEMORY; }
|
bool isInMemory() const { return m_type == MEMORY; }
|
||||||
bool isInStorage() const { return m_type == STORAGE; }
|
bool isInStorage() const { return m_type == STORAGE; }
|
||||||
|
|
||||||
/// @returns true if this lvalue reference type occupies a slot on the stack.
|
/// @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,
|
/// 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).
|
/// also removes the reference from the stack (note that is does not reset the type to @a NONE).
|
||||||
|
@ -45,25 +45,37 @@ GlobalContext::GlobalContext()
|
|||||||
|
|
||||||
void GlobalContext::setCurrentContract(ContractDefinition const& _contract)
|
void GlobalContext::setCurrentContract(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
m_this = createVariable("this", make_shared<ContractType>(_contract));
|
m_currentContract = &_contract;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<Declaration*> GlobalContext::getDeclarations() const
|
vector<Declaration*> GlobalContext::getDeclarations() const
|
||||||
{
|
{
|
||||||
vector<Declaration*> declarations;
|
vector<Declaration*> declarations;
|
||||||
declarations.reserve(m_objects.size() + 1);
|
declarations.reserve(m_magicVariables.size() + 1);
|
||||||
for (ASTPointer<Declaration> const& declaration: m_objects)
|
for (ASTPointer<Declaration> const& variable: m_magicVariables)
|
||||||
declarations.push_back(declaration.get());
|
declarations.push_back(variable.get());
|
||||||
declarations.push_back(m_this.get());
|
declarations.push_back(getCurrentThis());
|
||||||
return declarations;
|
return declarations;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTPointer<VariableDeclaration> GlobalContext::createVariable(const string& _name, shared_ptr<const Type> const& _type)
|
MagicVariableDeclaration*GlobalContext::getCurrentThis() const
|
||||||
{
|
{
|
||||||
ASTPointer<VariableDeclaration> variable = make_shared<VariableDeclaration>(Location(), ASTPointer<TypeName>(),
|
if (!m_thisPointer[m_currentContract])
|
||||||
make_shared<ASTString>(_name));
|
m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(
|
||||||
variable->setType(_type);
|
MagicVariableDeclaration::VariableKind::THIS,
|
||||||
return variable;
|
"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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <libsolidity/ASTForward.h>
|
#include <libsolidity/ASTForward.h>
|
||||||
@ -47,14 +48,14 @@ public:
|
|||||||
GlobalContext();
|
GlobalContext();
|
||||||
void setCurrentContract(ContractDefinition const& _contract);
|
void setCurrentContract(ContractDefinition const& _contract);
|
||||||
|
|
||||||
|
std::vector<MagicVariableDeclaration const*> getMagicVariables() const;
|
||||||
std::vector<Declaration*> getDeclarations() const;
|
std::vector<Declaration*> getDeclarations() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Creates a virtual variable declaration with the given name and type.
|
MagicVariableDeclaration* getCurrentThis() const;
|
||||||
static ASTPointer<VariableDeclaration> createVariable(std::string const& _name, std::shared_ptr<Type const> const& _type);
|
std::vector<std::shared_ptr<MagicVariableDeclaration>> m_magicVariables;
|
||||||
|
ContractDefinition const* m_currentContract;
|
||||||
std::vector<ASTPointer<Declaration>> m_objects;
|
std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration>> mutable m_thisPointer;
|
||||||
ASTPointer<Declaration> m_this;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -189,6 +189,8 @@ u256 IntegerType::literalValue(Literal const& _literal) const
|
|||||||
return u256(value);
|
return u256(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MemberList IntegerType::AddressMemberList = MemberList({{"balance", std::make_shared<IntegerType const>(256)}});
|
||||||
|
|
||||||
bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
// conversion to integer is fine, but not to address
|
// conversion to integer is fine, but not to address
|
||||||
|
10
Types.h
10
Types.h
@ -73,7 +73,7 @@ class Type: private boost::noncopyable
|
|||||||
public:
|
public:
|
||||||
enum class Category
|
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; }
|
virtual bool canBeStored() const { return true; }
|
||||||
/// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping.
|
/// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping.
|
||||||
virtual bool canLiveOutsideStorage() const { return true; }
|
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.
|
/// Returns the list of all members of this type. Default implementation: no members.
|
||||||
virtual MemberList const& getMembers() const { return EmptyMemberList; }
|
virtual MemberList const& getMembers() const { return EmptyMemberList; }
|
||||||
@ -154,6 +157,9 @@ public:
|
|||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
|
|
||||||
virtual unsigned getCalldataEncodedSize() const override { 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 std::string toString() const override;
|
||||||
virtual u256 literalValue(Literal const& _literal) const override;
|
virtual u256 literalValue(Literal const& _literal) const override;
|
||||||
@ -166,6 +172,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
int m_bits;
|
int m_bits;
|
||||||
Modifier m_modifier;
|
Modifier m_modifier;
|
||||||
|
static const MemberList AddressMemberList;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -186,6 +193,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual unsigned getCalldataEncodedSize() const { return 1; }
|
virtual unsigned getCalldataEncodedSize() const { return 1; }
|
||||||
|
virtual bool isValueType() const override { return true; }
|
||||||
|
|
||||||
virtual std::string toString() const override { return "bool"; }
|
virtual std::string toString() const override { return "bool"; }
|
||||||
virtual u256 literalValue(Literal const& _literal) const override;
|
virtual u256 literalValue(Literal const& _literal) const override;
|
||||||
|
Loading…
Reference in New Issue
Block a user