mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #5775 from ethereum/codeAccess
Provide access to code of contract types.
This commit is contained in:
commit
2ec997e697
@ -1,6 +1,7 @@
|
|||||||
### 0.5.3 (unreleased)
|
### 0.5.3 (unreleased)
|
||||||
|
|
||||||
Language Features:
|
Language Features:
|
||||||
|
* Provide access to creation and runtime code of contracts via ``type(C).creationCode`` / ``type(C).runtimeCode``.
|
||||||
|
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
|
@ -385,6 +385,8 @@ Global Variables
|
|||||||
- ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei
|
- ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei
|
||||||
- ``<address payable>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure
|
- ``<address payable>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure
|
||||||
- ``<address payable>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure
|
- ``<address payable>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure
|
||||||
|
- ``type(C).creationCode`` (``bytes memory``): creation bytecode of the given contract, see :ref:`Type Information<meta-type>`.
|
||||||
|
- ``type(C).runtimeCode`` (``bytes memory``): runtime bytecode of the given contract, see :ref:`Type Information<meta-type>`.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness,
|
Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness,
|
||||||
@ -445,7 +447,7 @@ These keywords are reserved in Solidity. They might become part of the syntax in
|
|||||||
``abstract``, ``after``, ``alias``, ``apply``, ``auto``, ``case``, ``catch``, ``copyof``, ``default``,
|
``abstract``, ``after``, ``alias``, ``apply``, ``auto``, ``case``, ``catch``, ``copyof``, ``default``,
|
||||||
``define``, ``final``, ``immutable``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``,
|
``define``, ``final``, ``immutable``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``,
|
||||||
``mutable``, ``null``, ``of``, ``override``, ``partial``, ``promise``, ``reference``, ``relocatable``,
|
``mutable``, ``null``, ``of``, ``override``, ``partial``, ``promise``, ``reference``, ``relocatable``,
|
||||||
``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``try``, ``type``, ``typedef``, ``typeof``,
|
``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``try``, ``typedef``, ``typeof``,
|
||||||
``unchecked``.
|
``unchecked``.
|
||||||
|
|
||||||
Language Grammar
|
Language Grammar
|
||||||
|
@ -329,6 +329,9 @@ Contracts do not support any operators.
|
|||||||
The members of contract types are the external functions of the contract
|
The members of contract types are the external functions of the contract
|
||||||
including public state variables.
|
including public state variables.
|
||||||
|
|
||||||
|
For a contract ``C`` you can use ``type(C)`` to access
|
||||||
|
:ref:`type information<meta-type>` about the contract.
|
||||||
|
|
||||||
.. index:: byte array, bytes32
|
.. index:: byte array, bytes32
|
||||||
|
|
||||||
Fixed-size byte arrays
|
Fixed-size byte arrays
|
||||||
|
@ -244,3 +244,33 @@ Furthermore, all functions of the current contract are callable directly includi
|
|||||||
.. note::
|
.. note::
|
||||||
Prior to version 0.5.0, there was a function called ``suicide`` with the same
|
Prior to version 0.5.0, there was a function called ``suicide`` with the same
|
||||||
semantics as ``selfdestruct``.
|
semantics as ``selfdestruct``.
|
||||||
|
|
||||||
|
.. index:: type, creationCode, runtimeCode
|
||||||
|
|
||||||
|
.. _meta-type:
|
||||||
|
|
||||||
|
Type Information
|
||||||
|
----------------
|
||||||
|
|
||||||
|
The expression ``type(X)`` can be used to retrieve information about the
|
||||||
|
type ``X``. Currently, there is limited support for this feature, but
|
||||||
|
it might be expanded in the future. The following properties are
|
||||||
|
available for a conract type ``C``:
|
||||||
|
|
||||||
|
``type(C).creationCode``:
|
||||||
|
Memory byte array that contains the creation bytecode of the contract.
|
||||||
|
This can be used in inline assembly to build custom creation routines,
|
||||||
|
especially by using the ``create2`` opcode.
|
||||||
|
This property can **not** be accessed in the contract itself or any
|
||||||
|
derived contract. It causes the bytecode to be included in the bytecode
|
||||||
|
of the call site and thus circular references like that are not possible.
|
||||||
|
|
||||||
|
``type(C).runtimeCode``:
|
||||||
|
Memory byte array that contains the runtime bytecode of the contract.
|
||||||
|
This is the code that is usually deployed by the constructor of ``C``.
|
||||||
|
If ``C`` has a constructor that uses inline assembly, this might be
|
||||||
|
different from the actually deployed bytecode. Also note that libraries
|
||||||
|
modify their runtime bytecode at time of deployment to guard against
|
||||||
|
regular calls.
|
||||||
|
The same restrictions as with ``.creationCode`` also apply for this
|
||||||
|
property.
|
||||||
|
@ -180,6 +180,7 @@ namespace langutil
|
|||||||
K(CallData, "calldata", 0) \
|
K(CallData, "calldata", 0) \
|
||||||
K(Struct, "struct", 0) \
|
K(Struct, "struct", 0) \
|
||||||
K(Throw, "throw", 0) \
|
K(Throw, "throw", 0) \
|
||||||
|
K(Type, "type", 0) \
|
||||||
K(Using, "using", 0) \
|
K(Using, "using", 0) \
|
||||||
K(Var, "var", 0) \
|
K(Var, "var", 0) \
|
||||||
K(View, "view", 0) \
|
K(View, "view", 0) \
|
||||||
@ -256,7 +257,6 @@ namespace langutil
|
|||||||
K(Supports, "supports", 0) \
|
K(Supports, "supports", 0) \
|
||||||
K(Switch, "switch", 0) \
|
K(Switch, "switch", 0) \
|
||||||
K(Try, "try", 0) \
|
K(Try, "try", 0) \
|
||||||
K(Type, "type", 0) \
|
|
||||||
K(Typedef, "typedef", 0) \
|
K(Typedef, "typedef", 0) \
|
||||||
K(TypeOf, "typeof", 0) \
|
K(TypeOf, "typeof", 0) \
|
||||||
K(Unchecked, "unchecked", 0) \
|
K(Unchecked, "unchecked", 0) \
|
||||||
|
@ -61,7 +61,14 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{
|
|||||||
make_shared<MagicVariableDeclaration>("sha256", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, false, StateMutability::Pure)),
|
make_shared<MagicVariableDeclaration>("sha256", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, false, StateMutability::Pure)),
|
||||||
make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
|
make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
|
||||||
make_shared<MagicVariableDeclaration>("suicide", make_shared<FunctionType>(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
|
make_shared<MagicVariableDeclaration>("suicide", make_shared<FunctionType>(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
|
||||||
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction))
|
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)),
|
||||||
|
make_shared<MagicVariableDeclaration>("type", make_shared<FunctionType>(
|
||||||
|
strings{"address"} /* accepts any contract type, handled by the type checker */,
|
||||||
|
strings{} /* returns a MagicType, handled by the type checker */,
|
||||||
|
FunctionType::Kind::MetaType,
|
||||||
|
false,
|
||||||
|
StateMutability::Pure
|
||||||
|
)),
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,56 @@ using namespace dev;
|
|||||||
using namespace langutil;
|
using namespace langutil;
|
||||||
using namespace dev::solidity;
|
using namespace dev::solidity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class that determines whether a contract's constructor uses inline assembly.
|
||||||
|
*/
|
||||||
|
class dev::solidity::ConstructorUsesAssembly
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// @returns true if and only if the contract's or any of its bases' constructors
|
||||||
|
/// use inline assembly.
|
||||||
|
bool check(ContractDefinition const& _contract)
|
||||||
|
{
|
||||||
|
for (auto const* base: _contract.annotation().linearizedBaseContracts)
|
||||||
|
if (checkInternal(*base))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Checker: public ASTConstVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Checker(FunctionDefinition const& _f) { _f.accept(*this); }
|
||||||
|
bool visit(InlineAssembly const&) override { assemblySeen = true; return false; }
|
||||||
|
bool assemblySeen = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool checkInternal(ContractDefinition const& _contract)
|
||||||
|
{
|
||||||
|
if (!m_usesAssembly.count(&_contract))
|
||||||
|
{
|
||||||
|
bool usesAssembly = false;
|
||||||
|
if (_contract.constructor())
|
||||||
|
usesAssembly = Checker{*_contract.constructor()}.assemblySeen;
|
||||||
|
m_usesAssembly[&_contract] = usesAssembly;
|
||||||
|
}
|
||||||
|
return m_usesAssembly[&_contract];
|
||||||
|
}
|
||||||
|
|
||||||
|
map<ContractDefinition const*, bool> m_usesAssembly;
|
||||||
|
};
|
||||||
|
|
||||||
|
StaticAnalyzer::StaticAnalyzer(ErrorReporter& _errorReporter):
|
||||||
|
m_errorReporter(_errorReporter)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticAnalyzer::~StaticAnalyzer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
bool StaticAnalyzer::analyze(SourceUnit const& _sourceUnit)
|
bool StaticAnalyzer::analyze(SourceUnit const& _sourceUnit)
|
||||||
{
|
{
|
||||||
_sourceUnit.accept(*this);
|
_sourceUnit.accept(*this);
|
||||||
@ -152,6 +202,18 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
|
|||||||
_memberAccess.location(),
|
_memberAccess.location(),
|
||||||
"\"block.blockhash()\" has been deprecated in favor of \"blockhash()\""
|
"\"block.blockhash()\" has been deprecated in favor of \"blockhash()\""
|
||||||
);
|
);
|
||||||
|
else if (type->kind() == MagicType::Kind::MetaType && _memberAccess.memberName() == "runtimeCode")
|
||||||
|
{
|
||||||
|
if (!m_constructorUsesAssembly)
|
||||||
|
m_constructorUsesAssembly = make_unique<ConstructorUsesAssembly>();
|
||||||
|
ContractType const& contract = dynamic_cast<ContractType const&>(*type->typeArgument());
|
||||||
|
if (m_constructorUsesAssembly->check(contract.contractDefinition()))
|
||||||
|
m_errorReporter.warning(
|
||||||
|
_memberAccess.location(),
|
||||||
|
"The constructor of the contract (or its base) uses inline assembly. "
|
||||||
|
"Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode."
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_memberAccess.memberName() == "callcode")
|
if (_memberAccess.memberName() == "callcode")
|
||||||
|
@ -38,6 +38,8 @@ namespace dev
|
|||||||
namespace solidity
|
namespace solidity
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class ConstructorUsesAssembly;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The module that performs static analysis on the AST.
|
* The module that performs static analysis on the AST.
|
||||||
@ -49,7 +51,8 @@ class StaticAnalyzer: private ASTConstVisitor
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// @param _errorReporter provides the error logging functionality.
|
/// @param _errorReporter provides the error logging functionality.
|
||||||
explicit StaticAnalyzer(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
|
explicit StaticAnalyzer(langutil::ErrorReporter& _errorReporter);
|
||||||
|
~StaticAnalyzer();
|
||||||
|
|
||||||
/// Performs static analysis on the given source unit and all of its sub-nodes.
|
/// Performs static analysis on the given source unit and all of its sub-nodes.
|
||||||
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
|
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
|
||||||
@ -85,6 +88,10 @@ private:
|
|||||||
/// when traversing.
|
/// when traversing.
|
||||||
std::map<std::pair<size_t, VariableDeclaration const*>, int> m_localVarUseCount;
|
std::map<std::pair<size_t, VariableDeclaration const*>, int> m_localVarUseCount;
|
||||||
|
|
||||||
|
/// Cache that holds information about whether a contract's constructor
|
||||||
|
/// uses inline assembly.
|
||||||
|
std::unique_ptr<ConstructorUsesAssembly> m_constructorUsesAssembly;
|
||||||
|
|
||||||
FunctionDefinition const* m_currentFunction = nullptr;
|
FunctionDefinition const* m_currentFunction = nullptr;
|
||||||
|
|
||||||
/// Flag that indicates a constructor.
|
/// Flag that indicates a constructor.
|
||||||
|
@ -199,6 +199,38 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c
|
|||||||
return components;
|
return components;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(FunctionCall const& _functionCall)
|
||||||
|
{
|
||||||
|
vector<ASTPointer<Expression const>> arguments = _functionCall.arguments();
|
||||||
|
if (arguments.size() != 1)
|
||||||
|
{
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
_functionCall.location(),
|
||||||
|
"This function takes one argument, but " +
|
||||||
|
toString(arguments.size()) +
|
||||||
|
" were provided."
|
||||||
|
);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
TypePointer firstArgType = type(*arguments.front());
|
||||||
|
if (
|
||||||
|
firstArgType->category() != Type::Category::TypeType ||
|
||||||
|
dynamic_cast<TypeType const&>(*firstArgType).actualType()->category() != TypeType::Category::Contract
|
||||||
|
)
|
||||||
|
{
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
arguments.front()->location(),
|
||||||
|
"Invalid type for argument in function call. "
|
||||||
|
"Contract type required, but " +
|
||||||
|
type(*arguments.front())->toString(true) +
|
||||||
|
" provided."
|
||||||
|
);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {MagicType::metaType(dynamic_cast<TypeType const&>(*firstArgType).actualType())};
|
||||||
|
}
|
||||||
|
|
||||||
void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
|
void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
|
||||||
{
|
{
|
||||||
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
|
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
|
||||||
@ -1831,6 +1863,9 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
|||||||
returnTypes = functionType->returnParameterTypes();
|
returnTypes = functionType->returnParameterTypes();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case FunctionType::Kind::MetaType:
|
||||||
|
returnTypes = typeCheckMetaTypeFunctionAndRetrieveReturnType(_functionCall);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
typeCheckFunctionCall(_functionCall, functionType);
|
typeCheckFunctionCall(_functionCall, functionType);
|
||||||
@ -2071,8 +2106,24 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
|||||||
if (tt->actualType()->category() == Type::Category::Enum)
|
if (tt->actualType()->category() == Type::Category::Enum)
|
||||||
annotation.isPure = true;
|
annotation.isPure = true;
|
||||||
if (auto magicType = dynamic_cast<MagicType const*>(exprType.get()))
|
if (auto magicType = dynamic_cast<MagicType const*>(exprType.get()))
|
||||||
|
{
|
||||||
if (magicType->kind() == MagicType::Kind::ABI)
|
if (magicType->kind() == MagicType::Kind::ABI)
|
||||||
annotation.isPure = true;
|
annotation.isPure = true;
|
||||||
|
else if (magicType->kind() == MagicType::Kind::MetaType && (
|
||||||
|
memberName == "creationCode" || memberName == "runtimeCode"
|
||||||
|
))
|
||||||
|
{
|
||||||
|
annotation.isPure = true;
|
||||||
|
m_scope->annotation().contractDependencies.insert(
|
||||||
|
&dynamic_cast<ContractType const&>(*magicType->typeArgument()).contractDefinition()
|
||||||
|
);
|
||||||
|
if (contractDependenciesAreCyclic(*m_scope))
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
_memberAccess.location(),
|
||||||
|
"Circular reference for contract code access."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,8 @@ private:
|
|||||||
bool _abiEncoderV2
|
bool _abiEncoderV2
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TypePointers typeCheckMetaTypeFunctionAndRetrieveReturnType(FunctionCall const& _functionCall);
|
||||||
|
|
||||||
/// Performs type checks and determines result types for type conversion FunctionCall nodes.
|
/// Performs type checks and determines result types for type conversion FunctionCall nodes.
|
||||||
TypePointer typeCheckTypeConversionAndRetrieveReturnType(
|
TypePointer typeCheckTypeConversionAndRetrieveReturnType(
|
||||||
FunctionCall const& _functionCall
|
FunctionCall const& _functionCall
|
||||||
|
@ -338,7 +338,9 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
|
|||||||
{MagicType::Kind::ABI, "encodeWithSignature"},
|
{MagicType::Kind::ABI, "encodeWithSignature"},
|
||||||
{MagicType::Kind::Block, "blockhash"},
|
{MagicType::Kind::Block, "blockhash"},
|
||||||
{MagicType::Kind::Message, "data"},
|
{MagicType::Kind::Message, "data"},
|
||||||
{MagicType::Kind::Message, "sig"}
|
{MagicType::Kind::Message, "sig"},
|
||||||
|
{MagicType::Kind::MetaType, "creationCode"},
|
||||||
|
{MagicType::Kind::MetaType, "runtimeCode"}
|
||||||
};
|
};
|
||||||
set<MagicMember> static const payableMembers{
|
set<MagicMember> static const payableMembers{
|
||||||
{MagicType::Kind::Message, "value"}
|
{MagicType::Kind::Message, "value"}
|
||||||
|
@ -138,6 +138,11 @@ bool ContractDefinition::constructorIsPublic() const
|
|||||||
return !f || f->isPublic();
|
return !f || f->isPublic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ContractDefinition::canBeDeployed() const
|
||||||
|
{
|
||||||
|
return constructorIsPublic() && annotation().unimplementedFunctions.empty();
|
||||||
|
}
|
||||||
|
|
||||||
FunctionDefinition const* ContractDefinition::fallbackFunction() const
|
FunctionDefinition const* ContractDefinition::fallbackFunction() const
|
||||||
{
|
{
|
||||||
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
|
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
|
||||||
|
@ -408,6 +408,10 @@ public:
|
|||||||
FunctionDefinition const* constructor() const;
|
FunctionDefinition const* constructor() const;
|
||||||
/// @returns true iff the constructor of this contract is public (or non-existing).
|
/// @returns true iff the constructor of this contract is public (or non-existing).
|
||||||
bool constructorIsPublic() const;
|
bool constructorIsPublic() const;
|
||||||
|
/// @returns true iff the contract can be deployed, i.e. is not abstract and has a
|
||||||
|
/// public constructor.
|
||||||
|
/// Should only be called after the type checker has run.
|
||||||
|
bool canBeDeployed() const;
|
||||||
/// Returns the fallback function or nullptr if no fallback function was specified.
|
/// Returns the fallback function or nullptr if no fallback function was specified.
|
||||||
FunctionDefinition const* fallbackFunction() const;
|
FunctionDefinition const* fallbackFunction() const;
|
||||||
|
|
||||||
|
@ -2626,6 +2626,7 @@ string FunctionType::richIdentifier() const
|
|||||||
case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break;
|
case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break;
|
||||||
case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break;
|
case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break;
|
||||||
case Kind::ABIDecode: id += "abidecode"; break;
|
case Kind::ABIDecode: id += "abidecode"; break;
|
||||||
|
case Kind::MetaType: id += "metatype"; break;
|
||||||
}
|
}
|
||||||
id += "_" + stateMutabilityToString(m_stateMutability);
|
id += "_" + stateMutabilityToString(m_stateMutability);
|
||||||
id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes);
|
id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes);
|
||||||
@ -3037,7 +3038,8 @@ bool FunctionType::isPure() const
|
|||||||
m_kind == Kind::ABIEncodePacked ||
|
m_kind == Kind::ABIEncodePacked ||
|
||||||
m_kind == Kind::ABIEncodeWithSelector ||
|
m_kind == Kind::ABIEncodeWithSelector ||
|
||||||
m_kind == Kind::ABIEncodeWithSignature ||
|
m_kind == Kind::ABIEncodeWithSignature ||
|
||||||
m_kind == Kind::ABIDecode;
|
m_kind == Kind::ABIDecode ||
|
||||||
|
m_kind == Kind::MetaType;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
|
TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
|
||||||
@ -3305,6 +3307,14 @@ string ModuleType::toString(bool) const
|
|||||||
return string("module \"") + m_sourceUnit.annotation().path + string("\"");
|
return string("module \"") + m_sourceUnit.annotation().path + string("\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shared_ptr<MagicType> MagicType::metaType(TypePointer _type)
|
||||||
|
{
|
||||||
|
solAssert(_type && _type->category() == Type::Category::Contract, "Only contracts supported for now.");
|
||||||
|
auto t = make_shared<MagicType>(Kind::MetaType);
|
||||||
|
t->m_typeArgument = std::move(_type);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
string MagicType::richIdentifier() const
|
string MagicType::richIdentifier() const
|
||||||
{
|
{
|
||||||
switch (m_kind)
|
switch (m_kind)
|
||||||
@ -3317,6 +3327,9 @@ string MagicType::richIdentifier() const
|
|||||||
return "t_magic_transaction";
|
return "t_magic_transaction";
|
||||||
case Kind::ABI:
|
case Kind::ABI:
|
||||||
return "t_magic_abi";
|
return "t_magic_abi";
|
||||||
|
case Kind::MetaType:
|
||||||
|
solAssert(m_typeArgument, "");
|
||||||
|
return "t_magic_meta_type_" + m_typeArgument->richIdentifier();
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -3403,12 +3416,27 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
|
|||||||
StateMutability::Pure
|
StateMutability::Pure
|
||||||
)}
|
)}
|
||||||
});
|
});
|
||||||
default:
|
case Kind::MetaType:
|
||||||
solAssert(false, "Unknown kind of magic.");
|
{
|
||||||
|
solAssert(
|
||||||
|
m_typeArgument && m_typeArgument->category() == Type::Category::Contract,
|
||||||
|
"Only contracts supported for now"
|
||||||
|
);
|
||||||
|
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_typeArgument).contractDefinition();
|
||||||
|
if (contract.canBeDeployed())
|
||||||
|
return MemberList::MemberMap({
|
||||||
|
{"creationCode", std::make_shared<ArrayType>(DataLocation::Memory)},
|
||||||
|
{"runtimeCode", std::make_shared<ArrayType>(DataLocation::Memory)}
|
||||||
|
});
|
||||||
|
else
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
solAssert(false, "Unknown kind of magic.");
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
string MagicType::toString(bool) const
|
string MagicType::toString(bool _short) const
|
||||||
{
|
{
|
||||||
switch (m_kind)
|
switch (m_kind)
|
||||||
{
|
{
|
||||||
@ -3420,7 +3448,17 @@ string MagicType::toString(bool) const
|
|||||||
return "tx";
|
return "tx";
|
||||||
case Kind::ABI:
|
case Kind::ABI:
|
||||||
return "abi";
|
return "abi";
|
||||||
default:
|
case Kind::MetaType:
|
||||||
solAssert(false, "Unknown kind of magic.");
|
solAssert(m_typeArgument, "");
|
||||||
|
return "type(" + m_typeArgument->toString(_short) + ")";
|
||||||
}
|
}
|
||||||
|
solAssert(false, "Unknown kind of magic.");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
TypePointer MagicType::typeArgument() const
|
||||||
|
{
|
||||||
|
solAssert(m_kind == Kind::MetaType, "");
|
||||||
|
solAssert(m_typeArgument, "");
|
||||||
|
return m_typeArgument;
|
||||||
}
|
}
|
||||||
|
@ -989,6 +989,7 @@ public:
|
|||||||
ABIEncodeWithSignature,
|
ABIEncodeWithSignature,
|
||||||
ABIDecode,
|
ABIDecode,
|
||||||
GasLeft, ///< gasleft()
|
GasLeft, ///< gasleft()
|
||||||
|
MetaType ///< type(...)
|
||||||
};
|
};
|
||||||
|
|
||||||
Category category() const override { return Category::Function; }
|
Category category() const override { return Category::Function; }
|
||||||
@ -1299,16 +1300,23 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Special type for magic variables (block, msg, tx), similar to a struct but without any reference
|
* Special type for magic variables (block, msg, tx, type(...)), similar to a struct but without any reference.
|
||||||
* (it always references a global singleton by name).
|
|
||||||
*/
|
*/
|
||||||
class MagicType: public Type
|
class MagicType: public Type
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum class Kind { Block, Message, Transaction, ABI };
|
enum class Kind {
|
||||||
|
Block, ///< "block"
|
||||||
|
Message, ///< "msg"
|
||||||
|
Transaction, ///< "tx"
|
||||||
|
ABI, ///< "abi"
|
||||||
|
MetaType ///< "type(...)"
|
||||||
|
};
|
||||||
Category category() const override { return Category::Magic; }
|
Category category() const override { return Category::Magic; }
|
||||||
|
|
||||||
explicit MagicType(Kind _kind): m_kind(_kind) {}
|
explicit MagicType(Kind _kind): m_kind(_kind) {}
|
||||||
|
/// Factory function for meta type
|
||||||
|
static std::shared_ptr<MagicType> metaType(TypePointer _type);
|
||||||
|
|
||||||
TypeResult binaryOperatorResult(Token, TypePointer const&) const override
|
TypeResult binaryOperatorResult(Token, TypePointer const&) const override
|
||||||
{
|
{
|
||||||
@ -1327,8 +1335,13 @@ public:
|
|||||||
|
|
||||||
Kind kind() const { return m_kind; }
|
Kind kind() const { return m_kind; }
|
||||||
|
|
||||||
|
TypePointer typeArgument() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Kind m_kind;
|
Kind m_kind;
|
||||||
|
/// Contract type used for contract metadata magic.
|
||||||
|
TypePointer m_typeArgument;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,22 +31,28 @@ using namespace dev::solidity;
|
|||||||
|
|
||||||
void Compiler::compileContract(
|
void Compiler::compileContract(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts,
|
std::map<ContractDefinition const*, shared_ptr<Compiler const>> const& _otherCompilers,
|
||||||
bytes const& _metadata
|
bytes const& _metadata
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize, m_optimizeRuns);
|
ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize, m_optimizeRuns);
|
||||||
runtimeCompiler.compileContract(_contract, _contracts);
|
runtimeCompiler.compileContract(_contract, _otherCompilers);
|
||||||
m_runtimeContext.appendAuxiliaryData(_metadata);
|
m_runtimeContext.appendAuxiliaryData(_metadata);
|
||||||
|
|
||||||
// This might modify m_runtimeContext because it can access runtime functions at
|
// This might modify m_runtimeContext because it can access runtime functions at
|
||||||
// creation time.
|
// creation time.
|
||||||
ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize, 1);
|
ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize, 1);
|
||||||
m_runtimeSub = creationCompiler.compileConstructor(_contract, _contracts);
|
m_runtimeSub = creationCompiler.compileConstructor(_contract, _otherCompilers);
|
||||||
|
|
||||||
m_context.optimise(m_optimize, m_optimizeRuns);
|
m_context.optimise(m_optimize, m_optimizeRuns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<eth::Assembly> Compiler::runtimeAssemblyPtr() const
|
||||||
|
{
|
||||||
|
solAssert(m_context.runtimeContext(), "");
|
||||||
|
return m_context.runtimeContext()->assemblyPtr();
|
||||||
|
}
|
||||||
|
|
||||||
eth::AssemblyItem Compiler::functionEntryLabel(FunctionDefinition const& _function) const
|
eth::AssemblyItem Compiler::functionEntryLabel(FunctionDefinition const& _function) const
|
||||||
{
|
{
|
||||||
return m_runtimeContext.functionEntryLabelIfExists(_function);
|
return m_runtimeContext.functionEntryLabelIfExists(_function);
|
||||||
|
@ -45,11 +45,15 @@ public:
|
|||||||
/// @arg _metadata contains the to be injected metadata CBOR
|
/// @arg _metadata contains the to be injected metadata CBOR
|
||||||
void compileContract(
|
void compileContract(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts,
|
std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers,
|
||||||
bytes const& _metadata
|
bytes const& _metadata
|
||||||
);
|
);
|
||||||
/// @returns Entire assembly.
|
/// @returns Entire assembly.
|
||||||
eth::Assembly const& assembly() const { return m_context.assembly(); }
|
eth::Assembly const& assembly() const { return m_context.assembly(); }
|
||||||
|
/// @returns Entire assembly as a shared pointer to non-const.
|
||||||
|
std::shared_ptr<eth::Assembly> assemblyPtr() const { return m_context.assemblyPtr(); }
|
||||||
|
/// @returns Runtime assembly.
|
||||||
|
std::shared_ptr<eth::Assembly> runtimeAssemblyPtr() const;
|
||||||
/// @returns The entire assembled object (with constructor).
|
/// @returns The entire assembled object (with constructor).
|
||||||
eth::LinkerObject assembledObject() const { return m_context.assembledObject(); }
|
eth::LinkerObject assembledObject() const { return m_context.assembledObject(); }
|
||||||
/// @returns Only the runtime object (without constructor).
|
/// @returns Only the runtime object (without constructor).
|
||||||
|
@ -167,11 +167,18 @@ unsigned CompilerContext::numberOfLocalVariables() const
|
|||||||
return m_localVariables.size();
|
return m_localVariables.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
eth::Assembly const& CompilerContext::compiledContract(const ContractDefinition& _contract) const
|
shared_ptr<eth::Assembly> CompilerContext::compiledContract(ContractDefinition const& _contract) const
|
||||||
{
|
{
|
||||||
auto ret = m_compiledContracts.find(&_contract);
|
auto ret = m_otherCompilers.find(&_contract);
|
||||||
solAssert(ret != m_compiledContracts.end(), "Compiled contract not found.");
|
solAssert(ret != m_otherCompilers.end(), "Compiled contract not found.");
|
||||||
return *ret->second;
|
return ret->second->assemblyPtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_ptr<eth::Assembly> CompilerContext::compiledContractRuntime(ContractDefinition const& _contract) const
|
||||||
|
{
|
||||||
|
auto ret = m_otherCompilers.find(&_contract);
|
||||||
|
solAssert(ret != m_otherCompilers.end(), "Compiled contract not found.");
|
||||||
|
return ret->second->runtimeAssemblyPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CompilerContext::isLocalVariable(Declaration const* _declaration) const
|
bool CompilerContext::isLocalVariable(Declaration const* _declaration) const
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
namespace dev {
|
namespace dev {
|
||||||
namespace solidity {
|
namespace solidity {
|
||||||
|
|
||||||
|
class Compiler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Context to be shared by all units that compile the same contract.
|
* Context to be shared by all units that compile the same contract.
|
||||||
@ -74,8 +75,9 @@ public:
|
|||||||
/// Returns the number of currently allocated local variables.
|
/// Returns the number of currently allocated local variables.
|
||||||
unsigned numberOfLocalVariables() const;
|
unsigned numberOfLocalVariables() const;
|
||||||
|
|
||||||
void setCompiledContracts(std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts) { m_compiledContracts = _contracts; }
|
void setOtherCompilers(std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers) { m_otherCompilers = _otherCompilers; }
|
||||||
eth::Assembly const& compiledContract(ContractDefinition const& _contract) const;
|
std::shared_ptr<eth::Assembly> compiledContract(ContractDefinition const& _contract) const;
|
||||||
|
std::shared_ptr<eth::Assembly> compiledContractRuntime(ContractDefinition const& _contract) const;
|
||||||
|
|
||||||
void setStackOffset(int _offset) { m_asm->setDeposit(_offset); }
|
void setStackOffset(int _offset) { m_asm->setDeposit(_offset); }
|
||||||
void adjustStackOffset(int _adjustment) { m_asm->adjustDeposit(_adjustment); }
|
void adjustStackOffset(int _adjustment) { m_asm->adjustDeposit(_adjustment); }
|
||||||
@ -222,15 +224,15 @@ public:
|
|||||||
void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, m_evmVersion, true, _runs); }
|
void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, m_evmVersion, true, _runs); }
|
||||||
|
|
||||||
/// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise.
|
/// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise.
|
||||||
CompilerContext* runtimeContext() { return m_runtimeContext; }
|
CompilerContext* runtimeContext() const { return m_runtimeContext; }
|
||||||
/// @returns the identifier of the runtime subroutine.
|
/// @returns the identifier of the runtime subroutine.
|
||||||
size_t runtimeSub() const { return m_runtimeSub; }
|
size_t runtimeSub() const { return m_runtimeSub; }
|
||||||
|
|
||||||
/// @returns a const reference to the underlying assembly.
|
/// @returns a const reference to the underlying assembly.
|
||||||
eth::Assembly const& assembly() const { return *m_asm; }
|
eth::Assembly const& assembly() const { return *m_asm; }
|
||||||
/// @returns non-const reference to the underlying assembly. Should be avoided in favour of
|
/// @returns a shared pointer to the assembly.
|
||||||
/// wrappers in this class.
|
/// Should be avoided except when adding sub-assemblies.
|
||||||
eth::Assembly& nonConstAssembly() { return *m_asm; }
|
std::shared_ptr<eth::Assembly> assemblyPtr() const { return m_asm; }
|
||||||
|
|
||||||
/// @arg _sourceCodes is the map of input files to source code strings
|
/// @arg _sourceCodes is the map of input files to source code strings
|
||||||
std::string assemblyString(StringMap const& _sourceCodes = StringMap()) const
|
std::string assemblyString(StringMap const& _sourceCodes = StringMap()) const
|
||||||
@ -307,7 +309,7 @@ private:
|
|||||||
/// Activated experimental features.
|
/// Activated experimental features.
|
||||||
std::set<ExperimentalFeature> m_experimentalFeatures;
|
std::set<ExperimentalFeature> m_experimentalFeatures;
|
||||||
/// Other already compiled contracts to be used in contract creation calls.
|
/// Other already compiled contracts to be used in contract creation calls.
|
||||||
std::map<ContractDefinition const*, eth::Assembly const*> m_compiledContracts;
|
std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> m_otherCompilers;
|
||||||
/// Storage offsets of state variables
|
/// Storage offsets of state variables
|
||||||
std::map<Declaration const*, std::pair<u256, unsigned>> m_stateVariables;
|
std::map<Declaration const*, std::pair<u256, unsigned>> m_stateVariables;
|
||||||
/// Offsets of local variables on the stack (relative to stack base).
|
/// Offsets of local variables on the stack (relative to stack base).
|
||||||
|
@ -1199,6 +1199,29 @@ void CompilerUtils::computeHashStatic()
|
|||||||
m_context << u256(32) << u256(0) << Instruction::KECCAK256;
|
m_context << u256(32) << u256(0) << Instruction::KECCAK256;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CompilerUtils::copyContractCodeToMemory(ContractDefinition const& contract, bool _creation)
|
||||||
|
{
|
||||||
|
string which = _creation ? "Creation" : "Runtime";
|
||||||
|
m_context.callLowLevelFunction(
|
||||||
|
"$copyContract" + which + "CodeToMemory_" + contract.type()->identifier(),
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
[&contract, _creation](CompilerContext& _context)
|
||||||
|
{
|
||||||
|
// copy the contract's code into memory
|
||||||
|
shared_ptr<eth::Assembly> assembly =
|
||||||
|
_creation ?
|
||||||
|
_context.compiledContract(contract) :
|
||||||
|
_context.compiledContractRuntime(contract);
|
||||||
|
// pushes size
|
||||||
|
auto subroutine = _context.addSubroutine(assembly);
|
||||||
|
_context << Instruction::DUP1 << subroutine;
|
||||||
|
_context << Instruction::DUP4 << Instruction::CODECOPY;
|
||||||
|
_context << Instruction::ADD;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void CompilerUtils::storeStringData(bytesConstRef _data)
|
void CompilerUtils::storeStringData(bytesConstRef _data)
|
||||||
{
|
{
|
||||||
//@todo provide both alternatives to the optimiser
|
//@todo provide both alternatives to the optimiser
|
||||||
|
@ -263,6 +263,13 @@ public:
|
|||||||
/// Appends code that computes the Keccak-256 hash of the topmost stack element of 32 byte type.
|
/// Appends code that computes the Keccak-256 hash of the topmost stack element of 32 byte type.
|
||||||
void computeHashStatic();
|
void computeHashStatic();
|
||||||
|
|
||||||
|
/// Apppends code that copies the code of the given contract to memory.
|
||||||
|
/// Stack pre: Memory position
|
||||||
|
/// Stack post: Updated memory position
|
||||||
|
/// @param creation if true, copies creation code, if false copies runtime code.
|
||||||
|
/// @note the contract has to be compiled already, so beware of cyclic dependencies!
|
||||||
|
void copyContractCodeToMemory(ContractDefinition const& contract, bool _creationCode);
|
||||||
|
|
||||||
/// Bytes we need to the start of call data.
|
/// Bytes we need to the start of call data.
|
||||||
/// - The size in bytes of the function (hash) identifier.
|
/// - The size in bytes of the function (hash) identifier.
|
||||||
static const unsigned dataStartOffset;
|
static const unsigned dataStartOffset;
|
||||||
|
@ -60,7 +60,7 @@ private:
|
|||||||
|
|
||||||
void ContractCompiler::compileContract(
|
void ContractCompiler::compileContract(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts
|
map<ContractDefinition const*, shared_ptr<Compiler const>> const& _otherCompilers
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, _contract);
|
CompilerContext::LocationSetter locationSetter(m_context, _contract);
|
||||||
@ -70,7 +70,7 @@ void ContractCompiler::compileContract(
|
|||||||
// This has to be the first code in the contract.
|
// This has to be the first code in the contract.
|
||||||
appendDelegatecallCheck();
|
appendDelegatecallCheck();
|
||||||
|
|
||||||
initializeContext(_contract, _contracts);
|
initializeContext(_contract, _otherCompilers);
|
||||||
// This generates the dispatch function for externally visible functions
|
// This generates the dispatch function for externally visible functions
|
||||||
// and adds the function to the compilation queue. Additionally internal functions,
|
// and adds the function to the compilation queue. Additionally internal functions,
|
||||||
// which are referenced directly or indirectly will be added.
|
// which are referenced directly or indirectly will be added.
|
||||||
@ -81,7 +81,7 @@ void ContractCompiler::compileContract(
|
|||||||
|
|
||||||
size_t ContractCompiler::compileConstructor(
|
size_t ContractCompiler::compileConstructor(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts
|
std::map<ContractDefinition const*, shared_ptr<Compiler const>> const& _otherCompilers
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, _contract);
|
CompilerContext::LocationSetter locationSetter(m_context, _contract);
|
||||||
@ -89,18 +89,18 @@ size_t ContractCompiler::compileConstructor(
|
|||||||
return deployLibrary(_contract);
|
return deployLibrary(_contract);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
initializeContext(_contract, _contracts);
|
initializeContext(_contract, _otherCompilers);
|
||||||
return packIntoContractCreator(_contract);
|
return packIntoContractCreator(_contract);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContractCompiler::initializeContext(
|
void ContractCompiler::initializeContext(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts
|
map<ContractDefinition const*, shared_ptr<Compiler const>> const& _otherCompilers
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
m_context.setExperimentalFeatures(_contract.sourceUnit().annotation().experimentalFeatures);
|
m_context.setExperimentalFeatures(_contract.sourceUnit().annotation().experimentalFeatures);
|
||||||
m_context.setCompiledContracts(_compiledContracts);
|
m_context.setOtherCompilers(_otherCompilers);
|
||||||
m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
|
m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
|
||||||
CompilerUtils(m_context).initialiseFreeMemoryPointer();
|
CompilerUtils(m_context).initialiseFreeMemoryPointer();
|
||||||
registerStateVariables(_contract);
|
registerStateVariables(_contract);
|
||||||
@ -716,7 +716,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
CodeGenerator::assemble(
|
CodeGenerator::assemble(
|
||||||
_inlineAssembly.operations(),
|
_inlineAssembly.operations(),
|
||||||
*_inlineAssembly.annotation().analysisInfo,
|
*_inlineAssembly.annotation().analysisInfo,
|
||||||
m_context.nonConstAssembly(),
|
*m_context.assemblyPtr(),
|
||||||
identifierAccess
|
identifierAccess
|
||||||
);
|
);
|
||||||
m_context.setStackOffset(startStackHeight);
|
m_context.setStackOffset(startStackHeight);
|
||||||
|
@ -49,13 +49,13 @@ public:
|
|||||||
|
|
||||||
void compileContract(
|
void compileContract(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts
|
std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers
|
||||||
);
|
);
|
||||||
/// Compiles the constructor part of the contract.
|
/// Compiles the constructor part of the contract.
|
||||||
/// @returns the identifier of the runtime sub-assembly.
|
/// @returns the identifier of the runtime sub-assembly.
|
||||||
size_t compileConstructor(
|
size_t compileConstructor(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts
|
std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers
|
||||||
);
|
);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -63,7 +63,7 @@ private:
|
|||||||
/// information about the contract like the AST annotations.
|
/// information about the contract like the AST annotations.
|
||||||
void initializeContext(
|
void initializeContext(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
std::map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts
|
std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers
|
||||||
);
|
);
|
||||||
/// Adds the code that is run at creation time. Should be run after exchanging the run-time context
|
/// Adds the code that is run at creation time. Should be run after exchanging the run-time context
|
||||||
/// with a new and initialized context. Adds the constructor code.
|
/// with a new and initialized context. Adds the constructor code.
|
||||||
|
@ -594,22 +594,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
ContractDefinition const* contract =
|
ContractDefinition const* contract =
|
||||||
&dynamic_cast<ContractType const&>(*function.returnParameterTypes().front()).contractDefinition();
|
&dynamic_cast<ContractType const&>(*function.returnParameterTypes().front()).contractDefinition();
|
||||||
m_context.callLowLevelFunction(
|
utils().fetchFreeMemoryPointer();
|
||||||
"$copyContractCreationCodeToMemory_" + contract->type()->identifier(),
|
utils().copyContractCodeToMemory(*contract, true);
|
||||||
0,
|
|
||||||
1,
|
|
||||||
[contract](CompilerContext& _context)
|
|
||||||
{
|
|
||||||
// copy the contract's code into memory
|
|
||||||
eth::Assembly const& assembly = _context.compiledContract(*contract);
|
|
||||||
CompilerUtils(_context).fetchFreeMemoryPointer();
|
|
||||||
// pushes size
|
|
||||||
auto subroutine = _context.addSubroutine(make_shared<eth::Assembly>(assembly));
|
|
||||||
_context << Instruction::DUP1 << subroutine;
|
|
||||||
_context << Instruction::DUP4 << Instruction::CODECOPY;
|
|
||||||
_context << Instruction::ADD;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
utils().abiEncode(argumentTypes, function.parameterTypes());
|
utils().abiEncode(argumentTypes, function.parameterTypes());
|
||||||
// now on stack: memory_end_ptr
|
// now on stack: memory_end_ptr
|
||||||
// need: size, offset, endowment
|
// need: size, offset, endowment
|
||||||
@ -1107,6 +1093,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
case FunctionType::Kind::GasLeft:
|
case FunctionType::Kind::GasLeft:
|
||||||
m_context << Instruction::GAS;
|
m_context << Instruction::GAS;
|
||||||
break;
|
break;
|
||||||
|
case FunctionType::Kind::MetaType:
|
||||||
|
// No code to generate.
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -1348,6 +1337,23 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
solAssert(false, "Gas has been removed.");
|
solAssert(false, "Gas has been removed.");
|
||||||
else if (member == "blockhash")
|
else if (member == "blockhash")
|
||||||
solAssert(false, "Blockhash has been removed.");
|
solAssert(false, "Blockhash has been removed.");
|
||||||
|
else if (member == "creationCode" || member == "runtimeCode")
|
||||||
|
{
|
||||||
|
TypePointer arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
|
||||||
|
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition();
|
||||||
|
utils().fetchFreeMemoryPointer();
|
||||||
|
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
|
||||||
|
utils().copyContractCodeToMemory(contract, member == "creationCode");
|
||||||
|
// Stack: start end
|
||||||
|
m_context.appendInlineAssembly(
|
||||||
|
Whiskers(R"({
|
||||||
|
mstore(start, sub(end, add(start, 0x20)))
|
||||||
|
mstore(<free>, end)
|
||||||
|
})")("free", to_string(CompilerUtils::freeMemoryPointer)).render(),
|
||||||
|
{"start", "end"}
|
||||||
|
);
|
||||||
|
m_context << Instruction::POP;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
solAssert(false, "Unknown magic member.");
|
solAssert(false, "Unknown magic member.");
|
||||||
break;
|
break;
|
||||||
|
@ -343,12 +343,12 @@ bool CompilerStack::compile()
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Only compile contracts individually which have been requested.
|
// Only compile contracts individually which have been requested.
|
||||||
map<ContractDefinition const*, eth::Assembly const*> compiledContracts;
|
map<ContractDefinition const*, shared_ptr<Compiler const>> otherCompilers;
|
||||||
for (Source const* source: m_sourceOrder)
|
for (Source const* source: m_sourceOrder)
|
||||||
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
|
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
|
||||||
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
|
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
|
||||||
if (isRequestedContract(*contract))
|
if (isRequestedContract(*contract))
|
||||||
compileContract(*contract, compiledContracts);
|
compileContract(*contract, otherCompilers);
|
||||||
m_stackState = CompilationSuccessful;
|
m_stackState = CompilationSuccessful;
|
||||||
this->link();
|
this->link();
|
||||||
return true;
|
return true;
|
||||||
@ -795,19 +795,15 @@ bool onlySafeExperimentalFeaturesActivated(set<ExperimentalFeature> const& featu
|
|||||||
|
|
||||||
void CompilerStack::compileContract(
|
void CompilerStack::compileContract(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts
|
map<ContractDefinition const*, shared_ptr<Compiler const>>& _otherCompilers
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
solAssert(m_stackState >= AnalysisSuccessful, "");
|
solAssert(m_stackState >= AnalysisSuccessful, "");
|
||||||
|
|
||||||
if (
|
if (_otherCompilers.count(&_contract) || !_contract.canBeDeployed())
|
||||||
_compiledContracts.count(&_contract) ||
|
|
||||||
!_contract.annotation().unimplementedFunctions.empty() ||
|
|
||||||
!_contract.constructorIsPublic()
|
|
||||||
)
|
|
||||||
return;
|
return;
|
||||||
for (auto const* dependency: _contract.annotation().contractDependencies)
|
for (auto const* dependency: _contract.annotation().contractDependencies)
|
||||||
compileContract(*dependency, _compiledContracts);
|
compileContract(*dependency, _otherCompilers);
|
||||||
|
|
||||||
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
|
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
|
||||||
|
|
||||||
@ -825,7 +821,7 @@ void CompilerStack::compileContract(
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Run optimiser and compile the contract.
|
// Run optimiser and compile the contract.
|
||||||
compiler->compileContract(_contract, _compiledContracts, cborEncodedMetadata);
|
compiler->compileContract(_contract, _otherCompilers, cborEncodedMetadata);
|
||||||
}
|
}
|
||||||
catch(eth::OptimizerException const&)
|
catch(eth::OptimizerException const&)
|
||||||
{
|
{
|
||||||
@ -852,7 +848,7 @@ void CompilerStack::compileContract(
|
|||||||
solAssert(false, "Assembly exception for deployed bytecode");
|
solAssert(false, "Assembly exception for deployed bytecode");
|
||||||
}
|
}
|
||||||
|
|
||||||
_compiledContracts[compiledContract.contract] = &compiler->assembly();
|
_otherCompilers[compiledContract.contract] = compiler;
|
||||||
}
|
}
|
||||||
|
|
||||||
CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const
|
CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const
|
||||||
|
@ -293,10 +293,12 @@ private:
|
|||||||
/// @returns true if the contract is requested to be compiled.
|
/// @returns true if the contract is requested to be compiled.
|
||||||
bool isRequestedContract(ContractDefinition const& _contract) const;
|
bool isRequestedContract(ContractDefinition const& _contract) const;
|
||||||
|
|
||||||
/// Compile a single contract and put the result in @a _compiledContracts.
|
/// Compile a single contract.
|
||||||
|
/// @param _otherCompilers provides access to compilers of other contracts, to get
|
||||||
|
/// their bytecode if needed. Only filled after they have been compiled.
|
||||||
void compileContract(
|
void compileContract(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
std::map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts
|
std::map<ContractDefinition const*, std::shared_ptr<Compiler const>>& _otherCompilers
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Links all the known library addresses in the available objects. Any unknown
|
/// Links all the known library addresses in the available objects. Any unknown
|
||||||
|
@ -1551,6 +1551,12 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
|
|||||||
nodeFactory.markEndPosition();
|
nodeFactory.markEndPosition();
|
||||||
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance());
|
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance());
|
||||||
break;
|
break;
|
||||||
|
case Token::Type:
|
||||||
|
// Inside expressions "type" is the name of a special, globally-available function.
|
||||||
|
nodeFactory.markEndPosition();
|
||||||
|
m_scanner->next();
|
||||||
|
expression = nodeFactory.createNode<Identifier>(make_shared<ASTString>("type"));
|
||||||
|
break;
|
||||||
case Token::LParen:
|
case Token::LParen:
|
||||||
case Token::LBrack:
|
case Token::LBrack:
|
||||||
{
|
{
|
||||||
|
@ -277,7 +277,7 @@
|
|||||||
"name" : "this",
|
"name" : "this",
|
||||||
"nodeType" : "Identifier",
|
"nodeType" : "Identifier",
|
||||||
"overloadedDeclarations" : [],
|
"overloadedDeclarations" : [],
|
||||||
"referencedDeclaration" : 65,
|
"referencedDeclaration" : 66,
|
||||||
"src" : "217:4:1",
|
"src" : "217:4:1",
|
||||||
"typeDescriptions" :
|
"typeDescriptions" :
|
||||||
{
|
{
|
||||||
|
@ -424,7 +424,7 @@
|
|||||||
[
|
[
|
||||||
null
|
null
|
||||||
],
|
],
|
||||||
"referencedDeclaration" : 65,
|
"referencedDeclaration" : 66,
|
||||||
"type" : "contract C",
|
"type" : "contract C",
|
||||||
"value" : "this"
|
"value" : "this"
|
||||||
},
|
},
|
||||||
|
@ -46,6 +46,7 @@ namespace dev
|
|||||||
{
|
{
|
||||||
namespace solidity
|
namespace solidity
|
||||||
{
|
{
|
||||||
|
class Contract;
|
||||||
namespace test
|
namespace test
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -84,7 +85,7 @@ eth::AssemblyItems compileContract(std::shared_ptr<CharStream> _sourceCode)
|
|||||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||||
{
|
{
|
||||||
Compiler compiler(dev::test::Options::get().evmVersion());
|
Compiler compiler(dev::test::Options::get().evmVersion());
|
||||||
compiler.compileContract(*contract, map<ContractDefinition const*, Assembly const*>{}, bytes());
|
compiler.compileContract(*contract, map<ContractDefinition const*, shared_ptr<Compiler const>>{}, bytes());
|
||||||
|
|
||||||
return compiler.runtimeAssemblyItems();
|
return compiler.runtimeAssemblyItems();
|
||||||
}
|
}
|
||||||
|
@ -14234,6 +14234,113 @@ BOOST_AUTO_TEST_CASE(base_access_to_function_type_variables)
|
|||||||
ABI_CHECK(callContractFunction("h()"), encodeArgs(2));
|
ABI_CHECK(callContractFunction("h()"), encodeArgs(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(code_access)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function lengths() public pure returns (bool) {
|
||||||
|
uint crLen = type(D).creationCode.length;
|
||||||
|
uint runLen = type(D).runtimeCode.length;
|
||||||
|
require(runLen < crLen);
|
||||||
|
require(crLen >= 0x20);
|
||||||
|
require(runLen >= 0x20);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
function creation() public pure returns (bytes memory) {
|
||||||
|
return type(D).creationCode;
|
||||||
|
}
|
||||||
|
function runtime() public pure returns (bytes memory) {
|
||||||
|
return type(D).runtimeCode;
|
||||||
|
}
|
||||||
|
function runtimeAllocCheck() public pure returns (bytes memory) {
|
||||||
|
uint[] memory a = new uint[](2);
|
||||||
|
bytes memory c = type(D).runtimeCode;
|
||||||
|
uint[] memory b = new uint[](2);
|
||||||
|
a[0] = 0x1111;
|
||||||
|
a[1] = 0x2222;
|
||||||
|
b[0] = 0x3333;
|
||||||
|
b[1] = 0x4444;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contract D {
|
||||||
|
function f() public pure returns (uint) { return 7; }
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
ABI_CHECK(callContractFunction("lengths()"), encodeArgs(true));
|
||||||
|
bytes codeCreation = callContractFunction("creation()");
|
||||||
|
bytes codeRuntime1 = callContractFunction("runtime()");
|
||||||
|
bytes codeRuntime2 = callContractFunction("runtimeAllocCheck()");
|
||||||
|
ABI_CHECK(codeRuntime1, codeRuntime2);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(code_access_create)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function test() public returns (uint) {
|
||||||
|
bytes memory c = type(D).creationCode;
|
||||||
|
D d;
|
||||||
|
assembly {
|
||||||
|
d := create(0, add(c, 0x20), mload(c))
|
||||||
|
}
|
||||||
|
return d.f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contract D {
|
||||||
|
uint x;
|
||||||
|
constructor() public { x = 7; }
|
||||||
|
function f() public view returns (uint) { return x; }
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
ABI_CHECK(callContractFunction("test()"), encodeArgs(7));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(code_access_content)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function testRuntime() public returns (bool) {
|
||||||
|
D d = new D();
|
||||||
|
bytes32 runtimeHash = keccak256(type(D).runtimeCode);
|
||||||
|
bytes32 otherHash;
|
||||||
|
uint size;
|
||||||
|
assembly {
|
||||||
|
size := extcodesize(d)
|
||||||
|
extcodecopy(d, mload(0x40), 0, size)
|
||||||
|
otherHash := keccak256(mload(0x40), size)
|
||||||
|
}
|
||||||
|
require(size == type(D).runtimeCode.length);
|
||||||
|
require(runtimeHash == otherHash);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
function testCreation() public returns (bool) {
|
||||||
|
D d = new D();
|
||||||
|
bytes32 creationHash = keccak256(type(D).creationCode);
|
||||||
|
require(creationHash == d.x());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contract D {
|
||||||
|
bytes32 public x;
|
||||||
|
constructor() public {
|
||||||
|
bytes32 codeHash;
|
||||||
|
assembly {
|
||||||
|
let size := codesize()
|
||||||
|
codecopy(mload(0x40), 0, size)
|
||||||
|
codeHash := keccak256(mload(0x40), size)
|
||||||
|
}
|
||||||
|
x = codeHash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
ABI_CHECK(callContractFunction("testRuntime()"), encodeArgs(true));
|
||||||
|
ABI_CHECK(callContractFunction("testCreation()"), encodeArgs(true));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -539,7 +539,6 @@ BOOST_AUTO_TEST_CASE(keyword_is_reserved)
|
|||||||
"supports",
|
"supports",
|
||||||
"switch",
|
"switch",
|
||||||
"try",
|
"try",
|
||||||
"type",
|
|
||||||
"typedef",
|
"typedef",
|
||||||
"typeof",
|
"typeof",
|
||||||
"unchecked"
|
"unchecked"
|
||||||
|
12
test/libsolidity/syntaxTests/metaTypes/codeAccess.sol
Normal file
12
test/libsolidity/syntaxTests/metaTypes/codeAccess.sol
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
contract Test {
|
||||||
|
function creationOther() public pure returns (bytes memory) {
|
||||||
|
return type(Other).creationCode;
|
||||||
|
}
|
||||||
|
function runtimeOther() public pure returns (bytes memory) {
|
||||||
|
return type(Other).runtimeCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contract Other {
|
||||||
|
function f(uint) public pure returns (uint) {}
|
||||||
|
}
|
||||||
|
// ----
|
@ -0,0 +1,10 @@
|
|||||||
|
contract Test {
|
||||||
|
function creationOther() public pure returns (bytes memory) {
|
||||||
|
return type(Other).creationCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contract Other {
|
||||||
|
function f(uint) public returns (uint);
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (97-121): Member "creationCode" not found or not visible after argument-dependent lookup in type(contract Other).
|
@ -0,0 +1,10 @@
|
|||||||
|
contract Test {
|
||||||
|
function runtime() public pure returns (bytes memory) {
|
||||||
|
return type(Other).runtimeCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contract Other {
|
||||||
|
function f(uint) public returns (uint);
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (91-114): Member "runtimeCode" not found or not visible after argument-dependent lookup in type(contract Other).
|
22
test/libsolidity/syntaxTests/metaTypes/codeAccessBase.sol
Normal file
22
test/libsolidity/syntaxTests/metaTypes/codeAccessBase.sol
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
contract Base {
|
||||||
|
function f() public pure returns (uint) {}
|
||||||
|
}
|
||||||
|
contract Test is Base {
|
||||||
|
function creation() public pure returns (bytes memory) {
|
||||||
|
return type(Test).creationCode;
|
||||||
|
}
|
||||||
|
function runtime() public pure returns (bytes memory) {
|
||||||
|
return type(Test).runtimeCode;
|
||||||
|
}
|
||||||
|
function creationBase() public pure returns (bytes memory) {
|
||||||
|
return type(Base).creationCode;
|
||||||
|
}
|
||||||
|
function runtimeBase() public pure returns (bytes memory) {
|
||||||
|
return type(Base).runtimeCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (165-188): Circular reference for contract code access.
|
||||||
|
// TypeError: (271-293): Circular reference for contract code access.
|
||||||
|
// TypeError: (381-404): Circular reference for contract code access.
|
||||||
|
// TypeError: (491-513): Circular reference for contract code access.
|
12
test/libsolidity/syntaxTests/metaTypes/codeAccessCyclic.sol
Normal file
12
test/libsolidity/syntaxTests/metaTypes/codeAccessCyclic.sol
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
contract A {
|
||||||
|
function f() public pure {
|
||||||
|
type(B).runtimeCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contract B {
|
||||||
|
function f() public pure {
|
||||||
|
type(A).runtimeCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (133-152): Circular reference for contract code access.
|
@ -0,0 +1,7 @@
|
|||||||
|
contract Test {
|
||||||
|
bytes constant c = type(B).creationCode;
|
||||||
|
bytes constant r = type(B).runtimeCode;
|
||||||
|
|
||||||
|
}
|
||||||
|
contract B { function f() public pure {} }
|
||||||
|
// ----
|
12
test/libsolidity/syntaxTests/metaTypes/codeAccessLibrary.sol
Normal file
12
test/libsolidity/syntaxTests/metaTypes/codeAccessLibrary.sol
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
contract Test {
|
||||||
|
function creationOther() public pure returns (bytes memory) {
|
||||||
|
return type(Library).creationCode;
|
||||||
|
}
|
||||||
|
function runtime() public pure returns (bytes memory) {
|
||||||
|
return type(Library).runtimeCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contract Library {
|
||||||
|
function f(uint) public pure returns (uint) {}
|
||||||
|
}
|
||||||
|
// ----
|
10
test/libsolidity/syntaxTests/metaTypes/codeIsNoLValue.sol
Normal file
10
test/libsolidity/syntaxTests/metaTypes/codeIsNoLValue.sol
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
contract Test {
|
||||||
|
function f() public pure {
|
||||||
|
type(C).creationCode = new bytes(6);
|
||||||
|
type(C).runtimeCode = new bytes(6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contract C {}
|
||||||
|
// ----
|
||||||
|
// TypeError: (55-75): Expression has to be an lvalue.
|
||||||
|
// TypeError: (100-119): Expression has to be an lvalue.
|
7
test/libsolidity/syntaxTests/metaTypes/noArgForType.sol
Normal file
7
test/libsolidity/syntaxTests/metaTypes/noArgForType.sol
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
contract Test {
|
||||||
|
function creation() public pure returns (bytes memory) {
|
||||||
|
type();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (85-91): This function takes one argument, but 0 were provided.
|
@ -0,0 +1,17 @@
|
|||||||
|
contract Test {
|
||||||
|
function f() public pure returns (uint) {
|
||||||
|
return type(C).runtimeCode.length +
|
||||||
|
type(D).runtimeCode.length +
|
||||||
|
type(C).creationCode.length +
|
||||||
|
type(D).creationCode.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contract C {
|
||||||
|
constructor() public { assembly {} }
|
||||||
|
}
|
||||||
|
contract D is C {
|
||||||
|
constructor() public {}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (77-96): The constructor of the contract (or its base) uses inline assembly. Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode.
|
||||||
|
// Warning: (118-137): The constructor of the contract (or its base) uses inline assembly. Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode.
|
@ -0,0 +1,7 @@
|
|||||||
|
contract Test {
|
||||||
|
function creation() public pure returns (bytes memory) {
|
||||||
|
type(1, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (85-95): This function takes one argument, but 2 were provided.
|
@ -0,0 +1,3 @@
|
|||||||
|
contract type { }
|
||||||
|
// ----
|
||||||
|
// ParserError: (9-13): Expected identifier but got 'type'
|
@ -0,0 +1,6 @@
|
|||||||
|
contract Test {
|
||||||
|
function type() public pure {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// ParserError: (29-33): Expected identifier but got 'type'
|
@ -0,0 +1,6 @@
|
|||||||
|
contract Test {
|
||||||
|
function f(uint type) public pure {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// ParserError: (36-40): Expected ',' but got 'type'
|
@ -0,0 +1,5 @@
|
|||||||
|
contract Test {
|
||||||
|
uint type;
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// ParserError: (25-29): Expected identifier but got 'type'
|
@ -0,0 +1,7 @@
|
|||||||
|
contract Test {
|
||||||
|
function f() public pure {
|
||||||
|
uint type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// ParserError: (60-64): Expected ';' but got 'type'
|
@ -0,0 +1,6 @@
|
|||||||
|
contract Test {
|
||||||
|
function f() public pure returns (bytes memory) {
|
||||||
|
type(Test);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
8
test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol
Normal file
8
test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
contract Test {
|
||||||
|
function f() public pure {
|
||||||
|
type(type(type(Test)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (65-75): Invalid type for argument in function call. Contract type required, but type(contract Test) provided.
|
||||||
|
// TypeError: (60-76): Invalid type for argument in function call. Contract type required, but tuple() provided.
|
@ -0,0 +1,9 @@
|
|||||||
|
contract Test {
|
||||||
|
struct S { uint x; }
|
||||||
|
function f() public pure {
|
||||||
|
// Unsupported for now, but might be supported in the future
|
||||||
|
type(S);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (154-155): Invalid type for argument in function call. Contract type required, but type(struct Test.S) provided.
|
Loading…
Reference in New Issue
Block a user