aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchriseth <chris@ethereum.org>2019-01-18 07:16:06 +0800
committerGitHub <noreply@github.com>2019-01-18 07:16:06 +0800
commit2ec997e697e306dd54165aad365406ee88c534cb (patch)
tree5e943e23d38e332de3eedd33be11f2cf9df5fd69
parent0711873a2f13d7b0f27e268fcd0a7683665f339d (diff)
parent2a92403690a4998ab097503231ac39f854b9c76c (diff)
downloaddexon-solidity-2ec997e697e306dd54165aad365406ee88c534cb.tar.gz
dexon-solidity-2ec997e697e306dd54165aad365406ee88c534cb.tar.zst
dexon-solidity-2ec997e697e306dd54165aad365406ee88c534cb.zip
Merge pull request #5775 from ethereum/codeAccess
Provide access to code of contract types.
-rw-r--r--Changelog.md1
-rw-r--r--docs/miscellaneous.rst4
-rw-r--r--docs/types/value-types.rst3
-rw-r--r--docs/units-and-global-variables.rst30
-rw-r--r--liblangutil/Token.h2
-rw-r--r--libsolidity/analysis/GlobalContext.cpp9
-rw-r--r--libsolidity/analysis/StaticAnalyzer.cpp62
-rw-r--r--libsolidity/analysis/StaticAnalyzer.h9
-rw-r--r--libsolidity/analysis/TypeChecker.cpp51
-rw-r--r--libsolidity/analysis/TypeChecker.h2
-rw-r--r--libsolidity/analysis/ViewPureChecker.cpp4
-rw-r--r--libsolidity/ast/AST.cpp5
-rw-r--r--libsolidity/ast/AST.h4
-rw-r--r--libsolidity/ast/Types.cpp50
-rw-r--r--libsolidity/ast/Types.h19
-rw-r--r--libsolidity/codegen/Compiler.cpp12
-rw-r--r--libsolidity/codegen/Compiler.h6
-rw-r--r--libsolidity/codegen/CompilerContext.cpp15
-rw-r--r--libsolidity/codegen/CompilerContext.h16
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp23
-rw-r--r--libsolidity/codegen/CompilerUtils.h7
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp14
-rw-r--r--libsolidity/codegen/ContractCompiler.h6
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp38
-rw-r--r--libsolidity/interface/CompilerStack.cpp18
-rw-r--r--libsolidity/interface/CompilerStack.h6
-rw-r--r--libsolidity/parsing/Parser.cpp6
-rw-r--r--test/libsolidity/ASTJSON/address_payable.json2
-rw-r--r--test/libsolidity/ASTJSON/address_payable_legacy.json2
-rw-r--r--test/libsolidity/Assembly.cpp3
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp107
-rw-r--r--test/libsolidity/SolidityParser.cpp1
-rw-r--r--test/libsolidity/syntaxTests/metaTypes/codeAccess.sol12
-rw-r--r--test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractCreation.sol10
-rw-r--r--test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractRuntime.sol10
-rw-r--r--test/libsolidity/syntaxTests/metaTypes/codeAccessBase.sol22
-rw-r--r--test/libsolidity/syntaxTests/metaTypes/codeAccessCyclic.sol12
-rw-r--r--test/libsolidity/syntaxTests/metaTypes/codeAccessIsConstant.sol7
-rw-r--r--test/libsolidity/syntaxTests/metaTypes/codeAccessLibrary.sol12
-rw-r--r--test/libsolidity/syntaxTests/metaTypes/codeIsNoLValue.sol10
-rw-r--r--test/libsolidity/syntaxTests/metaTypes/noArgForType.sol7
-rw-r--r--test/libsolidity/syntaxTests/metaTypes/runtimeCodeWarningAssembly.sol17
-rw-r--r--test/libsolidity/syntaxTests/metaTypes/tooManyArgsForType.sol7
-rw-r--r--test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierContractName.sol3
-rw-r--r--test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierFunction.sol6
-rw-r--r--test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierParameter.sol6
-rw-r--r--test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierStateVariable.sol5
-rw-r--r--test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierVariable.sol7
-rw-r--r--test/libsolidity/syntaxTests/metaTypes/typeOfContract.sol6
-rw-r--r--test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol8
-rw-r--r--test/libsolidity/syntaxTests/metaTypes/unsupportedArgForType.sol9
51 files changed, 641 insertions, 72 deletions
diff --git a/Changelog.md b/Changelog.md
index dc6beb44..bdfd9746 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,6 +1,7 @@
### 0.5.3 (unreleased)
Language Features:
+ * Provide access to creation and runtime code of contracts via ``type(C).creationCode`` / ``type(C).runtimeCode``.
Compiler Features:
diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst
index 5a6f3875..69124c77 100644
--- a/docs/miscellaneous.rst
+++ b/docs/miscellaneous.rst
@@ -385,6 +385,8 @@ Global Variables
- ``<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>.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::
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``,
``define``, ``final``, ``immutable``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``,
``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``.
Language Grammar
diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst
index 077587dd..09db1423 100644
--- a/docs/types/value-types.rst
+++ b/docs/types/value-types.rst
@@ -329,6 +329,9 @@ Contracts do not support any operators.
The members of contract types are the external functions of the contract
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
Fixed-size byte arrays
diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst
index c47f257b..ce7706c1 100644
--- a/docs/units-and-global-variables.rst
+++ b/docs/units-and-global-variables.rst
@@ -244,3 +244,33 @@ Furthermore, all functions of the current contract are callable directly includi
.. note::
Prior to version 0.5.0, there was a function called ``suicide`` with the same
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.
diff --git a/liblangutil/Token.h b/liblangutil/Token.h
index f832fdf7..b3a1acb1 100644
--- a/liblangutil/Token.h
+++ b/liblangutil/Token.h
@@ -180,6 +180,7 @@ namespace langutil
K(CallData, "calldata", 0) \
K(Struct, "struct", 0) \
K(Throw, "throw", 0) \
+ K(Type, "type", 0) \
K(Using, "using", 0) \
K(Var, "var", 0) \
K(View, "view", 0) \
@@ -256,7 +257,6 @@ namespace langutil
K(Supports, "supports", 0) \
K(Switch, "switch", 0) \
K(Try, "try", 0) \
- K(Type, "type", 0) \
K(Typedef, "typedef", 0) \
K(TypeOf, "typeof", 0) \
K(Unchecked, "unchecked", 0) \
diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp
index cd5fe07d..2276d783 100644
--- a/libsolidity/analysis/GlobalContext.cpp
+++ b/libsolidity/analysis/GlobalContext.cpp
@@ -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>("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>("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
+ )),
})
{
}
diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp
index aaaa4f9f..11ed6a4f 100644
--- a/libsolidity/analysis/StaticAnalyzer.cpp
+++ b/libsolidity/analysis/StaticAnalyzer.cpp
@@ -32,6 +32,56 @@ using namespace dev;
using namespace langutil;
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)
{
_sourceUnit.accept(*this);
@@ -152,6 +202,18 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
_memberAccess.location(),
"\"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")
diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h
index ff33fa3a..3daf83b3 100644
--- a/libsolidity/analysis/StaticAnalyzer.h
+++ b/libsolidity/analysis/StaticAnalyzer.h
@@ -38,6 +38,8 @@ namespace dev
namespace solidity
{
+class ConstructorUsesAssembly;
+
/**
* The module that performs static analysis on the AST.
@@ -49,7 +51,8 @@ class StaticAnalyzer: private ASTConstVisitor
{
public:
/// @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.
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
@@ -85,6 +88,10 @@ private:
/// when traversing.
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;
/// Flag that indicates a constructor.
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 7fa9933c..d038233c 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -199,6 +199,38 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c
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)
{
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
@@ -1831,6 +1863,9 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
returnTypes = functionType->returnParameterTypes();
break;
}
+ case FunctionType::Kind::MetaType:
+ returnTypes = typeCheckMetaTypeFunctionAndRetrieveReturnType(_functionCall);
+ break;
default:
{
typeCheckFunctionCall(_functionCall, functionType);
@@ -2071,8 +2106,24 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
if (tt->actualType()->category() == Type::Category::Enum)
annotation.isPure = true;
if (auto magicType = dynamic_cast<MagicType const*>(exprType.get()))
+ {
if (magicType->kind() == MagicType::Kind::ABI)
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;
}
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index b60c571a..d5f3645c 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -81,6 +81,8 @@ private:
bool _abiEncoderV2
);
+ TypePointers typeCheckMetaTypeFunctionAndRetrieveReturnType(FunctionCall const& _functionCall);
+
/// Performs type checks and determines result types for type conversion FunctionCall nodes.
TypePointer typeCheckTypeConversionAndRetrieveReturnType(
FunctionCall const& _functionCall
diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp
index eb019481..7df7ac17 100644
--- a/libsolidity/analysis/ViewPureChecker.cpp
+++ b/libsolidity/analysis/ViewPureChecker.cpp
@@ -338,7 +338,9 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
{MagicType::Kind::ABI, "encodeWithSignature"},
{MagicType::Kind::Block, "blockhash"},
{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{
{MagicType::Kind::Message, "value"}
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
index f379d758..4cf8b1e8 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -138,6 +138,11 @@ bool ContractDefinition::constructorIsPublic() const
return !f || f->isPublic();
}
+bool ContractDefinition::canBeDeployed() const
+{
+ return constructorIsPublic() && annotation().unimplementedFunctions.empty();
+}
+
FunctionDefinition const* ContractDefinition::fallbackFunction() const
{
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index 9ac065ea..cd986050 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -408,6 +408,10 @@ public:
FunctionDefinition const* constructor() const;
/// @returns true iff the constructor of this contract is public (or non-existing).
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.
FunctionDefinition const* fallbackFunction() const;
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index cc978b4a..081c7fb6 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -2626,6 +2626,7 @@ string FunctionType::richIdentifier() const
case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break;
case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break;
case Kind::ABIDecode: id += "abidecode"; break;
+ case Kind::MetaType: id += "metatype"; break;
}
id += "_" + stateMutabilityToString(m_stateMutability);
id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes);
@@ -3037,7 +3038,8 @@ bool FunctionType::isPure() const
m_kind == Kind::ABIEncodePacked ||
m_kind == Kind::ABIEncodeWithSelector ||
m_kind == Kind::ABIEncodeWithSignature ||
- m_kind == Kind::ABIDecode;
+ m_kind == Kind::ABIDecode ||
+ m_kind == Kind::MetaType;
}
TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
@@ -3305,6 +3307,14 @@ string ModuleType::toString(bool) const
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
{
switch (m_kind)
@@ -3317,6 +3327,9 @@ string MagicType::richIdentifier() const
return "t_magic_transaction";
case Kind::ABI:
return "t_magic_abi";
+ case Kind::MetaType:
+ solAssert(m_typeArgument, "");
+ return "t_magic_meta_type_" + m_typeArgument->richIdentifier();
}
return "";
}
@@ -3403,12 +3416,27 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
StateMutability::Pure
)}
});
- default:
- solAssert(false, "Unknown kind of magic.");
+ case Kind::MetaType:
+ {
+ 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)
{
@@ -3420,7 +3448,17 @@ string MagicType::toString(bool) const
return "tx";
case Kind::ABI:
return "abi";
- default:
- solAssert(false, "Unknown kind of magic.");
+ case Kind::MetaType:
+ 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;
}
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index bee00661..53109de1 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -989,6 +989,7 @@ public:
ABIEncodeWithSignature,
ABIDecode,
GasLeft, ///< gasleft()
+ MetaType ///< type(...)
};
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
- * (it always references a global singleton by name).
+ * Special type for magic variables (block, msg, tx, type(...)), similar to a struct but without any reference.
*/
class MagicType: public Type
{
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; }
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
{
@@ -1327,8 +1335,13 @@ public:
Kind kind() const { return m_kind; }
+ TypePointer typeArgument() const;
+
private:
Kind m_kind;
+ /// Contract type used for contract metadata magic.
+ TypePointer m_typeArgument;
+
};
/**
diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp
index a22e6e9d..72efed33 100644
--- a/libsolidity/codegen/Compiler.cpp
+++ b/libsolidity/codegen/Compiler.cpp
@@ -31,22 +31,28 @@ using namespace dev::solidity;
void Compiler::compileContract(
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
)
{
ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize, m_optimizeRuns);
- runtimeCompiler.compileContract(_contract, _contracts);
+ runtimeCompiler.compileContract(_contract, _otherCompilers);
m_runtimeContext.appendAuxiliaryData(_metadata);
// This might modify m_runtimeContext because it can access runtime functions at
// creation time.
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);
}
+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
{
return m_runtimeContext.functionEntryLabelIfExists(_function);
diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h
index 784d7f8c..c21de96d 100644
--- a/libsolidity/codegen/Compiler.h
+++ b/libsolidity/codegen/Compiler.h
@@ -45,11 +45,15 @@ public:
/// @arg _metadata contains the to be injected metadata CBOR
void compileContract(
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
);
/// @returns Entire 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).
eth::LinkerObject assembledObject() const { return m_context.assembledObject(); }
/// @returns Only the runtime object (without constructor).
diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
index be681b2e..861b1c98 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -167,11 +167,18 @@ unsigned CompilerContext::numberOfLocalVariables() const
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);
- solAssert(ret != m_compiledContracts.end(), "Compiled contract not found.");
- return *ret->second;
+ auto ret = m_otherCompilers.find(&_contract);
+ solAssert(ret != m_otherCompilers.end(), "Compiled contract not found.");
+ 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
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index dedcd95f..e5ddfbc5 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -41,6 +41,7 @@
namespace dev {
namespace solidity {
+class Compiler;
/**
* 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.
unsigned numberOfLocalVariables() const;
- void setCompiledContracts(std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts) { m_compiledContracts = _contracts; }
- eth::Assembly const& compiledContract(ContractDefinition const& _contract) const;
+ void setOtherCompilers(std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers) { m_otherCompilers = _otherCompilers; }
+ 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 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); }
/// @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.
size_t runtimeSub() const { return m_runtimeSub; }
/// @returns a const reference to the underlying assembly.
eth::Assembly const& assembly() const { return *m_asm; }
- /// @returns non-const reference to the underlying assembly. Should be avoided in favour of
- /// wrappers in this class.
- eth::Assembly& nonConstAssembly() { return *m_asm; }
+ /// @returns a shared pointer to the assembly.
+ /// Should be avoided except when adding sub-assemblies.
+ std::shared_ptr<eth::Assembly> assemblyPtr() const { return m_asm; }
/// @arg _sourceCodes is the map of input files to source code strings
std::string assemblyString(StringMap const& _sourceCodes = StringMap()) const
@@ -307,7 +309,7 @@ private:
/// Activated experimental features.
std::set<ExperimentalFeature> m_experimentalFeatures;
/// 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
std::map<Declaration const*, std::pair<u256, unsigned>> m_stateVariables;
/// Offsets of local variables on the stack (relative to stack base).
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index bbc703c7..6cfb0777 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -1199,6 +1199,29 @@ void CompilerUtils::computeHashStatic()
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)
{
//@todo provide both alternatives to the optimiser
diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h
index 7e4f47ba..6bde2e8b 100644
--- a/libsolidity/codegen/CompilerUtils.h
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -263,6 +263,13 @@ public:
/// Appends code that computes the Keccak-256 hash of the topmost stack element of 32 byte type.
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.
/// - The size in bytes of the function (hash) identifier.
static const unsigned dataStartOffset;
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index b051d260..f843e07a 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -60,7 +60,7 @@ private:
void ContractCompiler::compileContract(
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);
@@ -70,7 +70,7 @@ void ContractCompiler::compileContract(
// This has to be the first code in the contract.
appendDelegatecallCheck();
- initializeContext(_contract, _contracts);
+ initializeContext(_contract, _otherCompilers);
// This generates the dispatch function for externally visible functions
// and adds the function to the compilation queue. Additionally internal functions,
// which are referenced directly or indirectly will be added.
@@ -81,7 +81,7 @@ void ContractCompiler::compileContract(
size_t ContractCompiler::compileConstructor(
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);
@@ -89,18 +89,18 @@ size_t ContractCompiler::compileConstructor(
return deployLibrary(_contract);
else
{
- initializeContext(_contract, _contracts);
+ initializeContext(_contract, _otherCompilers);
return packIntoContractCreator(_contract);
}
}
void ContractCompiler::initializeContext(
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.setCompiledContracts(_compiledContracts);
+ m_context.setOtherCompilers(_otherCompilers);
m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
CompilerUtils(m_context).initialiseFreeMemoryPointer();
registerStateVariables(_contract);
@@ -716,7 +716,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
CodeGenerator::assemble(
_inlineAssembly.operations(),
*_inlineAssembly.annotation().analysisInfo,
- m_context.nonConstAssembly(),
+ *m_context.assemblyPtr(),
identifierAccess
);
m_context.setStackOffset(startStackHeight);
diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h
index 40871f0d..9ab006f6 100644
--- a/libsolidity/codegen/ContractCompiler.h
+++ b/libsolidity/codegen/ContractCompiler.h
@@ -49,13 +49,13 @@ public:
void compileContract(
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.
/// @returns the identifier of the runtime sub-assembly.
size_t compileConstructor(
ContractDefinition const& _contract,
- std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts
+ std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers
);
private:
@@ -63,7 +63,7 @@ private:
/// information about the contract like the AST annotations.
void initializeContext(
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
/// with a new and initialized context. Adds the constructor code.
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index be2709ae..e6bb163d 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -594,22 +594,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
}
ContractDefinition const* contract =
&dynamic_cast<ContractType const&>(*function.returnParameterTypes().front()).contractDefinition();
- m_context.callLowLevelFunction(
- "$copyContractCreationCodeToMemory_" + contract->type()->identifier(),
- 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().fetchFreeMemoryPointer();
+ utils().copyContractCodeToMemory(*contract, true);
utils().abiEncode(argumentTypes, function.parameterTypes());
// now on stack: memory_end_ptr
// need: size, offset, endowment
@@ -1107,6 +1093,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
case FunctionType::Kind::GasLeft:
m_context << Instruction::GAS;
break;
+ case FunctionType::Kind::MetaType:
+ // No code to generate.
+ break;
}
}
return false;
@@ -1348,6 +1337,23 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
solAssert(false, "Gas has been removed.");
else if (member == "blockhash")
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
solAssert(false, "Unknown magic member.");
break;
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index f9d889e7..9e4da62d 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -343,12 +343,12 @@ bool CompilerStack::compile()
return false;
// 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 (ASTPointer<ASTNode> const& node: source->ast->nodes())
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
if (isRequestedContract(*contract))
- compileContract(*contract, compiledContracts);
+ compileContract(*contract, otherCompilers);
m_stackState = CompilationSuccessful;
this->link();
return true;
@@ -795,19 +795,15 @@ bool onlySafeExperimentalFeaturesActivated(set<ExperimentalFeature> const& featu
void CompilerStack::compileContract(
ContractDefinition const& _contract,
- map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts
+ map<ContractDefinition const*, shared_ptr<Compiler const>>& _otherCompilers
)
{
solAssert(m_stackState >= AnalysisSuccessful, "");
- if (
- _compiledContracts.count(&_contract) ||
- !_contract.annotation().unimplementedFunctions.empty() ||
- !_contract.constructorIsPublic()
- )
+ if (_otherCompilers.count(&_contract) || !_contract.canBeDeployed())
return;
for (auto const* dependency: _contract.annotation().contractDependencies)
- compileContract(*dependency, _compiledContracts);
+ compileContract(*dependency, _otherCompilers);
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
@@ -825,7 +821,7 @@ void CompilerStack::compileContract(
try
{
// Run optimiser and compile the contract.
- compiler->compileContract(_contract, _compiledContracts, cborEncodedMetadata);
+ compiler->compileContract(_contract, _otherCompilers, cborEncodedMetadata);
}
catch(eth::OptimizerException const&)
{
@@ -852,7 +848,7 @@ void CompilerStack::compileContract(
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
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index 81d5009f..79927850 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -293,10 +293,12 @@ private:
/// @returns true if the contract is requested to be compiled.
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(
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
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index 8a6bc343..35476a76 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -1551,6 +1551,12 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
nodeFactory.markEndPosition();
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance());
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::LBrack:
{
diff --git a/test/libsolidity/ASTJSON/address_payable.json b/test/libsolidity/ASTJSON/address_payable.json
index 0f30e8e8..c8b71628 100644
--- a/test/libsolidity/ASTJSON/address_payable.json
+++ b/test/libsolidity/ASTJSON/address_payable.json
@@ -277,7 +277,7 @@
"name" : "this",
"nodeType" : "Identifier",
"overloadedDeclarations" : [],
- "referencedDeclaration" : 65,
+ "referencedDeclaration" : 66,
"src" : "217:4:1",
"typeDescriptions" :
{
diff --git a/test/libsolidity/ASTJSON/address_payable_legacy.json b/test/libsolidity/ASTJSON/address_payable_legacy.json
index dd8a5582..8abebce0 100644
--- a/test/libsolidity/ASTJSON/address_payable_legacy.json
+++ b/test/libsolidity/ASTJSON/address_payable_legacy.json
@@ -424,7 +424,7 @@
[
null
],
- "referencedDeclaration" : 65,
+ "referencedDeclaration" : 66,
"type" : "contract C",
"value" : "this"
},
diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp
index baa9bff1..b5a1797b 100644
--- a/test/libsolidity/Assembly.cpp
+++ b/test/libsolidity/Assembly.cpp
@@ -46,6 +46,7 @@ namespace dev
{
namespace solidity
{
+class Contract;
namespace test
{
@@ -84,7 +85,7 @@ eth::AssemblyItems compileContract(std::shared_ptr<CharStream> _sourceCode)
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
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();
}
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index 8d219d16..38be5ae7 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -14234,6 +14234,113 @@ BOOST_AUTO_TEST_CASE(base_access_to_function_type_variables)
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()
}
diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp
index a33c6134..413a8c9c 100644
--- a/test/libsolidity/SolidityParser.cpp
+++ b/test/libsolidity/SolidityParser.cpp
@@ -539,7 +539,6 @@ BOOST_AUTO_TEST_CASE(keyword_is_reserved)
"supports",
"switch",
"try",
- "type",
"typedef",
"typeof",
"unchecked"
diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccess.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccess.sol
new file mode 100644
index 00000000..e90443e1
--- /dev/null
+++ b/test/libsolidity/syntaxTests/metaTypes/codeAccess.sol
@@ -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) {}
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractCreation.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractCreation.sol
new file mode 100644
index 00000000..bae137c5
--- /dev/null
+++ b/test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractCreation.sol
@@ -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).
diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractRuntime.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractRuntime.sol
new file mode 100644
index 00000000..186d2714
--- /dev/null
+++ b/test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractRuntime.sol
@@ -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).
diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccessBase.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccessBase.sol
new file mode 100644
index 00000000..33dbfd7c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/metaTypes/codeAccessBase.sol
@@ -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.
diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccessCyclic.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccessCyclic.sol
new file mode 100644
index 00000000..d5723df6
--- /dev/null
+++ b/test/libsolidity/syntaxTests/metaTypes/codeAccessCyclic.sol
@@ -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.
diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccessIsConstant.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccessIsConstant.sol
new file mode 100644
index 00000000..cda5d5c3
--- /dev/null
+++ b/test/libsolidity/syntaxTests/metaTypes/codeAccessIsConstant.sol
@@ -0,0 +1,7 @@
+contract Test {
+ bytes constant c = type(B).creationCode;
+ bytes constant r = type(B).runtimeCode;
+
+}
+contract B { function f() public pure {} }
+// ----
diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccessLibrary.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccessLibrary.sol
new file mode 100644
index 00000000..f746dc35
--- /dev/null
+++ b/test/libsolidity/syntaxTests/metaTypes/codeAccessLibrary.sol
@@ -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) {}
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/metaTypes/codeIsNoLValue.sol b/test/libsolidity/syntaxTests/metaTypes/codeIsNoLValue.sol
new file mode 100644
index 00000000..022e4d6f
--- /dev/null
+++ b/test/libsolidity/syntaxTests/metaTypes/codeIsNoLValue.sol
@@ -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.
diff --git a/test/libsolidity/syntaxTests/metaTypes/noArgForType.sol b/test/libsolidity/syntaxTests/metaTypes/noArgForType.sol
new file mode 100644
index 00000000..542aaf53
--- /dev/null
+++ b/test/libsolidity/syntaxTests/metaTypes/noArgForType.sol
@@ -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.
diff --git a/test/libsolidity/syntaxTests/metaTypes/runtimeCodeWarningAssembly.sol b/test/libsolidity/syntaxTests/metaTypes/runtimeCodeWarningAssembly.sol
new file mode 100644
index 00000000..ec8d9784
--- /dev/null
+++ b/test/libsolidity/syntaxTests/metaTypes/runtimeCodeWarningAssembly.sol
@@ -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.
diff --git a/test/libsolidity/syntaxTests/metaTypes/tooManyArgsForType.sol b/test/libsolidity/syntaxTests/metaTypes/tooManyArgsForType.sol
new file mode 100644
index 00000000..61c2b779
--- /dev/null
+++ b/test/libsolidity/syntaxTests/metaTypes/tooManyArgsForType.sol
@@ -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.
diff --git a/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierContractName.sol b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierContractName.sol
new file mode 100644
index 00000000..144ca1c3
--- /dev/null
+++ b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierContractName.sol
@@ -0,0 +1,3 @@
+contract type { }
+// ----
+// ParserError: (9-13): Expected identifier but got 'type'
diff --git a/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierFunction.sol b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierFunction.sol
new file mode 100644
index 00000000..b7881f15
--- /dev/null
+++ b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierFunction.sol
@@ -0,0 +1,6 @@
+contract Test {
+ function type() public pure {
+ }
+}
+// ----
+// ParserError: (29-33): Expected identifier but got 'type'
diff --git a/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierParameter.sol b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierParameter.sol
new file mode 100644
index 00000000..001ba840
--- /dev/null
+++ b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierParameter.sol
@@ -0,0 +1,6 @@
+contract Test {
+ function f(uint type) public pure {
+ }
+}
+// ----
+// ParserError: (36-40): Expected ',' but got 'type'
diff --git a/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierStateVariable.sol b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierStateVariable.sol
new file mode 100644
index 00000000..fa827a33
--- /dev/null
+++ b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierStateVariable.sol
@@ -0,0 +1,5 @@
+contract Test {
+ uint type;
+}
+// ----
+// ParserError: (25-29): Expected identifier but got 'type'
diff --git a/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierVariable.sol b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierVariable.sol
new file mode 100644
index 00000000..fa57698d
--- /dev/null
+++ b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierVariable.sol
@@ -0,0 +1,7 @@
+contract Test {
+ function f() public pure {
+ uint type;
+ }
+}
+// ----
+// ParserError: (60-64): Expected ';' but got 'type'
diff --git a/test/libsolidity/syntaxTests/metaTypes/typeOfContract.sol b/test/libsolidity/syntaxTests/metaTypes/typeOfContract.sol
new file mode 100644
index 00000000..036c36e8
--- /dev/null
+++ b/test/libsolidity/syntaxTests/metaTypes/typeOfContract.sol
@@ -0,0 +1,6 @@
+contract Test {
+ function f() public pure returns (bytes memory) {
+ type(Test);
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol b/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol
new file mode 100644
index 00000000..0ce06786
--- /dev/null
+++ b/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol
@@ -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.
diff --git a/test/libsolidity/syntaxTests/metaTypes/unsupportedArgForType.sol b/test/libsolidity/syntaxTests/metaTypes/unsupportedArgForType.sol
new file mode 100644
index 00000000..5c27d42f
--- /dev/null
+++ b/test/libsolidity/syntaxTests/metaTypes/unsupportedArgForType.sol
@@ -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.