diff options
-rw-r--r-- | docs/control-structures.rst | 22 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 7 | ||||
-rw-r--r-- | libsolidity/ast/ASTAnnotations.h | 2 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 6 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 10 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 26 |
6 files changed, 60 insertions, 13 deletions
diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 6c0b0f27..3f012b12 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -104,7 +104,7 @@ contract can be called internally. External Function Calls ----------------------- -The expressions ``this.g(8);`` and ``c.g(2);`` (where ``g`` is a contract +The expressions ``this.g(8);`` and ``c.g(2);`` (where ``c`` is a contract instance) are also valid function calls, but this time, the function will be called "externally", via a message call and not directly via jumps. Please note that function calls on ``this`` cannot be used in the constructor, as the @@ -384,16 +384,16 @@ In the following example, we show how ``throw`` can be used to easily revert an Currently, Solidity automatically generates a runtime exception in the following situations: -1. If you access an array at a too large or negative index (i.e. ``x[i]`` where ``i >= x.length`` or ``i < 0``). -1. If you access a fixed-length ``bytesN`` at a too large or negative index. -1. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall`` or ``callcode`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``. -1. If you create a contract using the ``new`` keyword but the contract creation does not finish properly (see above for the definition of "not finish properly"). -1. If you divide or modulo by zero (e.g. ``5 / 0`` or ``23 % 0``). -1. If you shift by a negative amount. -1. If you convert a value too big or negative into an enum type. -1. If you perform an external function call targeting a contract that contains no code. -1. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function). -1. If your contract receives Ether via a public accessor function. +#. If you access an array at a too large or negative index (i.e. ``x[i]`` where ``i >= x.length`` or ``i < 0``). +#. If you access a fixed-length ``bytesN`` at a too large or negative index. +#. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall`` or ``callcode`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``. +#. If you create a contract using the ``new`` keyword but the contract creation does not finish properly (see above for the definition of "not finish properly"). +#. If you divide or modulo by zero (e.g. ``5 / 0`` or ``23 % 0``). +#. If you shift by a negative amount. +#. If you convert a value too big or negative into an enum type. +#. If you perform an external function call targeting a contract that contains no code. +#. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function). +#. If your contract receives Ether via a public accessor function. Internally, Solidity performs an "invalid jump" when an exception is thrown and thus causes the EVM to revert all changes made to the state. The reason for this is that there is no safe way to continue execution, because an expected effect did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction (or at least call) without effect. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 67c8ac17..adb3d54f 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -75,7 +75,10 @@ bool TypeChecker::visit(ContractDefinition const& _contract) checkContractAbstractConstructors(_contract); FunctionDefinition const* function = _contract.constructor(); - if (function) { + if (function) + { + if (!function->isPublic()) + _contract.annotation().hasPublicConstructor = false; if (!function->returnParameters().empty()) typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor."); if (function->isDeclaredConst()) @@ -1280,6 +1283,8 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) fatalTypeError(_newExpression.location(), "Identifier is not a contract."); if (!contract->annotation().isFullyImplemented) typeError(_newExpression.location(), "Trying to create an instance of an abstract contract."); + if (!contract->annotation().hasPublicConstructor) + typeError(_newExpression.location(), "Contract with internal constructor cannot be created directly."); solAssert(!!m_scope, ""); m_scope->annotation().contractDependencies.insert(contract); diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 9c4c3ae8..61e97a55 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -80,6 +80,8 @@ struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnota { /// Whether all functions are implemented. bool isFullyImplemented = true; + /// Whether a public constructor (even the default one) is available. + bool hasPublicConstructor = true; /// List of all (direct and indirect) base contracts in order from derived to /// base, including the contract itself. std::vector<ContractDefinition const*> linearizedBaseContracts; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 85ec0fb1..b26bd501 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -624,7 +624,11 @@ void CompilerStack::compileContract( map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts ) { - if (_compiledContracts.count(&_contract) || !_contract.annotation().isFullyImplemented) + if ( + _compiledContracts.count(&_contract) || + !_contract.annotation().isFullyImplemented || + !_contract.annotation().hasPublicConstructor + ) return; for (auto const* dependency: _contract.annotation().contractDependencies) compileContract(*dependency, _compiledContracts); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 19161831..63a5828e 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2505,6 +2505,16 @@ BOOST_AUTO_TEST_CASE(constructor_argument_overriding) BOOST_CHECK(callContractFunction("getA()") == encodeArgs(3)); } +BOOST_AUTO_TEST_CASE(internal_constructor) +{ + char const* sourceCode = R"( + contract C { + function C() internal {} + } + )"; + BOOST_CHECK(compileAndRunWithoutCheck(sourceCode, 0, "C").empty()); +} + BOOST_AUTO_TEST_CASE(function_modifier) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 9f6ea2b3..edb57b0d 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -4899,6 +4899,32 @@ BOOST_AUTO_TEST_CASE(assignment_to_constant) CHECK_ERROR(text, TypeError, "Cannot assign to a constant variable."); } +BOOST_AUTO_TEST_CASE(inconstructible_internal_constructor) +{ + char const* text = R"( + contract C { + function C() internal {} + } + contract D { + function f() { var x = new C(); } + } + )"; + CHECK_ERROR(text, TypeError, "Contract with internal constructor cannot be created directly."); +} + +BOOST_AUTO_TEST_CASE(constructible_internal_constructor) +{ + char const* text = R"( + contract C { + function C() internal {} + } + contract D is C { + function D() { } + } + )"; + success(text); +} + BOOST_AUTO_TEST_SUITE_END() } |