mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #12646 from ethereum/develop
Merging develop into breaking.
This commit is contained in:
commit
649029da92
@ -384,7 +384,7 @@ defaults:
|
|||||||
TERM: xterm
|
TERM: xterm
|
||||||
MAKEFLAGS: -j 2
|
MAKEFLAGS: -j 2
|
||||||
|
|
||||||
- base_node_latest_small: &base_node_latest_small
|
- base_node_small: &base_node_small
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node
|
- image: circleci/node
|
||||||
resource_class: small
|
resource_class: small
|
||||||
@ -521,35 +521,30 @@ defaults:
|
|||||||
name: t_native_test_ext_ens
|
name: t_native_test_ext_ens
|
||||||
project: ens
|
project: ens
|
||||||
binary_type: native
|
binary_type: native
|
||||||
# NOTE: Tests crash on nodejs 17: "Error: error:0308010C:digital envelope routines::unsupported"
|
|
||||||
nodejs_version: '16'
|
nodejs_version: '16'
|
||||||
- job_native_test_ext_trident: &job_native_test_ext_trident
|
- job_native_test_ext_trident: &job_native_test_ext_trident
|
||||||
<<: *workflow_ubuntu2004_static
|
<<: *workflow_ubuntu2004_static
|
||||||
name: t_native_test_ext_trident
|
name: t_native_test_ext_trident
|
||||||
project: trident
|
project: trident
|
||||||
binary_type: native
|
binary_type: native
|
||||||
# NOTE: Tests crash on nodejs 17: "Error: error:0308010C:digital envelope routines::unsupported"
|
|
||||||
nodejs_version: '16'
|
nodejs_version: '16'
|
||||||
- job_native_test_ext_euler: &job_native_test_ext_euler
|
- job_native_test_ext_euler: &job_native_test_ext_euler
|
||||||
<<: *workflow_ubuntu2004_static
|
<<: *workflow_ubuntu2004_static
|
||||||
name: t_native_test_ext_euler
|
name: t_native_test_ext_euler
|
||||||
project: euler
|
project: euler
|
||||||
binary_type: native
|
binary_type: native
|
||||||
# NOTE: Tests crash on nodejs 17: "Error: error:0308010C:digital envelope routines::unsupported"
|
|
||||||
nodejs_version: '16'
|
nodejs_version: '16'
|
||||||
- job_native_test_ext_yield_liquidator: &job_native_test_ext_yield_liquidator
|
- job_native_test_ext_yield_liquidator: &job_native_test_ext_yield_liquidator
|
||||||
<<: *workflow_ubuntu2004_static
|
<<: *workflow_ubuntu2004_static
|
||||||
name: t_native_test_ext_yield_liquidator
|
name: t_native_test_ext_yield_liquidator
|
||||||
project: yield-liquidator
|
project: yield-liquidator
|
||||||
binary_type: native
|
binary_type: native
|
||||||
# NOTE: Tests crash on nodejs 17: "Error: error:0308010C:digital envelope routines::unsupported"
|
|
||||||
nodejs_version: '16'
|
nodejs_version: '16'
|
||||||
- job_native_test_ext_bleeps: &job_native_test_ext_bleeps
|
- job_native_test_ext_bleeps: &job_native_test_ext_bleeps
|
||||||
<<: *workflow_ubuntu2004_static
|
<<: *workflow_ubuntu2004_static
|
||||||
name: t_native_test_ext_bleeps
|
name: t_native_test_ext_bleeps
|
||||||
project: bleeps
|
project: bleeps
|
||||||
binary_type: native
|
binary_type: native
|
||||||
# NOTE: Tests crash on nodejs 17: "Error: error:0308010C:digital envelope routines::unsupported"
|
|
||||||
nodejs_version: '16'
|
nodejs_version: '16'
|
||||||
resource_class: medium
|
resource_class: medium
|
||||||
- job_native_test_ext_pool_together: &job_native_test_ext_pool_together
|
- job_native_test_ext_pool_together: &job_native_test_ext_pool_together
|
||||||
@ -557,35 +552,30 @@ defaults:
|
|||||||
name: t_native_test_ext_pool_together
|
name: t_native_test_ext_pool_together
|
||||||
project: pool-together
|
project: pool-together
|
||||||
binary_type: native
|
binary_type: native
|
||||||
# NOTE: Tests crash on nodejs 17: "Error: error:0308010C:digital envelope routines::unsupported"
|
|
||||||
nodejs_version: '16'
|
nodejs_version: '16'
|
||||||
- job_native_test_ext_perpetual_pools: &job_native_test_ext_perpetual_pools
|
- job_native_test_ext_perpetual_pools: &job_native_test_ext_perpetual_pools
|
||||||
<<: *workflow_ubuntu2004_static
|
<<: *workflow_ubuntu2004_static
|
||||||
name: t_native_test_ext_perpetual_pools
|
name: t_native_test_ext_perpetual_pools
|
||||||
project: perpetual-pools
|
project: perpetual-pools
|
||||||
binary_type: native
|
binary_type: native
|
||||||
# NOTE: Tests crash on nodejs 17: "Error: error:0308010C:digital envelope routines::unsupported"
|
|
||||||
nodejs_version: '16'
|
nodejs_version: '16'
|
||||||
- job_native_test_ext_uniswap: &job_native_test_ext_uniswap
|
- job_native_test_ext_uniswap: &job_native_test_ext_uniswap
|
||||||
<<: *workflow_ubuntu2004_static
|
<<: *workflow_ubuntu2004_static
|
||||||
name: t_native_test_ext_uniswap
|
name: t_native_test_ext_uniswap
|
||||||
project: uniswap
|
project: uniswap
|
||||||
binary_type: native
|
binary_type: native
|
||||||
# NOTE: Tests crash on nodejs 17: "Error: error:0308010C:digital envelope routines::unsupported"
|
|
||||||
nodejs_version: '16'
|
nodejs_version: '16'
|
||||||
- job_native_test_prb_math: &job_native_test_prb_math
|
- job_native_test_prb_math: &job_native_test_prb_math
|
||||||
<<: *workflow_ubuntu2004_static
|
<<: *workflow_ubuntu2004_static
|
||||||
name: t_native_test_ext_prb_math
|
name: t_native_test_ext_prb_math
|
||||||
project: prb-math
|
project: prb-math
|
||||||
binary_type: native
|
binary_type: native
|
||||||
# NOTE: Tests crash on nodejs 17: "Error: error:0308010C:digital envelope routines::unsupported"
|
|
||||||
nodejs_version: '16'
|
nodejs_version: '16'
|
||||||
- job_native_test_ext_elementfi: &job_native_test_ext_elementfi
|
- job_native_test_ext_elementfi: &job_native_test_ext_elementfi
|
||||||
<<: *workflow_ubuntu2004_static
|
<<: *workflow_ubuntu2004_static
|
||||||
name: t_native_test_ext_elementfi
|
name: t_native_test_ext_elementfi
|
||||||
project: elementfi
|
project: elementfi
|
||||||
binary_type: native
|
binary_type: native
|
||||||
# NOTE: Tests crash on nodejs 17: "Error: error:0308010C:digital envelope routines::unsupported"
|
|
||||||
nodejs_version: '16'
|
nodejs_version: '16'
|
||||||
resource_class: medium
|
resource_class: medium
|
||||||
- job_ems_test_ext_colony: &job_ems_test_ext_colony
|
- job_ems_test_ext_colony: &job_ems_test_ext_colony
|
||||||
@ -615,7 +605,7 @@ jobs:
|
|||||||
- gitter_notify_failure_unless_pr
|
- gitter_notify_failure_unless_pr
|
||||||
|
|
||||||
chk_docs_examples:
|
chk_docs_examples:
|
||||||
<<: *base_node_latest_small
|
<<: *base_node_small
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
@ -684,7 +674,7 @@ jobs:
|
|||||||
- gitter_notify_failure_unless_pr
|
- gitter_notify_failure_unless_pr
|
||||||
|
|
||||||
chk_buglist:
|
chk_buglist:
|
||||||
<<: *base_node_latest_small
|
<<: *base_node_small
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run:
|
- run:
|
||||||
@ -1146,7 +1136,9 @@ jobs:
|
|||||||
- gitter_notify_failure_unless_pr
|
- gitter_notify_failure_unless_pr
|
||||||
|
|
||||||
t_ems_ext_hardhat:
|
t_ems_ext_hardhat:
|
||||||
<<: *base_node_latest_small
|
<<: *base_node_small
|
||||||
|
docker:
|
||||||
|
- image: circleci/node:16
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm
|
TERM: xterm
|
||||||
HARDHAT_TESTS_SOLC_PATH: /tmp/workspace/soljson.js
|
HARDHAT_TESTS_SOLC_PATH: /tmp/workspace/soljson.js
|
||||||
@ -1349,7 +1341,7 @@ jobs:
|
|||||||
- gitter_notify_failure_unless_pr
|
- gitter_notify_failure_unless_pr
|
||||||
|
|
||||||
b_bytecode_ems:
|
b_bytecode_ems:
|
||||||
<<: *base_node_latest_small
|
<<: *base_node_small
|
||||||
environment:
|
environment:
|
||||||
SOLC_EMSCRIPTEN: "On"
|
SOLC_EMSCRIPTEN: "On"
|
||||||
steps:
|
steps:
|
||||||
|
@ -18,6 +18,7 @@ Language Features:
|
|||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
* Yul Optimizer: Remove ``mstore`` and ``sstore`` operations if the slot already contains the same value.
|
* Yul Optimizer: Remove ``mstore`` and ``sstore`` operations if the slot already contains the same value.
|
||||||
|
* Yul: Emit immutable references for pure yul code when requested.
|
||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
@ -25,9 +26,10 @@ Bugfixes:
|
|||||||
* Code Generator: Fix ICE when accessing the members of external functions occupying more than two stack slots.
|
* Code Generator: Fix ICE when accessing the members of external functions occupying more than two stack slots.
|
||||||
* Code Generator: Fix ICE when doing an explicit conversion from ``string calldata`` to ``bytes``.
|
* Code Generator: Fix ICE when doing an explicit conversion from ``string calldata`` to ``bytes``.
|
||||||
* Control Flow Graph: Perform proper virtual lookup for modifiers for uninitialized variable and unreachable code analysis.
|
* Control Flow Graph: Perform proper virtual lookup for modifiers for uninitialized variable and unreachable code analysis.
|
||||||
* Immutables: Fix wrong error when the constructor of a base contract uses ``return`` and the parent contract contains immutable variables.
|
* Immutables: Fix wrong error when the constructor of a base contract uses ``return`` and the derived contract contains immutable variables.
|
||||||
* IR Generator: Add missing cleanup during the conversion of fixed bytes types to smaller fixed bytes types.
|
* IR Generator: Add missing cleanup during the conversion of fixed bytes types to smaller fixed bytes types.
|
||||||
* IR Generator: Add missing cleanup for indexed event arguments of value type.
|
* IR Generator: Add missing cleanup for indexed event arguments of value type.
|
||||||
|
* IR Generator: Fix internal error when copying reference types in calldata and storage to struct or array members in memory.
|
||||||
* IR Generator: Fix IR syntax error when copying storage arrays of structs containing functions.
|
* IR Generator: Fix IR syntax error when copying storage arrays of structs containing functions.
|
||||||
* Natspec: Fix ICE when overriding a struct getter with a Natspec-documented return value and the name in the struct is different.
|
* Natspec: Fix ICE when overriding a struct getter with a Natspec-documented return value and the name in the struct is different.
|
||||||
* TypeChecker: Fix ICE when a constant variable declaration forward references a struct.
|
* TypeChecker: Fix ICE when a constant variable declaration forward references a struct.
|
||||||
|
@ -66,7 +66,8 @@
|
|||||||
### Release solc-js
|
### Release solc-js
|
||||||
- [ ] Wait until solc-bin was properly deployed. You can test this via remix - a test run through remix is advisable anyway.
|
- [ ] Wait until solc-bin was properly deployed. You can test this via remix - a test run through remix is advisable anyway.
|
||||||
- [ ] Increment the version number, create a pull request for that, merge it after tests succeeded.
|
- [ ] Increment the version number, create a pull request for that, merge it after tests succeeded.
|
||||||
- [ ] Run ``npm run updateBinary && npm publish`` in the updated ``solc-js`` repository.
|
- [ ] Run ``npm run build:tarball`` in the updated ``solc-js`` repository to create ``solc-<version>.tgz``. Inspect the tarball to ensure that it contains an up to date compiler binary.
|
||||||
|
- [ ] Run ``npm run publish:tarball`` to publish the newly created tarball.
|
||||||
- [ ] Create a tag using ``git tag --annotate v$VERSION`` and push it with ``git push --tags``.
|
- [ ] Create a tag using ``git tag --annotate v$VERSION`` and push it with ``git push --tags``.
|
||||||
|
|
||||||
### Post-release
|
### Post-release
|
||||||
|
@ -92,7 +92,7 @@ Global Variables
|
|||||||
- ``block.difficulty`` (``uint``): current block difficulty
|
- ``block.difficulty`` (``uint``): current block difficulty
|
||||||
- ``block.gaslimit`` (``uint``): current block gaslimit
|
- ``block.gaslimit`` (``uint``): current block gaslimit
|
||||||
- ``block.number`` (``uint``): current block number
|
- ``block.number`` (``uint``): current block number
|
||||||
- ``block.timestamp`` (``uint``): current block timestamp
|
- ``block.timestamp`` (``uint``): current block timestamp in seconds since Unix epoch
|
||||||
- ``gasleft() returns (uint256)``: remaining gas
|
- ``gasleft() returns (uint256)``: remaining gas
|
||||||
- ``msg.data`` (``bytes``): complete calldata
|
- ``msg.data`` (``bytes``): complete calldata
|
||||||
- ``msg.sender`` (``address``): sender of the message (current call)
|
- ``msg.sender`` (``address``): sender of the message (current call)
|
||||||
|
@ -90,7 +90,7 @@ The value corresponding to a mapping key ``k`` is located at ``keccak256(h(k) .
|
|||||||
where ``.`` is concatenation and ``h`` is a function that is applied to the key depending on its type:
|
where ``.`` is concatenation and ``h`` is a function that is applied to the key depending on its type:
|
||||||
|
|
||||||
- for value types, ``h`` pads the value to 32 bytes in the same way as when storing the value in memory.
|
- for value types, ``h`` pads the value to 32 bytes in the same way as when storing the value in memory.
|
||||||
- for strings and byte arrays, ``h`` computes the ``keccak256`` hash of the unpadded data.
|
- for strings and byte arrays, ``h(k)`` is just the unpadded data.
|
||||||
|
|
||||||
If the mapping value is a
|
If the mapping value is a
|
||||||
non-value type, the computed slot marks the start of the data. If the value is of struct type,
|
non-value type, the computed slot marks the start of the data. If the value is of struct type,
|
||||||
|
@ -36,7 +36,7 @@ tools.
|
|||||||
Documentation Example
|
Documentation Example
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
Documentation is inserted above each ``contract``, ``interface``,
|
Documentation is inserted above each ``contract``, ``interface``, ``library``,
|
||||||
``function``, and ``event`` using the Doxygen notation format.
|
``function``, and ``event`` using the Doxygen notation format.
|
||||||
A ``public`` state variable is equivalent to a ``function``
|
A ``public`` state variable is equivalent to a ``function``
|
||||||
for the purposes of NatSpec.
|
for the purposes of NatSpec.
|
||||||
|
@ -14,7 +14,7 @@ General Resources
|
|||||||
* `Solidity Compiler Developers Chat <https://matrix.to/#/#ethereum_solidity-dev:gitter.im>`_
|
* `Solidity Compiler Developers Chat <https://matrix.to/#/#ethereum_solidity-dev:gitter.im>`_
|
||||||
* `Awesome Solidity <https://github.com/bkrem/awesome-solidity>`_
|
* `Awesome Solidity <https://github.com/bkrem/awesome-solidity>`_
|
||||||
* `Solidity by Example <https://solidity-by-example.org/>`_
|
* `Solidity by Example <https://solidity-by-example.org/>`_
|
||||||
|
* `Solidity Documentation Community Translations <https://github.com/solidity-docs>`_
|
||||||
|
|
||||||
Integrated (Ethereum) Development Environments
|
Integrated (Ethereum) Development Environments
|
||||||
==============================================
|
==============================================
|
||||||
@ -28,15 +28,15 @@ Integrated (Ethereum) Development Environments
|
|||||||
* `Embark <https://framework.embarklabs.io/>`_
|
* `Embark <https://framework.embarklabs.io/>`_
|
||||||
Developer platform for building and deploying decentralized applications.
|
Developer platform for building and deploying decentralized applications.
|
||||||
|
|
||||||
|
* `Foundry <https://github.com/gakonst/foundry>`_
|
||||||
|
Fast, portable and modular toolkit for Ethereum application development written in Rust.
|
||||||
|
|
||||||
* `Hardhat <https://hardhat.org/>`_
|
* `Hardhat <https://hardhat.org/>`_
|
||||||
Ethereum development environment with local Ethereum network, debugging features and plugin ecosystem.
|
Ethereum development environment with local Ethereum network, debugging features and plugin ecosystem.
|
||||||
|
|
||||||
* `Remix <https://remix.ethereum.org/>`_
|
* `Remix <https://remix.ethereum.org/>`_
|
||||||
Browser-based IDE with integrated compiler and Solidity runtime environment without server-side components.
|
Browser-based IDE with integrated compiler and Solidity runtime environment without server-side components.
|
||||||
|
|
||||||
* `Scaffold-ETH <https://github.com/austintgriffith/scaffold-eth>`_
|
|
||||||
Ethereum development stack focused on fast product iterations.
|
|
||||||
|
|
||||||
* `Truffle <https://www.trufflesuite.com/truffle>`_
|
* `Truffle <https://www.trufflesuite.com/truffle>`_
|
||||||
Ethereum development framework.
|
Ethereum development framework.
|
||||||
|
|
||||||
@ -112,6 +112,9 @@ Solidity Tools
|
|||||||
* `PIET <https://piet.slock.it/>`_
|
* `PIET <https://piet.slock.it/>`_
|
||||||
A tool to develop, audit and use Solidity smart contracts through a simple graphical interface.
|
A tool to develop, audit and use Solidity smart contracts through a simple graphical interface.
|
||||||
|
|
||||||
|
* `Scaffold-ETH <https://github.com/scaffold-eth/scaffold-eth>`_
|
||||||
|
Forkable Ethereum development stack focused on fast product iterations.
|
||||||
|
|
||||||
* `sol2uml <https://www.npmjs.com/package/sol2uml>`_
|
* `sol2uml <https://www.npmjs.com/package/sol2uml>`_
|
||||||
Unified Modeling Language (UML) class diagram generator for Solidity contracts.
|
Unified Modeling Language (UML) class diagram generator for Solidity contracts.
|
||||||
|
|
||||||
@ -130,6 +133,9 @@ Solidity Tools
|
|||||||
* `Solhint <https://github.com/protofire/solhint>`_
|
* `Solhint <https://github.com/protofire/solhint>`_
|
||||||
Solidity linter that provides security, style guide and best practice rules for smart contract validation.
|
Solidity linter that provides security, style guide and best practice rules for smart contract validation.
|
||||||
|
|
||||||
|
* `Sourcify <https://sourcify.dev/>`_
|
||||||
|
Decentralized automated contract verification service and public repository of contract metadata.
|
||||||
|
|
||||||
* `Sūrya <https://github.com/ConsenSys/surya/>`_
|
* `Sūrya <https://github.com/ConsenSys/surya/>`_
|
||||||
Utility tool for smart contract systems, offering a number of visual outputs and information about the contracts' structure. Also supports querying the function call graph.
|
Utility tool for smart contract systems, offering a number of visual outputs and information about the contracts' structure. Also supports querying the function call graph.
|
||||||
|
|
||||||
|
@ -337,9 +337,9 @@ Array Members
|
|||||||
Dynamic storage arrays and ``bytes`` (not ``string``) have a member function
|
Dynamic storage arrays and ``bytes`` (not ``string``) have a member function
|
||||||
called ``push(x)`` that you can use to append a given element at the end of the array.
|
called ``push(x)`` that you can use to append a given element at the end of the array.
|
||||||
The function returns nothing.
|
The function returns nothing.
|
||||||
**pop**:
|
**pop()**:
|
||||||
Dynamic storage arrays and ``bytes`` (not ``string``) have a member
|
Dynamic storage arrays and ``bytes`` (not ``string``) have a member
|
||||||
function called ``pop`` that you can use to remove an element from the
|
function called ``pop()`` that you can use to remove an element from the
|
||||||
end of the array. This also implicitly calls :ref:`delete<delete>` on the removed element.
|
end of the array. This also implicitly calls :ref:`delete<delete>` on the removed element.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
@ -188,7 +188,8 @@ The address type comes in two flavours, which are largely identical:
|
|||||||
- ``address payable``: Same as ``address``, but with the additional members ``transfer`` and ``send``.
|
- ``address payable``: Same as ``address``, but with the additional members ``transfer`` and ``send``.
|
||||||
|
|
||||||
The idea behind this distinction is that ``address payable`` is an address you can send Ether to,
|
The idea behind this distinction is that ``address payable`` is an address you can send Ether to,
|
||||||
while a plain ``address`` cannot be sent Ether.
|
while you are not supposed to send Ether to a plain ``address``, for example because it might be a smart contract
|
||||||
|
that was not built to accept Ether.
|
||||||
|
|
||||||
Type conversions:
|
Type conversions:
|
||||||
|
|
||||||
@ -331,7 +332,9 @@ on ``call``.
|
|||||||
|
|
||||||
* ``code`` and ``codehash``
|
* ``code`` and ``codehash``
|
||||||
|
|
||||||
You can query the deployed code for any smart contract. Use ``code`` to get the EVM bytecode as a string, which might be empty. Use ``codehash`` get the Keccak-256 hash of that code.
|
You can query the deployed code for any smart contract. Use ``.code`` to get the EVM bytecode as a
|
||||||
|
``bytes memory``, which might be empty. Use ``.codehash`` get the Keccak-256 hash of that code
|
||||||
|
(as a ``bytes32``). Note that ``addr.codehash`` is cheaper than using ``keccak256(addr.code)``.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
All contracts can be converted to ``address`` type, so it is possible to query the balance of the
|
All contracts can be converted to ``address`` type, so it is possible to query the balance of the
|
||||||
|
@ -441,7 +441,7 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
|||||||
{
|
{
|
||||||
bool allowed = false;
|
bool allowed = false;
|
||||||
if (auto arrayType = dynamic_cast<ArrayType const*>(type))
|
if (auto arrayType = dynamic_cast<ArrayType const*>(type))
|
||||||
allowed = arrayType->isByteArray();
|
allowed = arrayType->isByteArrayOrString();
|
||||||
if (!allowed)
|
if (!allowed)
|
||||||
m_errorReporter.fatalTypeError(9259_error, _variable.location(), "Only constants of value type and byte array type are implemented.");
|
m_errorReporter.fatalTypeError(9259_error, _variable.location(), "Only constants of value type and byte array type are implemented.");
|
||||||
}
|
}
|
||||||
|
@ -1783,7 +1783,7 @@ Type const* TypeChecker::typeCheckTypeConversionAndRetrieveReturnType(
|
|||||||
(
|
(
|
||||||
(
|
(
|
||||||
resultArrayType->isPointer() ||
|
resultArrayType->isPointer() ||
|
||||||
(argArrayType->isByteArray() && resultArrayType->isByteArray())
|
(argArrayType->isByteArrayOrString() && resultArrayType->isByteArrayOrString())
|
||||||
) &&
|
) &&
|
||||||
resultArrayType->location() == DataLocation::Storage
|
resultArrayType->location() == DataLocation::Storage
|
||||||
),
|
),
|
||||||
@ -1791,7 +1791,7 @@ Type const* TypeChecker::typeCheckTypeConversionAndRetrieveReturnType(
|
|||||||
);
|
);
|
||||||
else
|
else
|
||||||
solAssert(
|
solAssert(
|
||||||
argArrayType->isByteArray() && !argArrayType->isString() && resultType->category() == Type::Category::FixedBytes,
|
argArrayType->isByteArray() && resultType->category() == Type::Category::FixedBytes,
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1206,7 +1206,7 @@ BoolResult StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo)
|
|||||||
);
|
);
|
||||||
return
|
return
|
||||||
arrayType->location() != DataLocation::CallData &&
|
arrayType->location() != DataLocation::CallData &&
|
||||||
arrayType->isByteArray() &&
|
arrayType->isByteArrayOrString() &&
|
||||||
!(arrayType->dataStoredIn(DataLocation::Storage) && arrayType->isPointer());
|
!(arrayType->dataStoredIn(DataLocation::Storage) && arrayType->isPointer());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1575,11 +1575,11 @@ BoolResult ArrayType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
|||||||
return true;
|
return true;
|
||||||
// allow conversion bytes <-> string and bytes -> bytesNN
|
// allow conversion bytes <-> string and bytes -> bytesNN
|
||||||
if (_convertTo.category() != category())
|
if (_convertTo.category() != category())
|
||||||
return isByteArray() && !isString() && _convertTo.category() == Type::Category::FixedBytes;
|
return isByteArray() && _convertTo.category() == Type::Category::FixedBytes;
|
||||||
auto& convertTo = dynamic_cast<ArrayType const&>(_convertTo);
|
auto& convertTo = dynamic_cast<ArrayType const&>(_convertTo);
|
||||||
if (convertTo.location() != location())
|
if (convertTo.location() != location())
|
||||||
return false;
|
return false;
|
||||||
if (!isByteArray() || !convertTo.isByteArray())
|
if (!isByteArrayOrString() || !convertTo.isByteArrayOrString())
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1589,7 +1589,7 @@ string ArrayType::richIdentifier() const
|
|||||||
string id;
|
string id;
|
||||||
if (isString())
|
if (isString())
|
||||||
id = "t_string";
|
id = "t_string";
|
||||||
else if (isByteArray())
|
else if (isByteArrayOrString())
|
||||||
id = "t_bytes";
|
id = "t_bytes";
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1755,7 +1755,7 @@ string ArrayType::toString(bool _short) const
|
|||||||
string ret;
|
string ret;
|
||||||
if (isString())
|
if (isString())
|
||||||
ret = "string";
|
ret = "string";
|
||||||
else if (isByteArray())
|
else if (isByteArrayOrString())
|
||||||
ret = "bytes";
|
ret = "bytes";
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1774,7 +1774,7 @@ string ArrayType::canonicalName() const
|
|||||||
string ret;
|
string ret;
|
||||||
if (isString())
|
if (isString())
|
||||||
ret = "string";
|
ret = "string";
|
||||||
else if (isByteArray())
|
else if (isByteArrayOrString())
|
||||||
ret = "bytes";
|
ret = "bytes";
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1788,7 +1788,7 @@ string ArrayType::canonicalName() const
|
|||||||
|
|
||||||
string ArrayType::signatureInExternalFunction(bool _structsByName) const
|
string ArrayType::signatureInExternalFunction(bool _structsByName) const
|
||||||
{
|
{
|
||||||
if (isByteArray())
|
if (isByteArrayOrString())
|
||||||
return canonicalName();
|
return canonicalName();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1903,7 +1903,7 @@ u256 ArrayType::memoryDataSize() const
|
|||||||
{
|
{
|
||||||
solAssert(!isDynamicallySized(), "");
|
solAssert(!isDynamicallySized(), "");
|
||||||
solAssert(m_location == DataLocation::Memory, "");
|
solAssert(m_location == DataLocation::Memory, "");
|
||||||
solAssert(!isByteArray(), "");
|
solAssert(!isByteArrayOrString(), "");
|
||||||
bigint size = bigint(m_length) * m_baseType->memoryHeadSize();
|
bigint size = bigint(m_length) * m_baseType->memoryHeadSize();
|
||||||
solAssert(size <= numeric_limits<u256>::max(), "Array size does not fit u256.");
|
solAssert(size <= numeric_limits<u256>::max(), "Array size does not fit u256.");
|
||||||
return u256(size);
|
return u256(size);
|
||||||
@ -2705,7 +2705,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
|
|||||||
}
|
}
|
||||||
else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType))
|
else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType))
|
||||||
{
|
{
|
||||||
if (arrayType->isByteArray())
|
if (arrayType->isByteArrayOrString())
|
||||||
// Return byte arrays as whole.
|
// Return byte arrays as whole.
|
||||||
break;
|
break;
|
||||||
returnType = arrayType->baseType();
|
returnType = arrayType->baseType();
|
||||||
@ -2724,7 +2724,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
|
|||||||
if (member.type->category() != Category::Mapping)
|
if (member.type->category() != Category::Mapping)
|
||||||
{
|
{
|
||||||
if (auto arrayType = dynamic_cast<ArrayType const*>(member.type))
|
if (auto arrayType = dynamic_cast<ArrayType const*>(member.type))
|
||||||
if (!arrayType->isByteArray())
|
if (!arrayType->isByteArrayOrString())
|
||||||
continue;
|
continue;
|
||||||
m_returnParameterTypes.push_back(TypeProvider::withLocationIfReference(
|
m_returnParameterTypes.push_back(TypeProvider::withLocationIfReference(
|
||||||
DataLocation::Memory,
|
DataLocation::Memory,
|
||||||
@ -3817,7 +3817,7 @@ MemberList::MemberMap TypeType::nativeMembers(ASTNode const* _currentScope) cons
|
|||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
auto const* arrayType = dynamic_cast<ArrayType const*>(m_actualType);
|
auto const* arrayType = dynamic_cast<ArrayType const*>(m_actualType);
|
||||||
arrayType && arrayType->isByteArray()
|
arrayType && arrayType->isByteArrayOrString()
|
||||||
)
|
)
|
||||||
members.emplace_back("concat", TypeProvider::function(
|
members.emplace_back("concat", TypeProvider::function(
|
||||||
TypePointers{},
|
TypePointers{},
|
||||||
|
@ -837,8 +837,10 @@ public:
|
|||||||
|
|
||||||
BoolResult validForLocation(DataLocation _loc) const override;
|
BoolResult validForLocation(DataLocation _loc) const override;
|
||||||
|
|
||||||
|
/// @returns true if this is a byte array.
|
||||||
|
bool isByteArray() const { return m_arrayKind == ArrayKind::Bytes; }
|
||||||
/// @returns true if this is a byte array or a string
|
/// @returns true if this is a byte array or a string
|
||||||
bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; }
|
bool isByteArrayOrString() const { return m_arrayKind != ArrayKind::Ordinary; }
|
||||||
/// @returns true if this is a string
|
/// @returns true if this is a string
|
||||||
bool isString() const { return m_arrayKind == ArrayKind::String; }
|
bool isString() const { return m_arrayKind == ArrayKind::String; }
|
||||||
Type const* baseType() const { solAssert(!!m_baseType, ""); return m_baseType; }
|
Type const* baseType() const { solAssert(!!m_baseType, ""); return m_baseType; }
|
||||||
@ -849,11 +851,11 @@ public:
|
|||||||
std::unique_ptr<ReferenceType> copyForLocation(DataLocation _location, bool _isPointer) const override;
|
std::unique_ptr<ReferenceType> copyForLocation(DataLocation _location, bool _isPointer) const override;
|
||||||
|
|
||||||
/// The offset to advance in calldata to move from one array element to the next.
|
/// The offset to advance in calldata to move from one array element to the next.
|
||||||
unsigned calldataStride() const { return isByteArray() ? 1 : m_baseType->calldataHeadSize(); }
|
unsigned calldataStride() const { return isByteArrayOrString() ? 1 : m_baseType->calldataHeadSize(); }
|
||||||
/// The offset to advance in memory to move from one array element to the next.
|
/// The offset to advance in memory to move from one array element to the next.
|
||||||
unsigned memoryStride() const { return isByteArray() ? 1 : m_baseType->memoryHeadSize(); }
|
unsigned memoryStride() const { return isByteArrayOrString() ? 1 : m_baseType->memoryHeadSize(); }
|
||||||
/// The offset to advance in storage to move from one array element to the next.
|
/// The offset to advance in storage to move from one array element to the next.
|
||||||
unsigned storageStride() const { return isByteArray() ? 1 : m_baseType->storageBytes(); }
|
unsigned storageStride() const { return isByteArrayOrString() ? 1 : m_baseType->storageBytes(); }
|
||||||
|
|
||||||
void clearCache() const override;
|
void clearCache() const override;
|
||||||
|
|
||||||
@ -862,7 +864,6 @@ protected:
|
|||||||
std::vector<Type const*> decomposition() const override { return {m_baseType}; }
|
std::vector<Type const*> decomposition() const override { return {m_baseType}; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// String is interpreted as a subtype of Bytes.
|
|
||||||
enum class ArrayKind { Ordinary, Bytes, String };
|
enum class ArrayKind { Ordinary, Bytes, String };
|
||||||
|
|
||||||
bigint unlimitedStaticCalldataSize(bool _padded) const;
|
bigint unlimitedStaticCalldataSize(bool _padded) const;
|
||||||
|
@ -312,7 +312,7 @@ string ABIFunctions::abiEncodingFunction(
|
|||||||
{
|
{
|
||||||
case DataLocation::CallData:
|
case DataLocation::CallData:
|
||||||
if (
|
if (
|
||||||
fromArray->isByteArray() ||
|
fromArray->isByteArrayOrString() ||
|
||||||
*fromArray->baseType() == *TypeProvider::uint256() ||
|
*fromArray->baseType() == *TypeProvider::uint256() ||
|
||||||
*fromArray->baseType() == FixedBytesType(32)
|
*fromArray->baseType() == FixedBytesType(32)
|
||||||
)
|
)
|
||||||
@ -320,7 +320,7 @@ string ABIFunctions::abiEncodingFunction(
|
|||||||
else
|
else
|
||||||
return abiEncodingFunctionSimpleArray(*fromArray, *toArray, _options);
|
return abiEncodingFunctionSimpleArray(*fromArray, *toArray, _options);
|
||||||
case DataLocation::Memory:
|
case DataLocation::Memory:
|
||||||
if (fromArray->isByteArray())
|
if (fromArray->isByteArrayOrString())
|
||||||
return abiEncodingFunctionMemoryByteArray(*fromArray, *toArray, _options);
|
return abiEncodingFunctionMemoryByteArray(*fromArray, *toArray, _options);
|
||||||
else
|
else
|
||||||
return abiEncodingFunctionSimpleArray(*fromArray, *toArray, _options);
|
return abiEncodingFunctionSimpleArray(*fromArray, *toArray, _options);
|
||||||
@ -448,7 +448,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArrayWithoutCleanup(
|
|||||||
|
|
||||||
solAssert(fromArrayType.location() == DataLocation::CallData, "");
|
solAssert(fromArrayType.location() == DataLocation::CallData, "");
|
||||||
solAssert(
|
solAssert(
|
||||||
fromArrayType.isByteArray() ||
|
fromArrayType.isByteArrayOrString() ||
|
||||||
*fromArrayType.baseType() == *TypeProvider::uint256() ||
|
*fromArrayType.baseType() == *TypeProvider::uint256() ||
|
||||||
*fromArrayType.baseType() == FixedBytesType(32),
|
*fromArrayType.baseType() == FixedBytesType(32),
|
||||||
""
|
""
|
||||||
@ -468,7 +468,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArrayWithoutCleanup(
|
|||||||
_to.identifier() +
|
_to.identifier() +
|
||||||
_options.toFunctionNameSuffix();
|
_options.toFunctionNameSuffix();
|
||||||
return createFunction(functionName, [&]() {
|
return createFunction(functionName, [&]() {
|
||||||
bool needsPadding = _options.padded && fromArrayType.isByteArray();
|
bool needsPadding = _options.padded && fromArrayType.isByteArrayOrString();
|
||||||
if (fromArrayType.isDynamicallySized())
|
if (fromArrayType.isDynamicallySized())
|
||||||
{
|
{
|
||||||
Whiskers templ(R"(
|
Whiskers templ(R"(
|
||||||
@ -482,7 +482,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArrayWithoutCleanup(
|
|||||||
)");
|
)");
|
||||||
templ("storeLength", arrayStoreLengthForEncodingFunction(toArrayType, _options));
|
templ("storeLength", arrayStoreLengthForEncodingFunction(toArrayType, _options));
|
||||||
templ("functionName", functionName);
|
templ("functionName", functionName);
|
||||||
if (fromArrayType.isByteArray() || fromArrayType.calldataStride() == 1)
|
if (fromArrayType.isByteArrayOrString() || fromArrayType.calldataStride() == 1)
|
||||||
templ("scaleLengthByStride", "");
|
templ("scaleLengthByStride", "");
|
||||||
else
|
else
|
||||||
templ("scaleLengthByStride",
|
templ("scaleLengthByStride",
|
||||||
@ -536,7 +536,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
|
|||||||
|
|
||||||
solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
|
solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
|
||||||
solAssert(_from.length() == _to.length(), "");
|
solAssert(_from.length() == _to.length(), "");
|
||||||
solAssert(!_from.isByteArray(), "");
|
solAssert(!_from.isByteArrayOrString(), "");
|
||||||
if (_from.dataStoredIn(DataLocation::Storage))
|
if (_from.dataStoredIn(DataLocation::Storage))
|
||||||
solAssert(_from.baseType()->storageBytes() > 16, "");
|
solAssert(_from.baseType()->storageBytes() > 16, "");
|
||||||
|
|
||||||
@ -647,10 +647,10 @@ string ABIFunctions::abiEncodingFunctionMemoryByteArray(
|
|||||||
solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
|
solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
|
||||||
solAssert(_from.length() == _to.length(), "");
|
solAssert(_from.length() == _to.length(), "");
|
||||||
solAssert(_from.dataStoredIn(DataLocation::Memory), "");
|
solAssert(_from.dataStoredIn(DataLocation::Memory), "");
|
||||||
solAssert(_from.isByteArray(), "");
|
solAssert(_from.isByteArrayOrString(), "");
|
||||||
|
|
||||||
return createFunction(functionName, [&]() {
|
return createFunction(functionName, [&]() {
|
||||||
solAssert(_to.isByteArray(), "");
|
solAssert(_to.isByteArrayOrString(), "");
|
||||||
Whiskers templ(R"(
|
Whiskers templ(R"(
|
||||||
function <functionName>(value, pos) -> end {
|
function <functionName>(value, pos) -> end {
|
||||||
let length := <lengthFun>(value)
|
let length := <lengthFun>(value)
|
||||||
@ -686,9 +686,9 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
|
|||||||
solAssert(_from.dataStoredIn(DataLocation::Storage), "");
|
solAssert(_from.dataStoredIn(DataLocation::Storage), "");
|
||||||
|
|
||||||
return createFunction(functionName, [&]() {
|
return createFunction(functionName, [&]() {
|
||||||
if (_from.isByteArray())
|
if (_from.isByteArrayOrString())
|
||||||
{
|
{
|
||||||
solAssert(_to.isByteArray(), "");
|
solAssert(_to.isByteArrayOrString(), "");
|
||||||
Whiskers templ(R"(
|
Whiskers templ(R"(
|
||||||
// <readableTypeNameFrom> -> <readableTypeNameTo>
|
// <readableTypeNameFrom> -> <readableTypeNameTo>
|
||||||
function <functionName>(value, pos) -> ret {
|
function <functionName>(value, pos) -> ret {
|
||||||
@ -1168,7 +1168,7 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from
|
|||||||
string ABIFunctions::abiDecodingFunctionArrayAvailableLength(ArrayType const& _type, bool _fromMemory)
|
string ABIFunctions::abiDecodingFunctionArrayAvailableLength(ArrayType const& _type, bool _fromMemory)
|
||||||
{
|
{
|
||||||
solAssert(_type.dataStoredIn(DataLocation::Memory), "");
|
solAssert(_type.dataStoredIn(DataLocation::Memory), "");
|
||||||
if (_type.isByteArray())
|
if (_type.isByteArrayOrString())
|
||||||
return abiDecodingFunctionByteArrayAvailableLength(_type, _fromMemory);
|
return abiDecodingFunctionByteArrayAvailableLength(_type, _fromMemory);
|
||||||
solAssert(_type.calldataStride() > 0, "");
|
solAssert(_type.calldataStride() > 0, "");
|
||||||
|
|
||||||
@ -1275,7 +1275,7 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
|
|||||||
string ABIFunctions::abiDecodingFunctionByteArrayAvailableLength(ArrayType const& _type, bool _fromMemory)
|
string ABIFunctions::abiDecodingFunctionByteArrayAvailableLength(ArrayType const& _type, bool _fromMemory)
|
||||||
{
|
{
|
||||||
solAssert(_type.dataStoredIn(DataLocation::Memory), "");
|
solAssert(_type.dataStoredIn(DataLocation::Memory), "");
|
||||||
solAssert(_type.isByteArray(), "");
|
solAssert(_type.isByteArrayOrString(), "");
|
||||||
|
|
||||||
string functionName =
|
string functionName =
|
||||||
"abi_decode_available_length_" +
|
"abi_decode_available_length_" +
|
||||||
|
@ -50,8 +50,8 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
solAssert(_targetType.location() == DataLocation::Storage, "");
|
solAssert(_targetType.location() == DataLocation::Storage, "");
|
||||||
|
|
||||||
Type const* uint256 = TypeProvider::uint256();
|
Type const* uint256 = TypeProvider::uint256();
|
||||||
Type const* targetBaseType = _targetType.isByteArray() ? uint256 : _targetType.baseType();
|
Type const* targetBaseType = _targetType.isByteArrayOrString() ? uint256 : _targetType.baseType();
|
||||||
Type const* sourceBaseType = _sourceType.isByteArray() ? uint256 : _sourceType.baseType();
|
Type const* sourceBaseType = _sourceType.isByteArrayOrString() ? uint256 : _sourceType.baseType();
|
||||||
|
|
||||||
// TODO unroll loop for small sizes
|
// TODO unroll loop for small sizes
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
// stack: target_ref source_ref source_length target_ref target_length
|
// stack: target_ref source_ref source_length target_ref target_length
|
||||||
if (_targetType.isDynamicallySized())
|
if (_targetType.isDynamicallySized())
|
||||||
// store new target length
|
// store new target length
|
||||||
if (!_targetType.isByteArray())
|
if (!_targetType.isByteArrayOrString())
|
||||||
// Otherwise, length will be stored below.
|
// Otherwise, length will be stored below.
|
||||||
_context << Instruction::DUP3 << Instruction::DUP3 << Instruction::SSTORE;
|
_context << Instruction::DUP3 << Instruction::DUP3 << Instruction::SSTORE;
|
||||||
if (sourceBaseType->category() == Type::Category::Mapping)
|
if (sourceBaseType->category() == Type::Category::Mapping)
|
||||||
@ -126,7 +126,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
evmasm::AssemblyItem copyLoopEndWithoutByteOffset = _context.newTag();
|
evmasm::AssemblyItem copyLoopEndWithoutByteOffset = _context.newTag();
|
||||||
|
|
||||||
// special case for short byte arrays: Store them together with their length.
|
// special case for short byte arrays: Store them together with their length.
|
||||||
if (_targetType.isByteArray())
|
if (_targetType.isByteArrayOrString())
|
||||||
{
|
{
|
||||||
// stack: target_ref target_data_end source_length target_data_pos source_ref
|
// stack: target_ref target_data_end source_length target_data_pos source_ref
|
||||||
_context << Instruction::DUP3;
|
_context << Instruction::DUP3;
|
||||||
@ -141,7 +141,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
_context << Instruction::DUP3 << u256(31) << Instruction::LT;
|
_context << Instruction::DUP3 << u256(31) << Instruction::LT;
|
||||||
evmasm::AssemblyItem longByteArray = _context.appendConditionalJump();
|
evmasm::AssemblyItem longByteArray = _context.appendConditionalJump();
|
||||||
// store the short byte array
|
// store the short byte array
|
||||||
solAssert(_sourceType.isByteArray(), "");
|
solAssert(_sourceType.isByteArrayOrString(), "");
|
||||||
if (_sourceType.location() == DataLocation::Storage)
|
if (_sourceType.location() == DataLocation::Storage)
|
||||||
{
|
{
|
||||||
// just copy the slot, it contains length and data
|
// just copy the slot, it contains length and data
|
||||||
@ -323,7 +323,7 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
|||||||
{
|
{
|
||||||
if (!_sourceType.isDynamicallySized())
|
if (!_sourceType.isDynamicallySized())
|
||||||
m_context << _sourceType.length();
|
m_context << _sourceType.length();
|
||||||
if (!_sourceType.isByteArray())
|
if (!_sourceType.isByteArrayOrString())
|
||||||
convertLengthToSize(_sourceType);
|
convertLengthToSize(_sourceType);
|
||||||
|
|
||||||
string routine = "calldatacopy(target, source, len)\n";
|
string routine = "calldatacopy(target, source, len)\n";
|
||||||
@ -375,14 +375,14 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
|||||||
m_context << Instruction::SWAP1 << u256(32) << Instruction::ADD;
|
m_context << Instruction::SWAP1 << u256(32) << Instruction::ADD;
|
||||||
m_context << Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
}
|
}
|
||||||
if (!_sourceType.isByteArray())
|
if (!_sourceType.isByteArrayOrString())
|
||||||
convertLengthToSize(_sourceType);
|
convertLengthToSize(_sourceType);
|
||||||
// stack: <target> <source> <size>
|
// stack: <target> <source> <size>
|
||||||
m_context << Instruction::DUP1 << Instruction::DUP4 << Instruction::DUP4;
|
m_context << Instruction::DUP1 << Instruction::DUP4 << Instruction::DUP4;
|
||||||
// We can resort to copying full 32 bytes only if
|
// We can resort to copying full 32 bytes only if
|
||||||
// - the length is known to be a multiple of 32 or
|
// - the length is known to be a multiple of 32 or
|
||||||
// - we will pad to full 32 bytes later anyway.
|
// - we will pad to full 32 bytes later anyway.
|
||||||
if (!_sourceType.isByteArray() || _padToWordBoundaries)
|
if (!_sourceType.isByteArrayOrString() || _padToWordBoundaries)
|
||||||
utils.memoryCopy32();
|
utils.memoryCopy32();
|
||||||
else
|
else
|
||||||
utils.memoryCopy();
|
utils.memoryCopy();
|
||||||
@ -390,7 +390,7 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
|||||||
m_context << Instruction::SWAP1 << Instruction::POP;
|
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||||
// stack: <target> <size>
|
// stack: <target> <size>
|
||||||
|
|
||||||
bool paddingNeeded = _padToWordBoundaries && _sourceType.isByteArray();
|
bool paddingNeeded = _padToWordBoundaries && _sourceType.isByteArrayOrString();
|
||||||
|
|
||||||
if (paddingNeeded)
|
if (paddingNeeded)
|
||||||
{
|
{
|
||||||
@ -446,7 +446,7 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
|||||||
m_context << Instruction::DUP1 << Instruction::ISZERO;
|
m_context << Instruction::DUP1 << Instruction::ISZERO;
|
||||||
evmasm::AssemblyItem loopEnd = m_context.appendConditionalJump();
|
evmasm::AssemblyItem loopEnd = m_context.appendConditionalJump();
|
||||||
// Special case for tightly-stored byte arrays
|
// Special case for tightly-stored byte arrays
|
||||||
if (_sourceType.isByteArray())
|
if (_sourceType.isByteArrayOrString())
|
||||||
{
|
{
|
||||||
// stack here: memory_offset storage_offset length
|
// stack here: memory_offset storage_offset length
|
||||||
m_context << Instruction::DUP1 << u256(31) << Instruction::LT;
|
m_context << Instruction::DUP1 << u256(31) << Instruction::LT;
|
||||||
@ -482,14 +482,14 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
|||||||
}
|
}
|
||||||
|
|
||||||
// stack here: memory_end_offset storage_data_offset memory_offset
|
// stack here: memory_end_offset storage_data_offset memory_offset
|
||||||
bool haveByteOffset = !_sourceType.isByteArray() && storageBytes <= 16;
|
bool haveByteOffset = !_sourceType.isByteArrayOrString() && storageBytes <= 16;
|
||||||
if (haveByteOffset)
|
if (haveByteOffset)
|
||||||
m_context << u256(0) << Instruction::SWAP1;
|
m_context << u256(0) << Instruction::SWAP1;
|
||||||
// stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
|
// stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
|
||||||
evmasm::AssemblyItem loopStart = m_context.newTag();
|
evmasm::AssemblyItem loopStart = m_context.newTag();
|
||||||
m_context << loopStart;
|
m_context << loopStart;
|
||||||
// load and store
|
// load and store
|
||||||
if (_sourceType.isByteArray())
|
if (_sourceType.isByteArrayOrString())
|
||||||
{
|
{
|
||||||
// Packed both in storage and memory.
|
// Packed both in storage and memory.
|
||||||
m_context << Instruction::DUP2 << Instruction::SLOAD;
|
m_context << Instruction::DUP2 << Instruction::SLOAD;
|
||||||
@ -528,12 +528,12 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
|||||||
// stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
|
// stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
|
||||||
if (haveByteOffset)
|
if (haveByteOffset)
|
||||||
m_context << Instruction::SWAP1 << Instruction::POP;
|
m_context << Instruction::SWAP1 << Instruction::POP;
|
||||||
if (!_sourceType.isByteArray())
|
if (!_sourceType.isByteArrayOrString())
|
||||||
{
|
{
|
||||||
solAssert(_sourceType.calldataStride() % 32 == 0, "");
|
solAssert(_sourceType.calldataStride() % 32 == 0, "");
|
||||||
solAssert(_sourceType.memoryStride() % 32 == 0, "");
|
solAssert(_sourceType.memoryStride() % 32 == 0, "");
|
||||||
}
|
}
|
||||||
if (_padToWordBoundaries && _sourceType.isByteArray())
|
if (_padToWordBoundaries && _sourceType.isByteArrayOrString())
|
||||||
{
|
{
|
||||||
// memory_end_offset - start is the actual length (we want to compute the ceil of).
|
// memory_end_offset - start is the actual length (we want to compute the ceil of).
|
||||||
// memory_offset - start is its next multiple of 32, but it might be off by 32.
|
// memory_offset - start is its next multiple of 32, but it might be off by 32.
|
||||||
@ -624,7 +624,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
|
|||||||
m_context << u256(0) << Instruction::DUP3 << Instruction::SSTORE;
|
m_context << u256(0) << Instruction::DUP3 << Instruction::SSTORE;
|
||||||
// Special case: short byte arrays are stored togeher with their length
|
// Special case: short byte arrays are stored togeher with their length
|
||||||
evmasm::AssemblyItem endTag = m_context.newTag();
|
evmasm::AssemblyItem endTag = m_context.newTag();
|
||||||
if (_type.isByteArray())
|
if (_type.isByteArrayOrString())
|
||||||
{
|
{
|
||||||
// stack: ref old_length
|
// stack: ref old_length
|
||||||
m_context << Instruction::DUP1 << u256(31) << Instruction::LT;
|
m_context << Instruction::DUP1 << u256(31) << Instruction::LT;
|
||||||
@ -664,7 +664,7 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const
|
|||||||
ArrayType const& _type = dynamic_cast<ArrayType const&>(*type);
|
ArrayType const& _type = dynamic_cast<ArrayType const&>(*type);
|
||||||
solAssert(_type.location() == DataLocation::Storage, "");
|
solAssert(_type.location() == DataLocation::Storage, "");
|
||||||
solAssert(_type.isDynamicallySized(), "");
|
solAssert(_type.isDynamicallySized(), "");
|
||||||
if (!_type.isByteArray() && _type.baseType()->storageBytes() < 32)
|
if (!_type.isByteArrayOrString() && _type.baseType()->storageBytes() < 32)
|
||||||
solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
|
solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
|
||||||
|
|
||||||
unsigned stackHeightStart = _context.stackHeight();
|
unsigned stackHeightStart = _context.stackHeight();
|
||||||
@ -677,7 +677,7 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const
|
|||||||
solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "2");
|
solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "2");
|
||||||
|
|
||||||
// Special case for short byte arrays, they are stored together with their length
|
// Special case for short byte arrays, they are stored together with their length
|
||||||
if (_type.isByteArray())
|
if (_type.isByteArrayOrString())
|
||||||
{
|
{
|
||||||
evmasm::AssemblyItem regularPath = _context.newTag();
|
evmasm::AssemblyItem regularPath = _context.newTag();
|
||||||
// We start by a large case-distinction about the old and new length of the byte array.
|
// We start by a large case-distinction about the old and new length of the byte array.
|
||||||
@ -766,7 +766,7 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const
|
|||||||
// stack: ref new_length old_length
|
// stack: ref new_length old_length
|
||||||
// store new length
|
// store new length
|
||||||
_context << Instruction::DUP2;
|
_context << Instruction::DUP2;
|
||||||
if (_type.isByteArray())
|
if (_type.isByteArrayOrString())
|
||||||
// For a "long" byte array, store length as 2*length+1
|
// For a "long" byte array, store length as 2*length+1
|
||||||
_context << Instruction::DUP1 << Instruction::ADD << u256(1) << Instruction::ADD;
|
_context << Instruction::DUP1 << Instruction::ADD << u256(1) << Instruction::ADD;
|
||||||
_context << Instruction::DUP4 << Instruction::SSTORE;
|
_context << Instruction::DUP4 << Instruction::SSTORE;
|
||||||
@ -806,10 +806,10 @@ void ArrayUtils::incrementDynamicArraySize(ArrayType const& _type) const
|
|||||||
{
|
{
|
||||||
solAssert(_type.location() == DataLocation::Storage, "");
|
solAssert(_type.location() == DataLocation::Storage, "");
|
||||||
solAssert(_type.isDynamicallySized(), "");
|
solAssert(_type.isDynamicallySized(), "");
|
||||||
if (!_type.isByteArray() && _type.baseType()->storageBytes() < 32)
|
if (!_type.isByteArrayOrString() && _type.baseType()->storageBytes() < 32)
|
||||||
solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
|
solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
|
||||||
|
|
||||||
if (_type.isByteArray())
|
if (_type.isByteArrayOrString())
|
||||||
{
|
{
|
||||||
// We almost always just add 2 (length of byte arrays is shifted left by one)
|
// We almost always just add 2 (length of byte arrays is shifted left by one)
|
||||||
// except for the case where we transition from a short byte array
|
// except for the case where we transition from a short byte array
|
||||||
@ -850,10 +850,10 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const
|
|||||||
{
|
{
|
||||||
solAssert(_type.location() == DataLocation::Storage, "");
|
solAssert(_type.location() == DataLocation::Storage, "");
|
||||||
solAssert(_type.isDynamicallySized(), "");
|
solAssert(_type.isDynamicallySized(), "");
|
||||||
if (!_type.isByteArray() && _type.baseType()->storageBytes() < 32)
|
if (!_type.isByteArrayOrString() && _type.baseType()->storageBytes() < 32)
|
||||||
solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
|
solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
|
||||||
|
|
||||||
if (_type.isByteArray())
|
if (_type.isByteArrayOrString())
|
||||||
{
|
{
|
||||||
m_context << Instruction::DUP1 << Instruction::SLOAD << Instruction::DUP1;
|
m_context << Instruction::DUP1 << Instruction::SLOAD << Instruction::DUP1;
|
||||||
m_context.callYulFunction(m_context.utilFunctions().extractByteArrayLengthFunction(), 1, 1);
|
m_context.callYulFunction(m_context.utilFunctions().extractByteArrayLengthFunction(), 1, 1);
|
||||||
@ -999,7 +999,7 @@ void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) con
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!_arrayType.isByteArray())
|
if (!_arrayType.isByteArrayOrString())
|
||||||
{
|
{
|
||||||
if (_arrayType.location() == DataLocation::Memory)
|
if (_arrayType.location() == DataLocation::Memory)
|
||||||
m_context << _arrayType.memoryStride();
|
m_context << _arrayType.memoryStride();
|
||||||
@ -1031,7 +1031,7 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType, unsigned _stackDept
|
|||||||
break;
|
break;
|
||||||
case DataLocation::Storage:
|
case DataLocation::Storage:
|
||||||
m_context << Instruction::SLOAD;
|
m_context << Instruction::SLOAD;
|
||||||
if (_arrayType.isByteArray())
|
if (_arrayType.isByteArrayOrString())
|
||||||
m_context.callYulFunction(m_context.utilFunctions().extractByteArrayLengthFunction(), 1, 1);
|
m_context.callYulFunction(m_context.utilFunctions().extractByteArrayLengthFunction(), 1, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1062,7 +1062,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck, b
|
|||||||
{
|
{
|
||||||
case DataLocation::Memory:
|
case DataLocation::Memory:
|
||||||
// stack: <base_ref> <index>
|
// stack: <base_ref> <index>
|
||||||
if (!_arrayType.isByteArray())
|
if (!_arrayType.isByteArrayOrString())
|
||||||
m_context << u256(_arrayType.memoryHeadSize()) << Instruction::MUL;
|
m_context << u256(_arrayType.memoryHeadSize()) << Instruction::MUL;
|
||||||
if (_arrayType.isDynamicallySized())
|
if (_arrayType.isDynamicallySized())
|
||||||
m_context << u256(32) << Instruction::ADD;
|
m_context << u256(32) << Instruction::ADD;
|
||||||
@ -1071,7 +1071,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck, b
|
|||||||
m_context << Instruction::ADD;
|
m_context << Instruction::ADD;
|
||||||
break;
|
break;
|
||||||
case DataLocation::CallData:
|
case DataLocation::CallData:
|
||||||
if (!_arrayType.isByteArray())
|
if (!_arrayType.isByteArrayOrString())
|
||||||
{
|
{
|
||||||
m_context << _arrayType.calldataStride();
|
m_context << _arrayType.calldataStride();
|
||||||
m_context << Instruction::MUL;
|
m_context << Instruction::MUL;
|
||||||
@ -1090,7 +1090,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck, b
|
|||||||
// stack: [<base_ref>] <index> <base_ref>
|
// stack: [<base_ref>] <index> <base_ref>
|
||||||
|
|
||||||
evmasm::AssemblyItem endTag = m_context.newTag();
|
evmasm::AssemblyItem endTag = m_context.newTag();
|
||||||
if (_arrayType.isByteArray())
|
if (_arrayType.isByteArrayOrString())
|
||||||
{
|
{
|
||||||
// Special case of short byte arrays.
|
// Special case of short byte arrays.
|
||||||
m_context << Instruction::SWAP1;
|
m_context << Instruction::SWAP1;
|
||||||
@ -1153,7 +1153,7 @@ void ArrayUtils::accessCallDataArrayElement(ArrayType const& _arrayType, bool _d
|
|||||||
{
|
{
|
||||||
solAssert(_arrayType.baseType()->storageBytes() <= 32, "");
|
solAssert(_arrayType.baseType()->storageBytes() <= 32, "");
|
||||||
if (
|
if (
|
||||||
!_arrayType.isByteArray() &&
|
!_arrayType.isByteArrayOrString() &&
|
||||||
_arrayType.baseType()->storageBytes() < 32 &&
|
_arrayType.baseType()->storageBytes() < 32 &&
|
||||||
m_context.useABICoderV2()
|
m_context.useABICoderV2()
|
||||||
)
|
)
|
||||||
@ -1165,7 +1165,7 @@ void ArrayUtils::accessCallDataArrayElement(ArrayType const& _arrayType, bool _d
|
|||||||
CompilerUtils(m_context).loadFromMemoryDynamic(
|
CompilerUtils(m_context).loadFromMemoryDynamic(
|
||||||
*_arrayType.baseType(),
|
*_arrayType.baseType(),
|
||||||
true,
|
true,
|
||||||
!_arrayType.isByteArray(),
|
!_arrayType.isByteArrayOrString(),
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -970,7 +970,7 @@ void CompilerUtils::convertType(
|
|||||||
else if (targetTypeCategory == Type::Category::Array)
|
else if (targetTypeCategory == Type::Category::Array)
|
||||||
{
|
{
|
||||||
auto const& arrayType = dynamic_cast<ArrayType const&>(_targetType);
|
auto const& arrayType = dynamic_cast<ArrayType const&>(_targetType);
|
||||||
solAssert(arrayType.isByteArray());
|
solAssert(arrayType.isByteArrayOrString());
|
||||||
size_t storageSize = 32 + ((data.size() + 31) / 32) * 32;
|
size_t storageSize = 32 + ((data.size() + 31) / 32) * 32;
|
||||||
allocateMemory(storageSize);
|
allocateMemory(storageSize);
|
||||||
// stack: mempos
|
// stack: mempos
|
||||||
@ -992,7 +992,7 @@ void CompilerUtils::convertType(
|
|||||||
if (_targetType.category() == Type::Category::FixedBytes)
|
if (_targetType.category() == Type::Category::FixedBytes)
|
||||||
{
|
{
|
||||||
solAssert(
|
solAssert(
|
||||||
typeOnStack.isByteArray() && !typeOnStack.isString(),
|
typeOnStack.isByteArray(),
|
||||||
"Array types other than bytes not convertible to bytesNN."
|
"Array types other than bytes not convertible to bytesNN."
|
||||||
);
|
);
|
||||||
solAssert(typeOnStack.isDynamicallySized());
|
solAssert(typeOnStack.isDynamicallySized());
|
||||||
@ -1019,7 +1019,7 @@ void CompilerUtils::convertType(
|
|||||||
case DataLocation::Storage:
|
case DataLocation::Storage:
|
||||||
// Other cases are done explicitly in LValue::storeValue, and only possible by assignment.
|
// Other cases are done explicitly in LValue::storeValue, and only possible by assignment.
|
||||||
solAssert(
|
solAssert(
|
||||||
(targetType.isPointer() || (typeOnStack.isByteArray() && targetType.isByteArray())) &&
|
(targetType.isPointer() || (typeOnStack.isByteArrayOrString() && targetType.isByteArrayOrString())) &&
|
||||||
typeOnStack.location() == DataLocation::Storage,
|
typeOnStack.location() == DataLocation::Storage,
|
||||||
"Invalid conversion to storage type."
|
"Invalid conversion to storage type."
|
||||||
);
|
);
|
||||||
@ -1105,7 +1105,7 @@ void CompilerUtils::convertType(
|
|||||||
}
|
}
|
||||||
case DataLocation::CallData:
|
case DataLocation::CallData:
|
||||||
solAssert(
|
solAssert(
|
||||||
((targetType.isByteArray() && typeOnStack.isByteArray()) || _typeOnStack == _targetType) &&
|
((targetType.isByteArrayOrString() && typeOnStack.isByteArrayOrString()) || _typeOnStack == _targetType) &&
|
||||||
typeOnStack.location() == DataLocation::CallData,
|
typeOnStack.location() == DataLocation::CallData,
|
||||||
"Invalid conversion to calldata type."
|
"Invalid conversion to calldata type."
|
||||||
);
|
);
|
||||||
@ -1119,7 +1119,7 @@ void CompilerUtils::convertType(
|
|||||||
if (_targetType.category() == Type::Category::FixedBytes)
|
if (_targetType.category() == Type::Category::FixedBytes)
|
||||||
{
|
{
|
||||||
solAssert(
|
solAssert(
|
||||||
typeOnStack.arrayType().isByteArray() && !typeOnStack.arrayType().isString(),
|
typeOnStack.arrayType().isByteArray(),
|
||||||
"Array types other than bytes not convertible to bytesNN."
|
"Array types other than bytes not convertible to bytesNN."
|
||||||
);
|
);
|
||||||
solAssert(typeOnStack.isDynamicallySized());
|
solAssert(typeOnStack.isDynamicallySized());
|
||||||
@ -1142,7 +1142,7 @@ void CompilerUtils::convertType(
|
|||||||
auto const& targetArrayType = dynamic_cast<ArrayType const&>(_targetType);
|
auto const& targetArrayType = dynamic_cast<ArrayType const&>(_targetType);
|
||||||
solAssert(
|
solAssert(
|
||||||
typeOnStack.arrayType().isImplicitlyConvertibleTo(targetArrayType) ||
|
typeOnStack.arrayType().isImplicitlyConvertibleTo(targetArrayType) ||
|
||||||
(typeOnStack.arrayType().isByteArray() && targetArrayType.isByteArray())
|
(typeOnStack.arrayType().isByteArrayOrString() && targetArrayType.isByteArrayOrString())
|
||||||
);
|
);
|
||||||
solAssert(
|
solAssert(
|
||||||
typeOnStack.arrayType().dataStoredIn(DataLocation::CallData) &&
|
typeOnStack.arrayType().dataStoredIn(DataLocation::CallData) &&
|
||||||
|
@ -153,7 +153,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
|||||||
if (paramTypes[i]->isDynamicallySized())
|
if (paramTypes[i]->isDynamicallySized())
|
||||||
{
|
{
|
||||||
solAssert(
|
solAssert(
|
||||||
dynamic_cast<ArrayType const&>(*paramTypes[i]).isByteArray(),
|
dynamic_cast<ArrayType const&>(*paramTypes[i]).isByteArrayOrString(),
|
||||||
"Expected string or byte array for mapping key type"
|
"Expected string or byte array for mapping key type"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -239,7 +239,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
|||||||
if (returnTypes[i]->category() == Type::Category::Mapping)
|
if (returnTypes[i]->category() == Type::Category::Mapping)
|
||||||
continue;
|
continue;
|
||||||
if (auto arrayType = dynamic_cast<ArrayType const*>(returnTypes[i]))
|
if (auto arrayType = dynamic_cast<ArrayType const*>(returnTypes[i]))
|
||||||
if (!arrayType->isByteArray())
|
if (!arrayType->isByteArrayOrString())
|
||||||
continue;
|
continue;
|
||||||
pair<u256, unsigned> const& offsets = structType->storageOffsetsOfMember(names[i]);
|
pair<u256, unsigned> const& offsets = structType->storageOffsetsOfMember(names[i]);
|
||||||
m_context << Instruction::DUP1 << u256(offsets.first) << Instruction::ADD << u256(offsets.second);
|
m_context << Instruction::DUP1 << u256(offsets.first) << Instruction::ADD << u256(offsets.second);
|
||||||
@ -1047,7 +1047,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
// stack: ArrayReference (newLength-1)
|
// stack: ArrayReference (newLength-1)
|
||||||
ArrayUtils(m_context).accessIndex(*arrayType, false);
|
ArrayUtils(m_context).accessIndex(*arrayType, false);
|
||||||
|
|
||||||
if (arrayType->isByteArray())
|
if (arrayType->isByteArrayOrString())
|
||||||
setLValue<StorageByteArrayElement>(_functionCall);
|
setLValue<StorageByteArrayElement>(_functionCall);
|
||||||
else
|
else
|
||||||
setLValueToStorageItem(_functionCall);
|
setLValueToStorageItem(_functionCall);
|
||||||
@ -1084,7 +1084,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
utils().moveToStackTop(1 + type->sizeOnStack());
|
utils().moveToStackTop(1 + type->sizeOnStack());
|
||||||
utils().moveToStackTop(1 + type->sizeOnStack());
|
utils().moveToStackTop(1 + type->sizeOnStack());
|
||||||
// stack: argValue storageSlot slotOffset
|
// stack: argValue storageSlot slotOffset
|
||||||
if (!arrayType->isByteArray())
|
if (!arrayType->isByteArrayOrString())
|
||||||
StorageItem(m_context, *paramType).storeValue(*type, _functionCall.location(), true);
|
StorageItem(m_context, *paramType).storeValue(*type, _functionCall.location(), true);
|
||||||
else
|
else
|
||||||
StorageByteArrayElement(m_context).storeValue(*type, _functionCall.location(), true);
|
StorageByteArrayElement(m_context).storeValue(*type, _functionCall.location(), true);
|
||||||
@ -1165,7 +1165,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
// update free memory pointer
|
// update free memory pointer
|
||||||
m_context << Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
// Stack: memptr requested_length requested_length
|
// Stack: memptr requested_length requested_length
|
||||||
if (arrayType.isByteArray())
|
if (arrayType.isByteArrayOrString())
|
||||||
// Round up to multiple of 32
|
// Round up to multiple of 32
|
||||||
m_context << u256(31) << Instruction::ADD << u256(31) << Instruction::NOT << Instruction::AND;
|
m_context << u256(31) << Instruction::ADD << u256(31) << Instruction::NOT << Instruction::AND;
|
||||||
else
|
else
|
||||||
@ -2086,7 +2086,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
|
|||||||
{
|
{
|
||||||
case DataLocation::Storage:
|
case DataLocation::Storage:
|
||||||
ArrayUtils(m_context).accessIndex(arrayType);
|
ArrayUtils(m_context).accessIndex(arrayType);
|
||||||
if (arrayType.isByteArray())
|
if (arrayType.isByteArrayOrString())
|
||||||
{
|
{
|
||||||
solAssert(!arrayType.isString(), "Index access to string is not allowed.");
|
solAssert(!arrayType.isString(), "Index access to string is not allowed.");
|
||||||
setLValue<StorageByteArrayElement>(_indexAccess);
|
setLValue<StorageByteArrayElement>(_indexAccess);
|
||||||
@ -2096,7 +2096,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
|
|||||||
break;
|
break;
|
||||||
case DataLocation::Memory:
|
case DataLocation::Memory:
|
||||||
ArrayUtils(m_context).accessIndex(arrayType);
|
ArrayUtils(m_context).accessIndex(arrayType);
|
||||||
setLValue<MemoryItem>(_indexAccess, *_indexAccess.annotation().type, !arrayType.isByteArray());
|
setLValue<MemoryItem>(_indexAccess, *_indexAccess.annotation().type, !arrayType.isByteArrayOrString());
|
||||||
break;
|
break;
|
||||||
case DataLocation::CallData:
|
case DataLocation::CallData:
|
||||||
ArrayUtils(m_context).accessCallDataArrayElement(arrayType);
|
ArrayUtils(m_context).accessCallDataArrayElement(arrayType);
|
||||||
|
@ -1183,8 +1183,8 @@ string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type)
|
|||||||
w("calldata", _type.location() == DataLocation::CallData);
|
w("calldata", _type.location() == DataLocation::CallData);
|
||||||
if (_type.location() == DataLocation::Storage)
|
if (_type.location() == DataLocation::Storage)
|
||||||
{
|
{
|
||||||
w("byteArray", _type.isByteArray());
|
w("byteArray", _type.isByteArrayOrString());
|
||||||
if (_type.isByteArray())
|
if (_type.isByteArrayOrString())
|
||||||
w("extractByteArrayLength", extractByteArrayLengthFunction());
|
w("extractByteArrayLength", extractByteArrayLengthFunction());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1220,7 +1220,7 @@ std::string YulUtilFunctions::resizeArrayFunction(ArrayType const& _type)
|
|||||||
solAssert(_type.location() == DataLocation::Storage, "");
|
solAssert(_type.location() == DataLocation::Storage, "");
|
||||||
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32);
|
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32);
|
||||||
|
|
||||||
if (_type.isByteArray())
|
if (_type.isByteArrayOrString())
|
||||||
return resizeDynamicByteArrayFunction(_type);
|
return resizeDynamicByteArrayFunction(_type);
|
||||||
|
|
||||||
string functionName = "resize_array_" + _type.identifier();
|
string functionName = "resize_array_" + _type.identifier();
|
||||||
@ -1259,7 +1259,7 @@ string YulUtilFunctions::cleanUpStorageArrayEndFunction(ArrayType const& _type)
|
|||||||
{
|
{
|
||||||
solAssert(_type.location() == DataLocation::Storage, "");
|
solAssert(_type.location() == DataLocation::Storage, "");
|
||||||
solAssert(_type.baseType()->category() != Type::Category::Mapping, "");
|
solAssert(_type.baseType()->category() != Type::Category::Mapping, "");
|
||||||
solAssert(!_type.isByteArray(), "");
|
solAssert(!_type.isByteArrayOrString(), "");
|
||||||
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32);
|
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32);
|
||||||
|
|
||||||
string functionName = "cleanup_storage_array_end_" + _type.identifier();
|
string functionName = "cleanup_storage_array_end_" + _type.identifier();
|
||||||
@ -1319,7 +1319,7 @@ string YulUtilFunctions::resizeDynamicByteArrayFunction(ArrayType const& _type)
|
|||||||
|
|
||||||
string YulUtilFunctions::cleanUpDynamicByteArrayEndSlotsFunction(ArrayType const& _type)
|
string YulUtilFunctions::cleanUpDynamicByteArrayEndSlotsFunction(ArrayType const& _type)
|
||||||
{
|
{
|
||||||
solAssert(_type.isByteArray(), "");
|
solAssert(_type.isByteArrayOrString(), "");
|
||||||
solAssert(_type.isDynamicallySized(), "");
|
solAssert(_type.isDynamicallySized(), "");
|
||||||
|
|
||||||
string functionName = "clean_up_bytearray_end_slots_" + _type.identifier();
|
string functionName = "clean_up_bytearray_end_slots_" + _type.identifier();
|
||||||
@ -1479,7 +1479,7 @@ string YulUtilFunctions::storageArrayPopFunction(ArrayType const& _type)
|
|||||||
solAssert(_type.location() == DataLocation::Storage, "");
|
solAssert(_type.location() == DataLocation::Storage, "");
|
||||||
solAssert(_type.isDynamicallySized(), "");
|
solAssert(_type.isDynamicallySized(), "");
|
||||||
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented.");
|
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented.");
|
||||||
if (_type.isByteArray())
|
if (_type.isByteArrayOrString())
|
||||||
return storageByteArrayPopFunction(_type);
|
return storageByteArrayPopFunction(_type);
|
||||||
|
|
||||||
string functionName = "array_pop_" + _type.identifier();
|
string functionName = "array_pop_" + _type.identifier();
|
||||||
@ -1509,7 +1509,7 @@ string YulUtilFunctions::storageByteArrayPopFunction(ArrayType const& _type)
|
|||||||
{
|
{
|
||||||
solAssert(_type.location() == DataLocation::Storage, "");
|
solAssert(_type.location() == DataLocation::Storage, "");
|
||||||
solAssert(_type.isDynamicallySized(), "");
|
solAssert(_type.isDynamicallySized(), "");
|
||||||
solAssert(_type.isByteArray(), "");
|
solAssert(_type.isByteArrayOrString(), "");
|
||||||
|
|
||||||
string functionName = "byte_array_pop_" + _type.identifier();
|
string functionName = "byte_array_pop_" + _type.identifier();
|
||||||
return m_functionCollector.createFunction(functionName, [&]() {
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
@ -1566,7 +1566,7 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type, Type c
|
|||||||
return m_functionCollector.createFunction(functionName, [&]() {
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(array <values>) {
|
function <functionName>(array <values>) {
|
||||||
<?isByteArray>
|
<?isByteArrayOrString>
|
||||||
let data := sload(array)
|
let data := sload(array)
|
||||||
let oldLen := <extractByteArrayLength>(data)
|
let oldLen := <extractByteArrayLength>(data)
|
||||||
if iszero(lt(oldLen, <maxArrayLength>)) { <panic>() }
|
if iszero(lt(oldLen, <maxArrayLength>)) { <panic>() }
|
||||||
@ -1598,20 +1598,20 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type, Type c
|
|||||||
let slot, offset := <indexAccess>(array, oldLen)
|
let slot, offset := <indexAccess>(array, oldLen)
|
||||||
<storeValue>(slot, offset <values>)
|
<storeValue>(slot, offset <values>)
|
||||||
}
|
}
|
||||||
<!isByteArray>
|
<!isByteArrayOrString>
|
||||||
let oldLen := sload(array)
|
let oldLen := sload(array)
|
||||||
if iszero(lt(oldLen, <maxArrayLength>)) { <panic>() }
|
if iszero(lt(oldLen, <maxArrayLength>)) { <panic>() }
|
||||||
sstore(array, add(oldLen, 1))
|
sstore(array, add(oldLen, 1))
|
||||||
let slot, offset := <indexAccess>(array, oldLen)
|
let slot, offset := <indexAccess>(array, oldLen)
|
||||||
<storeValue>(slot, offset <values>)
|
<storeValue>(slot, offset <values>)
|
||||||
</isByteArray>
|
</isByteArrayOrString>
|
||||||
})")
|
})")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("values", _fromType->sizeOnStack() == 0 ? "" : ", " + suffixedVariableNameList("value", 0, _fromType->sizeOnStack()))
|
("values", _fromType->sizeOnStack() == 0 ? "" : ", " + suffixedVariableNameList("value", 0, _fromType->sizeOnStack()))
|
||||||
("panic", panicFunction(PanicCode::ResourceError))
|
("panic", panicFunction(PanicCode::ResourceError))
|
||||||
("extractByteArrayLength", _type.isByteArray() ? extractByteArrayLengthFunction() : "")
|
("extractByteArrayLength", _type.isByteArrayOrString() ? extractByteArrayLengthFunction() : "")
|
||||||
("dataAreaFunction", arrayDataAreaFunction(_type))
|
("dataAreaFunction", arrayDataAreaFunction(_type))
|
||||||
("isByteArray", _type.isByteArray())
|
("isByteArrayOrString", _type.isByteArrayOrString())
|
||||||
("indexAccess", storageArrayIndexAccessFunction(_type))
|
("indexAccess", storageArrayIndexAccessFunction(_type))
|
||||||
("storeValue", updateStorageValueFunction(*_fromType, *_type.baseType()))
|
("storeValue", updateStorageValueFunction(*_fromType, *_type.baseType()))
|
||||||
("maxArrayLength", (u256(1) << 64).str())
|
("maxArrayLength", (u256(1) << 64).str())
|
||||||
@ -1642,9 +1642,9 @@ string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _type)
|
|||||||
slot, offset := <indexAccess>(array, oldLen)
|
slot, offset := <indexAccess>(array, oldLen)
|
||||||
})")
|
})")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("isBytes", _type.isByteArray())
|
("isBytes", _type.isByteArrayOrString())
|
||||||
("increaseBytesSize", _type.isByteArray() ? increaseByteArraySizeFunction(_type) : "")
|
("increaseBytesSize", _type.isByteArrayOrString() ? increaseByteArraySizeFunction(_type) : "")
|
||||||
("extractLength", _type.isByteArray() ? extractByteArrayLengthFunction() : "")
|
("extractLength", _type.isByteArrayOrString() ? extractByteArrayLengthFunction() : "")
|
||||||
("panic", panicFunction(PanicCode::ResourceError))
|
("panic", panicFunction(PanicCode::ResourceError))
|
||||||
("fetchLength", arrayLengthFunction(_type))
|
("fetchLength", arrayLengthFunction(_type))
|
||||||
("indexAccess", storageArrayIndexAccessFunction(_type))
|
("indexAccess", storageArrayIndexAccessFunction(_type))
|
||||||
@ -1795,7 +1795,7 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
|
|||||||
if (!_toType.isDynamicallySized())
|
if (!_toType.isDynamicallySized())
|
||||||
solAssert(!_fromType.isDynamicallySized() && _fromType.length() <= _toType.length(), "");
|
solAssert(!_fromType.isDynamicallySized() && _fromType.length() <= _toType.length(), "");
|
||||||
|
|
||||||
if (_fromType.isByteArray())
|
if (_fromType.isByteArrayOrString())
|
||||||
return copyByteArrayToStorageFunction(_fromType, _toType);
|
return copyByteArrayToStorageFunction(_fromType, _toType);
|
||||||
if (_fromType.dataStoredIn(DataLocation::Storage) && _toType.baseType()->isValueType())
|
if (_fromType.dataStoredIn(DataLocation::Storage) && _toType.baseType()->isValueType())
|
||||||
return copyValueArrayStorageToStorageFunction(_fromType, _toType);
|
return copyValueArrayStorageToStorageFunction(_fromType, _toType);
|
||||||
@ -1902,8 +1902,8 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy
|
|||||||
*_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast<ReferenceType const&>(_toType),
|
*_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast<ReferenceType const&>(_toType),
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
solAssert(_fromType.isByteArray(), "");
|
solAssert(_fromType.isByteArrayOrString(), "");
|
||||||
solAssert(_toType.isByteArray(), "");
|
solAssert(_toType.isByteArrayOrString(), "");
|
||||||
|
|
||||||
string functionName = "copy_byte_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
|
string functionName = "copy_byte_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
|
||||||
return m_functionCollector.createFunction(functionName, [&](){
|
return m_functionCollector.createFunction(functionName, [&](){
|
||||||
@ -1980,8 +1980,8 @@ string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const&
|
|||||||
solAssert(_toType.baseType()->isValueType(), "");
|
solAssert(_toType.baseType()->isValueType(), "");
|
||||||
solAssert(_fromType.baseType()->isImplicitlyConvertibleTo(*_toType.baseType()), "");
|
solAssert(_fromType.baseType()->isImplicitlyConvertibleTo(*_toType.baseType()), "");
|
||||||
|
|
||||||
solAssert(!_fromType.isByteArray(), "");
|
solAssert(!_fromType.isByteArrayOrString(), "");
|
||||||
solAssert(!_toType.isByteArray(), "");
|
solAssert(!_toType.isByteArrayOrString(), "");
|
||||||
solAssert(_fromType.dataStoredIn(DataLocation::Storage), "");
|
solAssert(_fromType.dataStoredIn(DataLocation::Storage), "");
|
||||||
solAssert(_toType.dataStoredIn(DataLocation::Storage), "");
|
solAssert(_toType.dataStoredIn(DataLocation::Storage), "");
|
||||||
|
|
||||||
@ -2155,7 +2155,7 @@ string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type)
|
|||||||
})")
|
})")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("stride", to_string(_type.location() == DataLocation::Memory ? _type.memoryStride() : _type.calldataStride()))
|
("stride", to_string(_type.location() == DataLocation::Memory ? _type.memoryStride() : _type.calldataStride()))
|
||||||
("byteArray", _type.isByteArray())
|
("byteArray", _type.isByteArrayOrString())
|
||||||
("mul", overflowCheckedIntMulFunction(*TypeProvider::uint256()))
|
("mul", overflowCheckedIntMulFunction(*TypeProvider::uint256()))
|
||||||
.render();
|
.render();
|
||||||
default:
|
default:
|
||||||
@ -2187,7 +2187,7 @@ string YulUtilFunctions::arrayAllocationSizeFunction(ArrayType const& _type)
|
|||||||
)");
|
)");
|
||||||
w("functionName", functionName);
|
w("functionName", functionName);
|
||||||
w("panic", panicFunction(PanicCode::ResourceError));
|
w("panic", panicFunction(PanicCode::ResourceError));
|
||||||
w("byteArray", _type.isByteArray());
|
w("byteArray", _type.isByteArrayOrString());
|
||||||
w("roundUp", roundUpFunction());
|
w("roundUp", roundUpFunction());
|
||||||
w("dynamic", _type.isDynamicallySized());
|
w("dynamic", _type.isDynamicallySized());
|
||||||
return w.render();
|
return w.render();
|
||||||
@ -2262,7 +2262,7 @@ string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type)
|
|||||||
("dataAreaFunc", arrayDataAreaFunction(_type))
|
("dataAreaFunc", arrayDataAreaFunction(_type))
|
||||||
("indexAccessNoChecks", longByteArrayStorageIndexAccessNoCheckFunction())
|
("indexAccessNoChecks", longByteArrayStorageIndexAccessNoCheckFunction())
|
||||||
("multipleItemsPerSlot", _type.baseType()->storageBytes() <= 16)
|
("multipleItemsPerSlot", _type.baseType()->storageBytes() <= 16)
|
||||||
("isBytesArray", _type.isByteArray())
|
("isBytesArray", _type.isByteArrayOrString())
|
||||||
("storageSize", _type.baseType()->storageSize().str())
|
("storageSize", _type.baseType()->storageSize().str())
|
||||||
("storageBytes", toString(_type.baseType()->storageBytes()))
|
("storageBytes", toString(_type.baseType()->storageBytes()))
|
||||||
("itemsPerSlot", to_string(32 / _type.baseType()->storageBytes()))
|
("itemsPerSlot", to_string(32 / _type.baseType()->storageBytes()))
|
||||||
@ -2376,7 +2376,7 @@ string YulUtilFunctions::accessCalldataTailFunction(Type const& _type)
|
|||||||
|
|
||||||
string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
|
string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
|
||||||
{
|
{
|
||||||
solAssert(!_type.isByteArray(), "");
|
solAssert(!_type.isByteArrayOrString(), "");
|
||||||
if (_type.dataStoredIn(DataLocation::Storage))
|
if (_type.dataStoredIn(DataLocation::Storage))
|
||||||
solAssert(_type.baseType()->storageBytes() > 16, "");
|
solAssert(_type.baseType()->storageBytes() > 16, "");
|
||||||
string functionName = "array_nextElement_" + _type.identifier();
|
string functionName = "array_nextElement_" + _type.identifier();
|
||||||
@ -2447,7 +2447,7 @@ string YulUtilFunctions::copyArrayFromStorageToMemoryFunction(ArrayType const& _
|
|||||||
solAssert(_to.memoryStride() == 32, "");
|
solAssert(_to.memoryStride() == 32, "");
|
||||||
solAssert(_to.baseType()->dataStoredIn(DataLocation::Memory), "");
|
solAssert(_to.baseType()->dataStoredIn(DataLocation::Memory), "");
|
||||||
solAssert(_from.baseType()->dataStoredIn(DataLocation::Storage), "");
|
solAssert(_from.baseType()->dataStoredIn(DataLocation::Storage), "");
|
||||||
solAssert(!_from.isByteArray(), "");
|
solAssert(!_from.isByteArrayOrString(), "");
|
||||||
solAssert(*_to.withLocation(DataLocation::Storage, _from.isPointer()) == _from, "");
|
solAssert(*_to.withLocation(DataLocation::Storage, _from.isPointer()) == _from, "");
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(slot) -> memPtr {
|
function <functionName>(slot) -> memPtr {
|
||||||
@ -2755,7 +2755,7 @@ string YulUtilFunctions::updateStorageValueFunction(
|
|||||||
solAssert(_fromType.category() == Type::Category::StringLiteral, "");
|
solAssert(_fromType.category() == Type::Category::StringLiteral, "");
|
||||||
solAssert(toReferenceType->category() == Type::Category::Array, "");
|
solAssert(toReferenceType->category() == Type::Category::Array, "");
|
||||||
auto const& toArrayType = dynamic_cast<ArrayType const&>(*toReferenceType);
|
auto const& toArrayType = dynamic_cast<ArrayType const&>(*toReferenceType);
|
||||||
solAssert(toArrayType.isByteArray(), "");
|
solAssert(toArrayType.isByteArrayOrString(), "");
|
||||||
|
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(slot<?dynamicOffset>, offset</dynamicOffset>) {
|
function <functionName>(slot<?dynamicOffset>, offset</dynamicOffset>) {
|
||||||
@ -3224,7 +3224,7 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
|||||||
|
|
||||||
solAssert(
|
solAssert(
|
||||||
fromType.arrayType().isImplicitlyConvertibleTo(targetType) ||
|
fromType.arrayType().isImplicitlyConvertibleTo(targetType) ||
|
||||||
(fromType.arrayType().isByteArray() && targetType.isByteArray())
|
(fromType.arrayType().isByteArrayOrString() && targetType.isByteArrayOrString())
|
||||||
);
|
);
|
||||||
solAssert(
|
solAssert(
|
||||||
fromType.arrayType().dataStoredIn(DataLocation::CallData) &&
|
fromType.arrayType().dataStoredIn(DataLocation::CallData) &&
|
||||||
@ -3460,7 +3460,7 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
|||||||
|
|
||||||
string YulUtilFunctions::bytesToFixedBytesConversionFunction(ArrayType const& _from, FixedBytesType const& _to)
|
string YulUtilFunctions::bytesToFixedBytesConversionFunction(ArrayType const& _from, FixedBytesType const& _to)
|
||||||
{
|
{
|
||||||
solAssert(_from.isByteArray() && !_from.isString(), "");
|
solAssert(_from.isByteArray(), "");
|
||||||
solAssert(_from.isDynamicallySized(), "");
|
solAssert(_from.isDynamicallySized(), "");
|
||||||
string functionName = "convert_bytes_to_fixedbytes_from_" + _from.identifier() + "_to_" + _to.identifier();
|
string functionName = "convert_bytes_to_fixedbytes_from_" + _from.identifier() + "_to_" + _to.identifier();
|
||||||
return m_functionCollector.createFunction(functionName, [&](auto& _args, auto& _returnParams) {
|
return m_functionCollector.createFunction(functionName, [&](auto& _args, auto& _returnParams) {
|
||||||
@ -3633,14 +3633,14 @@ string YulUtilFunctions::arrayConversionFunction(ArrayType const& _from, ArrayTy
|
|||||||
{
|
{
|
||||||
if (_to.dataStoredIn(DataLocation::CallData))
|
if (_to.dataStoredIn(DataLocation::CallData))
|
||||||
solAssert(
|
solAssert(
|
||||||
_from.dataStoredIn(DataLocation::CallData) && _from.isByteArray() && _to.isByteArray(),
|
_from.dataStoredIn(DataLocation::CallData) && _from.isByteArrayOrString() && _to.isByteArrayOrString(),
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
|
|
||||||
// Other cases are done explicitly in LValue::storeValue, and only possible by assignment.
|
// Other cases are done explicitly in LValue::storeValue, and only possible by assignment.
|
||||||
if (_to.location() == DataLocation::Storage)
|
if (_to.location() == DataLocation::Storage)
|
||||||
solAssert(
|
solAssert(
|
||||||
(_to.isPointer() || (_from.isByteArray() && _to.isByteArray())) &&
|
(_to.isPointer() || (_from.isByteArrayOrString() && _to.isByteArrayOrString())) &&
|
||||||
_from.location() == DataLocation::Storage,
|
_from.location() == DataLocation::Storage,
|
||||||
"Invalid conversion to storage type."
|
"Invalid conversion to storage type."
|
||||||
);
|
);
|
||||||
@ -4238,7 +4238,7 @@ string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const
|
|||||||
}
|
}
|
||||||
else if (_to.category() == Type::Category::Array)
|
else if (_to.category() == Type::Category::Array)
|
||||||
{
|
{
|
||||||
solAssert(dynamic_cast<ArrayType const&>(_to).isByteArray(), "");
|
solAssert(dynamic_cast<ArrayType const&>(_to).isByteArrayOrString(), "");
|
||||||
Whiskers templ(R"(
|
Whiskers templ(R"(
|
||||||
function <functionName>() -> converted {
|
function <functionName>() -> converted {
|
||||||
converted := <copyLiteralToMemory>()
|
converted := <copyLiteralToMemory>()
|
||||||
|
@ -677,7 +677,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
|||||||
continue;
|
continue;
|
||||||
if (
|
if (
|
||||||
auto const* arrayType = dynamic_cast<ArrayType const*>(returnTypes[i]);
|
auto const* arrayType = dynamic_cast<ArrayType const*>(returnTypes[i]);
|
||||||
arrayType && !arrayType->isByteArray()
|
arrayType && !arrayType->isByteArrayOrString()
|
||||||
)
|
)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -698,7 +698,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
|||||||
solAssert(returnTypes.size() == 1, "");
|
solAssert(returnTypes.size() == 1, "");
|
||||||
auto const* arrayType = dynamic_cast<ArrayType const*>(returnTypes.front());
|
auto const* arrayType = dynamic_cast<ArrayType const*>(returnTypes.front());
|
||||||
if (arrayType)
|
if (arrayType)
|
||||||
solAssert(arrayType->isByteArray(), "");
|
solAssert(arrayType->isByteArrayOrString(), "");
|
||||||
vector<string> retVars = IRVariable("ret", *returnTypes.front()).stackSlots();
|
vector<string> retVars = IRVariable("ret", *returnTypes.front()).stackSlots();
|
||||||
returnVariables += retVars;
|
returnVariables += retVars;
|
||||||
code += Whiskers(R"(
|
code += Whiskers(R"(
|
||||||
|
@ -805,8 +805,8 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
|||||||
if (auto type = dynamic_cast<IntegerType const*>(commonType))
|
if (auto type = dynamic_cast<IntegerType const*>(commonType))
|
||||||
isSigned = type->isSigned();
|
isSigned = type->isSigned();
|
||||||
|
|
||||||
string args = expressionAsType(_binOp.leftExpression(), *commonType, true);
|
string args = expressionAsCleanedType(_binOp.leftExpression(), *commonType);
|
||||||
args += ", " + expressionAsType(_binOp.rightExpression(), *commonType, true);
|
args += ", " + expressionAsCleanedType(_binOp.rightExpression(), *commonType);
|
||||||
|
|
||||||
auto functionType = dynamic_cast<FunctionType const*>(commonType);
|
auto functionType = dynamic_cast<FunctionType const*>(commonType);
|
||||||
solAssert(functionType ? (op == Token::Equal || op == Token::NotEqual) : true, "Invalid function pointer comparison!");
|
solAssert(functionType ? (op == Token::Equal || op == Token::NotEqual) : true, "Invalid function pointer comparison!");
|
||||||
@ -1037,7 +1037,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(parameterTypes[i]->sizeOnStack() == 1, "");
|
solAssert(parameterTypes[i]->sizeOnStack() == 1, "");
|
||||||
indexedArgs.emplace_back(convert(arg, *paramTypes[i], true));
|
indexedArgs.emplace_back(convertAndCleanup(arg, *parameterTypes[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2088,7 +2088,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
else if (dynamic_cast<UserDefinedValueType const*>(&actualType))
|
else if (dynamic_cast<UserDefinedValueType const*>(&actualType))
|
||||||
solAssert(member == "wrap" || member == "unwrap");
|
solAssert(member == "wrap" || member == "unwrap");
|
||||||
else if (auto const* arrayType = dynamic_cast<ArrayType const*>(&actualType))
|
else if (auto const* arrayType = dynamic_cast<ArrayType const*>(&actualType))
|
||||||
solAssert(arrayType->isByteArray() && member == "concat");
|
solAssert(arrayType->isByteArrayOrString() && member == "concat");
|
||||||
else
|
else
|
||||||
// The old code generator had a generic "else" case here
|
// The old code generator had a generic "else" case here
|
||||||
// without any specific code being generated,
|
// without any specific code being generated,
|
||||||
@ -2226,7 +2226,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
|
|||||||
|
|
||||||
setLValue(_indexAccess, IRLValue{
|
setLValue(_indexAccess, IRLValue{
|
||||||
*arrayType.baseType(),
|
*arrayType.baseType(),
|
||||||
IRLValue::Memory{memAddress, arrayType.isByteArray()}
|
IRLValue::Memory{memAddress, arrayType.isByteArrayOrString()}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2240,7 +2240,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
|
|||||||
", " +
|
", " +
|
||||||
expressionAsType(*_indexAccess.indexExpression(), *TypeProvider::uint256()) +
|
expressionAsType(*_indexAccess.indexExpression(), *TypeProvider::uint256()) +
|
||||||
")";
|
")";
|
||||||
if (arrayType.isByteArray())
|
if (arrayType.isByteArrayOrString())
|
||||||
define(_indexAccess) <<
|
define(_indexAccess) <<
|
||||||
m_utils.cleanupFunction(*arrayType.baseType()) <<
|
m_utils.cleanupFunction(*arrayType.baseType()) <<
|
||||||
"(calldataload(" <<
|
"(calldataload(" <<
|
||||||
@ -2727,32 +2727,43 @@ void IRGeneratorForStatements::assignInternalFunctionIDIfNotCalledDirectly(
|
|||||||
m_context.addToInternalDispatch(_referencedFunction);
|
m_context.addToInternalDispatch(_referencedFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
IRVariable IRGeneratorForStatements::convert(IRVariable const& _from, Type const& _to, bool _forceCleanup)
|
IRVariable IRGeneratorForStatements::convert(IRVariable const& _from, Type const& _to)
|
||||||
{
|
{
|
||||||
if (_from.type() == _to && !_forceCleanup)
|
if (_from.type() == _to)
|
||||||
return _from;
|
return _from;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
IRVariable converted(m_context.newYulVariable(), _to);
|
IRVariable converted(m_context.newYulVariable(), _to);
|
||||||
define(converted, _from, _forceCleanup);
|
define(converted, _from);
|
||||||
return converted;
|
return converted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string IRGeneratorForStatements::expressionAsType(Expression const& _expression, Type const& _to, bool _forceCleanup)
|
IRVariable IRGeneratorForStatements::convertAndCleanup(IRVariable const& _from, Type const& _to)
|
||||||
|
{
|
||||||
|
IRVariable converted(m_context.newYulVariable(), _to);
|
||||||
|
defineAndCleanup(converted, _from);
|
||||||
|
return converted;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string IRGeneratorForStatements::expressionAsType(Expression const& _expression, Type const& _to)
|
||||||
{
|
{
|
||||||
IRVariable from(_expression);
|
IRVariable from(_expression);
|
||||||
if (from.type() == _to)
|
if (from.type() == _to)
|
||||||
{
|
return from.commaSeparatedList();
|
||||||
if (_forceCleanup)
|
|
||||||
return m_utils.cleanupFunction(_to) + "(" + from.commaSeparatedList() + ")";
|
|
||||||
else
|
|
||||||
return from.commaSeparatedList();
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
return m_utils.conversionFunction(from.type(), _to) + "(" + from.commaSeparatedList() + ")";
|
return m_utils.conversionFunction(from.type(), _to) + "(" + from.commaSeparatedList() + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string IRGeneratorForStatements::expressionAsCleanedType(Expression const& _expression, Type const& _to)
|
||||||
|
{
|
||||||
|
IRVariable from(_expression);
|
||||||
|
if (from.type() == _to)
|
||||||
|
return m_utils.cleanupFunction(_to) + "(" + expressionAsType(_expression, _to) + ")";
|
||||||
|
else
|
||||||
|
return expressionAsType(_expression, _to) ;
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream& IRGeneratorForStatements::define(IRVariable const& _var)
|
std::ostream& IRGeneratorForStatements::define(IRVariable const& _var)
|
||||||
{
|
{
|
||||||
if (_var.type().sizeOnStack() > 0)
|
if (_var.type().sizeOnStack() > 0)
|
||||||
@ -2985,8 +2996,11 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
|
|||||||
{
|
{
|
||||||
solAssert(_lvalue.type.sizeOnStack() == 1);
|
solAssert(_lvalue.type.sizeOnStack() == 1);
|
||||||
auto const* valueReferenceType = dynamic_cast<ReferenceType const*>(&_value.type());
|
auto const* valueReferenceType = dynamic_cast<ReferenceType const*>(&_value.type());
|
||||||
solAssert(valueReferenceType && valueReferenceType->dataStoredIn(DataLocation::Memory));
|
solAssert(valueReferenceType);
|
||||||
appendCode() << "mstore(" + _memory.address + ", " + _value.part("mpos").name() + ")\n";
|
if (valueReferenceType->dataStoredIn(DataLocation::Memory))
|
||||||
|
appendCode() << "mstore(" + _memory.address + ", " + _value.part("mpos").name() + ")\n";
|
||||||
|
else
|
||||||
|
appendCode() << "mstore(" + _memory.address + ", " + m_utils.conversionFunction(_value.type(), _lvalue.type) + "(" + _value.commaSeparatedList() + "))\n";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[&](IRLValue::Stack const& _stack) { assign(_stack.variable, _value); },
|
[&](IRLValue::Stack const& _stack) { assign(_stack.variable, _value); },
|
||||||
|
@ -86,10 +86,19 @@ public:
|
|||||||
IRVariable evaluateExpression(Expression const& _expression, Type const& _to);
|
IRVariable evaluateExpression(Expression const& _expression, Type const& _to);
|
||||||
|
|
||||||
/// Defines @a _var using the value of @a _value while performing type conversions, if required.
|
/// Defines @a _var using the value of @a _value while performing type conversions, if required.
|
||||||
/// If @a _forceCleanup is set to true, it also cleans the value of the variable after the conversion.
|
void define(IRVariable const& _var, IRVariable const& _value)
|
||||||
void define(IRVariable const& _var, IRVariable const& _value, bool _forceCleanup = false)
|
|
||||||
{
|
{
|
||||||
declareAssign(_var, _value, true, _forceCleanup);
|
bool _declare = true;
|
||||||
|
declareAssign(_var, _value, _declare);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Defines @a _var using the value of @a _value while performing type conversions, if required.
|
||||||
|
/// It also cleans the value of the variable.
|
||||||
|
void defineAndCleanup(IRVariable const& _var, IRVariable const& _value)
|
||||||
|
{
|
||||||
|
bool _forceCleanup = true;
|
||||||
|
bool _declare = true;
|
||||||
|
declareAssign(_var, _value, _declare, _forceCleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @returns the name of a function that computes the value of the given constant
|
/// @returns the name of a function that computes the value of the given constant
|
||||||
@ -166,13 +175,20 @@ private:
|
|||||||
);
|
);
|
||||||
|
|
||||||
/// Generates the required conversion code and @returns an IRVariable referring to the value of @a _variable
|
/// Generates the required conversion code and @returns an IRVariable referring to the value of @a _variable
|
||||||
/// If @a _forceCleanup is set to true, it also cleans the value of the variable after the conversion.
|
IRVariable convert(IRVariable const& _variable, Type const& _to);
|
||||||
IRVariable convert(IRVariable const& _variable, Type const& _to, bool _forceCleanup = false);
|
|
||||||
|
/// Generates the required conversion code and @returns an IRVariable referring to the value of @a _variable
|
||||||
|
/// It also cleans the value of the variable.
|
||||||
|
IRVariable convertAndCleanup(IRVariable const& _from, Type const& _to);
|
||||||
|
|
||||||
/// @returns a Yul expression representing the current value of @a _expression,
|
/// @returns a Yul expression representing the current value of @a _expression,
|
||||||
/// converted to type @a _to if it does not yet have that type.
|
/// converted to type @a _to if it does not yet have that type.
|
||||||
/// If @a _forceCleanup is set to true, it also cleans the value, in case it already has type @a _to.
|
std::string expressionAsType(Expression const& _expression, Type const& _to);
|
||||||
std::string expressionAsType(Expression const& _expression, Type const& _to, bool _forceCleanup = false);
|
|
||||||
|
/// @returns a Yul expression representing the current value of @a _expression,
|
||||||
|
/// converted to type @a _to if it does not yet have that type.
|
||||||
|
/// It also cleans the value, in case it already has type @a _to.
|
||||||
|
std::string expressionAsCleanedType(Expression const& _expression, Type const& _to);
|
||||||
|
|
||||||
/// @returns an output stream that can be used to define @a _var using a function call or
|
/// @returns an output stream that can be used to define @a _var using a function call or
|
||||||
/// single stack slot expression.
|
/// single stack slot expression.
|
||||||
|
@ -953,7 +953,7 @@ bool isReturnedFromStructGetter(Type const* _type)
|
|||||||
if (category == Type::Category::Mapping)
|
if (category == Type::Category::Mapping)
|
||||||
return false;
|
return false;
|
||||||
if (category == Type::Category::Array)
|
if (category == Type::Category::Array)
|
||||||
return dynamic_cast<ArrayType const&>(*_type).isByteArray();
|
return dynamic_cast<ArrayType const&>(*_type).isByteArrayOrString();
|
||||||
// default
|
// default
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -990,7 +990,7 @@ void SMTEncoder::visitPublicGetter(FunctionCall const& _funCall)
|
|||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
type->isValueType() ||
|
type->isValueType() ||
|
||||||
(type->category() == Type::Category::Array && dynamic_cast<ArrayType const&>(*type).isByteArray())
|
(type->category() == Type::Category::Array && dynamic_cast<ArrayType const&>(*type).isByteArrayOrString())
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
solAssert(symbArguments.empty(), "");
|
solAssert(symbArguments.empty(), "");
|
||||||
@ -1071,7 +1071,7 @@ void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall)
|
|||||||
if (auto sliceType = dynamic_cast<ArraySliceType const*>(argType))
|
if (auto sliceType = dynamic_cast<ArraySliceType const*>(argType))
|
||||||
arrayType = &sliceType->arrayType();
|
arrayType = &sliceType->arrayType();
|
||||||
|
|
||||||
if (arrayType && arrayType->isByteArray() && smt::isFixedBytes(*funCallType))
|
if (arrayType && arrayType->isByteArrayOrString() && smt::isFixedBytes(*funCallType))
|
||||||
{
|
{
|
||||||
auto array = dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.expression(*argument));
|
auto array = dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.expression(*argument));
|
||||||
bytesToFixedBytesAssertions(*array, _funCall);
|
bytesToFixedBytesAssertions(*array, _funCall);
|
||||||
@ -2695,14 +2695,14 @@ Expression const* SMTEncoder::cleanExpression(Expression const& _expr)
|
|||||||
auto typeType = dynamic_cast<TypeType const*>(functionCall->expression().annotation().type);
|
auto typeType = dynamic_cast<TypeType const*>(functionCall->expression().annotation().type);
|
||||||
solAssert(typeType, "");
|
solAssert(typeType, "");
|
||||||
if (auto const* arrayType = dynamic_cast<ArrayType const*>(typeType->actualType()))
|
if (auto const* arrayType = dynamic_cast<ArrayType const*>(typeType->actualType()))
|
||||||
if (arrayType->isByteArray())
|
if (arrayType->isByteArrayOrString())
|
||||||
{
|
{
|
||||||
// this is a cast to `bytes`
|
// this is a cast to `bytes`
|
||||||
solAssert(functionCall->arguments().size() == 1, "");
|
solAssert(functionCall->arguments().size() == 1, "");
|
||||||
Expression const& arg = *functionCall->arguments()[0];
|
Expression const& arg = *functionCall->arguments()[0];
|
||||||
if (
|
if (
|
||||||
auto const* argArrayType = dynamic_cast<ArrayType const*>(arg.annotation().type);
|
auto const* argArrayType = dynamic_cast<ArrayType const*>(arg.annotation().type);
|
||||||
argArrayType && argArrayType->isByteArray()
|
argArrayType && argArrayType->isByteArrayOrString()
|
||||||
)
|
)
|
||||||
return cleanExpression(arg);
|
return cleanExpression(arg);
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ SortPointer smtSort(frontend::Type const& _type)
|
|||||||
auto sliceArrayType = dynamic_cast<ArraySliceType const*>(&_type);
|
auto sliceArrayType = dynamic_cast<ArraySliceType const*>(&_type);
|
||||||
ArrayType const* arrayType = sliceArrayType ? &sliceArrayType->arrayType() : dynamic_cast<ArrayType const*>(&_type);
|
ArrayType const* arrayType = sliceArrayType ? &sliceArrayType->arrayType() : dynamic_cast<ArrayType const*>(&_type);
|
||||||
if (
|
if (
|
||||||
(arrayType && (arrayType->isString() || arrayType->isByteArray())) ||
|
(arrayType && arrayType->isByteArrayOrString()) ||
|
||||||
_type.category() == frontend::Type::Category::StringLiteral
|
_type.category() == frontend::Type::Category::StringLiteral
|
||||||
)
|
)
|
||||||
tupleName = "bytes";
|
tupleName = "bytes";
|
||||||
|
@ -176,7 +176,7 @@ Json::Value ABI::formatType(
|
|||||||
ret["type"] = _encodingType.canonicalName() + suffix;
|
ret["type"] = _encodingType.canonicalName() + suffix;
|
||||||
else if (ArrayType const* arrayType = dynamic_cast<ArrayType const*>(&_encodingType))
|
else if (ArrayType const* arrayType = dynamic_cast<ArrayType const*>(&_encodingType))
|
||||||
{
|
{
|
||||||
if (arrayType->isByteArray())
|
if (arrayType->isByteArrayOrString())
|
||||||
ret["type"] = _encodingType.canonicalName() + suffix;
|
ret["type"] = _encodingType.canonicalName() + suffix;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1461,36 +1461,36 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
|
|||||||
stack.optimize();
|
stack.optimize();
|
||||||
|
|
||||||
MachineAssemblyObject object;
|
MachineAssemblyObject object;
|
||||||
MachineAssemblyObject runtimeObject;
|
MachineAssemblyObject deployedObject;
|
||||||
tie(object, runtimeObject) = stack.assembleWithDeployed();
|
tie(object, deployedObject) = stack.assembleWithDeployed();
|
||||||
|
|
||||||
if (object.bytecode)
|
if (object.bytecode)
|
||||||
object.bytecode->link(_inputsAndSettings.libraries);
|
object.bytecode->link(_inputsAndSettings.libraries);
|
||||||
if (runtimeObject.bytecode)
|
if (deployedObject.bytecode)
|
||||||
runtimeObject.bytecode->link(_inputsAndSettings.libraries);
|
deployedObject.bytecode->link(_inputsAndSettings.libraries);
|
||||||
|
|
||||||
for (string const& objectKind: vector<string>{"bytecode", "deployedBytecode"})
|
for (auto&& [kind, isDeployed]: {make_pair("bytecode"s, false), make_pair("deployedBytecode"s, true)})
|
||||||
if (isArtifactRequested(
|
if (isArtifactRequested(
|
||||||
_inputsAndSettings.outputSelection,
|
_inputsAndSettings.outputSelection,
|
||||||
sourceName,
|
sourceName,
|
||||||
contractName,
|
contractName,
|
||||||
evmObjectComponents(objectKind),
|
evmObjectComponents(kind),
|
||||||
wildcardMatchesExperimental
|
wildcardMatchesExperimental
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
MachineAssemblyObject const& o = objectKind == "bytecode" ? object : runtimeObject;
|
MachineAssemblyObject const& o = isDeployed ? deployedObject : object;
|
||||||
if (o.bytecode)
|
if (o.bytecode)
|
||||||
output["contracts"][sourceName][contractName]["evm"][objectKind] =
|
output["contracts"][sourceName][contractName]["evm"][kind] =
|
||||||
collectEVMObject(
|
collectEVMObject(
|
||||||
*o.bytecode,
|
*o.bytecode,
|
||||||
o.sourceMappings.get(),
|
o.sourceMappings.get(),
|
||||||
Json::arrayValue,
|
Json::arrayValue,
|
||||||
false,
|
isDeployed,
|
||||||
[&](string const& _element) { return isArtifactRequested(
|
[&, kind = kind](string const& _element) { return isArtifactRequested(
|
||||||
_inputsAndSettings.outputSelection,
|
_inputsAndSettings.outputSelection,
|
||||||
sourceName,
|
sourceName,
|
||||||
contractName,
|
contractName,
|
||||||
"evm." + objectKind + "." + _element,
|
"evm." + kind + "." + _element,
|
||||||
wildcardMatchesExperimental
|
wildcardMatchesExperimental
|
||||||
); }
|
); }
|
||||||
);
|
);
|
||||||
|
@ -94,7 +94,7 @@ void StorageLayout::generate(Type const* _type)
|
|||||||
}
|
}
|
||||||
else if (auto arrayType = dynamic_cast<ArrayType const*>(_type))
|
else if (auto arrayType = dynamic_cast<ArrayType const*>(_type))
|
||||||
{
|
{
|
||||||
if (arrayType->isByteArray())
|
if (arrayType->isByteArrayOrString())
|
||||||
typeInfo["encoding"] = "bytes";
|
typeInfo["encoding"] = "bytes";
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -267,17 +267,22 @@ template<
|
|||||||
typename MapType,
|
typename MapType,
|
||||||
typename KeyType,
|
typename KeyType,
|
||||||
typename ValueType = std::decay_t<decltype(std::declval<MapType>().find(std::declval<KeyType>())->second)> const&,
|
typename ValueType = std::decay_t<decltype(std::declval<MapType>().find(std::declval<KeyType>())->second)> const&,
|
||||||
typename AllowCopyType = void*
|
typename AllowCopyType = std::conditional_t<std::is_pod_v<ValueType> || std::is_pointer_v<ValueType>, detail::allow_copy, void*>
|
||||||
>
|
>
|
||||||
decltype(auto) valueOrDefault(MapType&& _map, KeyType const& _key, ValueType&& _defaultValue = {}, AllowCopyType = nullptr)
|
decltype(auto) valueOrDefault(
|
||||||
|
MapType&& _map,
|
||||||
|
KeyType const& _key,
|
||||||
|
ValueType&& _defaultValue = {},
|
||||||
|
AllowCopyType = {}
|
||||||
|
)
|
||||||
{
|
{
|
||||||
auto it = _map.find(_key);
|
auto it = _map.find(_key);
|
||||||
static_assert(
|
static_assert(
|
||||||
std::is_same_v<AllowCopyType, detail::allow_copy> ||
|
std::is_same_v<AllowCopyType, detail::allow_copy> ||
|
||||||
std::is_reference_v<decltype((it == _map.end()) ? _defaultValue : it->second)>,
|
std::is_reference_v<decltype((it == _map.end()) ? std::forward<ValueType>(_defaultValue) : it->second)>,
|
||||||
"valueOrDefault does not allow copies by default. Pass allow_copy as additional argument, if you want to allow copies."
|
"valueOrDefault does not allow copies by default. Pass allow_copy as additional argument, if you want to allow copies."
|
||||||
);
|
);
|
||||||
return (it == _map.end()) ? _defaultValue : it->second;
|
return (it == _map.end()) ? std::forward<ValueType>(_defaultValue) : it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace detail
|
namespace detail
|
||||||
|
@ -52,7 +52,7 @@ TMPDIR=$(mktemp -d)
|
|||||||
popd
|
popd
|
||||||
|
|
||||||
cp "$REPO_ROOT/scripts/bytecodecompare/prepare_report.js" .
|
cp "$REPO_ROOT/scripts/bytecodecompare/prepare_report.js" .
|
||||||
npm install solc-js/
|
npm install ./solc-js/dist
|
||||||
|
|
||||||
echo "Running the compiler..."
|
echo "Running the compiler..."
|
||||||
# shellcheck disable=SC2035
|
# shellcheck disable=SC2035
|
||||||
|
@ -149,11 +149,11 @@ for binary_name in $platform_binaries; do
|
|||||||
if [[ $platform == emscripten-wasm32 ]] || [[ $platform == emscripten-asmjs ]]; then
|
if [[ $platform == emscripten-wasm32 ]] || [[ $platform == emscripten-asmjs ]]; then
|
||||||
ln -sf "${solc_bin_dir}/${platform}/${binary_name}" "${solcjs_dir}/soljson.js"
|
ln -sf "${solc_bin_dir}/${platform}/${binary_name}" "${solcjs_dir}/soljson.js"
|
||||||
ln -sf "${solc_bin_dir}/${platform}/${binary_name}" "${solcjs_dir}/dist/soljson.js"
|
ln -sf "${solc_bin_dir}/${platform}/${binary_name}" "${solcjs_dir}/dist/soljson.js"
|
||||||
ln -s "${solcjs_dir}" solc-js
|
npm install "${solcjs_dir}/dist"
|
||||||
cp "${script_dir}/bytecodecompare/prepare_report.js" prepare_report.js
|
cp "${script_dir}/bytecodecompare/prepare_report.js" prepare_report.js
|
||||||
|
|
||||||
validate_reported_version \
|
validate_reported_version \
|
||||||
"$(solc-js/dist/solc.js --version)" \
|
"$(node_modules/solc/solc.js --version)" \
|
||||||
"$solidity_version_and_commit"
|
"$solidity_version_and_commit"
|
||||||
|
|
||||||
# shellcheck disable=SC2035
|
# shellcheck disable=SC2035
|
||||||
|
1
test/cmdlineTests/standard_yul_immutable_references/args
Normal file
1
test/cmdlineTests/standard_yul_immutable_references/args
Normal file
@ -0,0 +1 @@
|
|||||||
|
--pretty-json
|
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"language": "Yul",
|
||||||
|
"sources":
|
||||||
|
{
|
||||||
|
"A":
|
||||||
|
{
|
||||||
|
"content": "object \"YulTest\" { code { let size := datasize(\"runtime\") datacopy(0, dataoffset(\"runtime\"), size) setimmutable(0, \"test\", 1) return(0, size) } object \"runtime\" { code { mstore(0, loadimmutable(\"test\")) return(0, 0x20) } }}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings":
|
||||||
|
{
|
||||||
|
"outputSelection": {
|
||||||
|
"A": {
|
||||||
|
"*": [
|
||||||
|
"evm.deployedBytecode.immutableReferences",
|
||||||
|
"evm.bytecode",
|
||||||
|
"evm.deployedBytecode"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"contracts":
|
||||||
|
{
|
||||||
|
"A":
|
||||||
|
{
|
||||||
|
"YulTest":
|
||||||
|
{
|
||||||
|
"evm":
|
||||||
|
{
|
||||||
|
"bytecode":
|
||||||
|
{
|
||||||
|
"functionDebugData": {},
|
||||||
|
"generatedSources": [],
|
||||||
|
"linkReferences": {},
|
||||||
|
"object": "60298060156000396001600060010152806000f3fe7f000000000000000000000000000000000000000000000000000000000000000060005260206000f3",
|
||||||
|
"opcodes": "PUSH1 0x29 DUP1 PUSH1 0x15 PUSH1 0x0 CODECOPY PUSH1 0x1 PUSH1 0x0 PUSH1 0x1 ADD MSTORE DUP1 PUSH1 0x0 RETURN INVALID PUSH32 0x0 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 RETURN ",
|
||||||
|
"sourceMap": "42:19:0:-:0;100:4;77:21;74:1;65:40;133:1;122;109:26;;;149:4;146:1;139:15"
|
||||||
|
},
|
||||||
|
"deployedBytecode":
|
||||||
|
{
|
||||||
|
"functionDebugData": {},
|
||||||
|
"generatedSources": [],
|
||||||
|
"immutableReferences":
|
||||||
|
{
|
||||||
|
"test":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"length": 32,
|
||||||
|
"start": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"linkReferences": {},
|
||||||
|
"object": "7f000000000000000000000000000000000000000000000000000000000000000060005260206000f3",
|
||||||
|
"opcodes": "PUSH32 0x0 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 RETURN ",
|
||||||
|
"sourceMap": "203:21:0:-:0;200:1;193:32;241:4;238:1;231:15"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"errors":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"component": "general",
|
||||||
|
"formattedMessage": "Yul is still experimental. Please use the output with care.",
|
||||||
|
"message": "Yul is still experimental. Please use the output with care.",
|
||||||
|
"severity": "warning",
|
||||||
|
"type": "Warning"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -23,7 +23,7 @@ sub_0: assembly {
|
|||||||
/* \"A\":137:149 */
|
/* \"A\":137:149 */
|
||||||
revert
|
revert
|
||||||
}
|
}
|
||||||
","bytecode":{"functionDebugData":{},"generatedSources":[],"linkReferences":{},"object":"<BYTECODE REMOVED>","opcodes":"<OPCODES REMOVED>","sourceMap":"<SOURCEMAP REMOVED>"},"deployedBytecode":{"functionDebugData":{},"generatedSources":[],"linkReferences":{},"object":"<BYTECODE REMOVED>","opcodes":"<OPCODES REMOVED>","sourceMap":"<SOURCEMAP REMOVED>"}},"ir":"object \"NamedObject\" {
|
","bytecode":{"functionDebugData":{},"generatedSources":[],"linkReferences":{},"object":"<BYTECODE REMOVED>","opcodes":"<OPCODES REMOVED>","sourceMap":"<SOURCEMAP REMOVED>"},"deployedBytecode":{"functionDebugData":{},"generatedSources":[],"immutableReferences":{},"linkReferences":{},"object":"<BYTECODE REMOVED>","opcodes":"<OPCODES REMOVED>","sourceMap":"<SOURCEMAP REMOVED>"}},"ir":"object \"NamedObject\" {
|
||||||
code {
|
code {
|
||||||
let x := dataoffset(\"DataName\")
|
let x := dataoffset(\"DataName\")
|
||||||
sstore(add(x, 0), 0)
|
sstore(add(x, 0), 0)
|
||||||
|
@ -27,6 +27,7 @@ source test/externalTests/common.sh
|
|||||||
verify_input "$@"
|
verify_input "$@"
|
||||||
BINARY_TYPE="$1"
|
BINARY_TYPE="$1"
|
||||||
BINARY_PATH="$2"
|
BINARY_PATH="$2"
|
||||||
|
SELECTED_PRESETS="$3"
|
||||||
|
|
||||||
function compile_fn { npm run compile; }
|
function compile_fn { npm run compile; }
|
||||||
function test_fn { npm run test; }
|
function test_fn { npm run test; }
|
||||||
@ -34,8 +35,8 @@ function test_fn { npm run test; }
|
|||||||
function bleeps_test
|
function bleeps_test
|
||||||
{
|
{
|
||||||
local repo="https://github.com/wighawag/bleeps"
|
local repo="https://github.com/wighawag/bleeps"
|
||||||
local ref_type=tag
|
local ref_type=branch
|
||||||
local ref=bleeps_migrations # TODO: There's a 0.4.19 contract in 'main' that would need patching for the latest compiler.
|
local ref=main
|
||||||
local config_file="hardhat.config.ts"
|
local config_file="hardhat.config.ts"
|
||||||
local config_var=config
|
local config_var=config
|
||||||
|
|
||||||
@ -65,6 +66,16 @@ function bleeps_test
|
|||||||
pushd "contracts/"
|
pushd "contracts/"
|
||||||
sed -i 's|"bleeps-common": "workspace:\*",|"bleeps-common": "file:../common-lib/",|g' package.json
|
sed -i 's|"bleeps-common": "workspace:\*",|"bleeps-common": "file:../common-lib/",|g' package.json
|
||||||
|
|
||||||
|
sed -i 's/function() public/fallback() external/g' src/externals/WETH9.sol
|
||||||
|
sed -i 's/this\.balance/address(this).balance/g' src/externals/WETH9.sol
|
||||||
|
sed -i 's/uint(-1)/type(uint).max/g' src/externals/WETH9.sol
|
||||||
|
sed -i 's/msg\.sender\.transfer(/payable(msg.sender).transfer(/g' src/externals/WETH9.sol
|
||||||
|
sed -i 's/^\s*\(Deposit\|Withdrawal\|Approval\|Transfer\)(/emit \1(/g' src/externals/WETH9.sol
|
||||||
|
|
||||||
|
# This test does not currently pass due to an upstream problem.
|
||||||
|
# TODO: Remove this line when https://github.com/wighawag/bleeps/issues/2 is fixed
|
||||||
|
rm test/BleepsDAO.governor.test.ts
|
||||||
|
|
||||||
neutralize_package_lock
|
neutralize_package_lock
|
||||||
neutralize_package_json_hooks
|
neutralize_package_json_hooks
|
||||||
force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH"
|
force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH"
|
||||||
|
@ -59,7 +59,7 @@ function colony_test
|
|||||||
[[ $BINARY_TYPE == native ]] && replace_global_solc "$BINARY_PATH"
|
[[ $BINARY_TYPE == native ]] && replace_global_solc "$BINARY_PATH"
|
||||||
|
|
||||||
neutralize_package_json_hooks
|
neutralize_package_json_hooks
|
||||||
force_truffle_compiler_settings "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$(first_word "$SELECTED_PRESETS")"
|
force_truffle_compiler_settings "$config_file" "$BINARY_TYPE" "${DIR}/solc/dist" "$(first_word "$SELECTED_PRESETS")"
|
||||||
yarn install
|
yarn install
|
||||||
git submodule update --init
|
git submodule update --init
|
||||||
|
|
||||||
@ -69,10 +69,10 @@ function colony_test
|
|||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
replace_version_pragmas
|
replace_version_pragmas
|
||||||
[[ $BINARY_TYPE == solcjs ]] && force_solc_modules "${DIR}/solc"
|
[[ $BINARY_TYPE == solcjs ]] && force_solc_modules "${DIR}/solc/dist"
|
||||||
|
|
||||||
for preset in $SELECTED_PRESETS; do
|
for preset in $SELECTED_PRESETS; do
|
||||||
truffle_run_test "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
|
truffle_run_test "$config_file" "$BINARY_TYPE" "${DIR}/solc/dist" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ source test/externalTests/common.sh
|
|||||||
verify_input "$@"
|
verify_input "$@"
|
||||||
BINARY_TYPE="$1"
|
BINARY_TYPE="$1"
|
||||||
BINARY_PATH="$2"
|
BINARY_PATH="$2"
|
||||||
|
SELECTED_PRESETS="$3"
|
||||||
|
|
||||||
function compile_fn { npm run build; }
|
function compile_fn { npm run build; }
|
||||||
function test_fn { npm run test; }
|
function test_fn { npm run test; }
|
||||||
|
@ -63,14 +63,14 @@ function gnosis_safe_test
|
|||||||
|
|
||||||
neutralize_package_lock
|
neutralize_package_lock
|
||||||
neutralize_package_json_hooks
|
neutralize_package_json_hooks
|
||||||
force_truffle_compiler_settings "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$(first_word "$SELECTED_PRESETS")"
|
force_truffle_compiler_settings "$config_file" "$BINARY_TYPE" "${DIR}/solc/dist" "$(first_word "$SELECTED_PRESETS")"
|
||||||
npm install --package-lock
|
npm install --package-lock
|
||||||
|
|
||||||
replace_version_pragmas
|
replace_version_pragmas
|
||||||
[[ $BINARY_TYPE == solcjs ]] && force_solc_modules "${DIR}/solc"
|
[[ $BINARY_TYPE == solcjs ]] && force_solc_modules "${DIR}/solc/dist"
|
||||||
|
|
||||||
for preset in $SELECTED_PRESETS; do
|
for preset in $SELECTED_PRESETS; do
|
||||||
truffle_run_test "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
|
truffle_run_test "$config_file" "$BINARY_TYPE" "${DIR}/solc/dist" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,14 +61,14 @@ function gnosis_safe_test
|
|||||||
|
|
||||||
neutralize_package_lock
|
neutralize_package_lock
|
||||||
neutralize_package_json_hooks
|
neutralize_package_json_hooks
|
||||||
force_truffle_compiler_settings "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$(first_word "$SELECTED_PRESETS")"
|
force_truffle_compiler_settings "$config_file" "$BINARY_TYPE" "${DIR}/solc/dist" "$(first_word "$SELECTED_PRESETS")"
|
||||||
npm install --package-lock
|
npm install --package-lock
|
||||||
|
|
||||||
replace_version_pragmas
|
replace_version_pragmas
|
||||||
[[ $BINARY_TYPE == solcjs ]] && force_solc_modules "${DIR}/solc"
|
[[ $BINARY_TYPE == solcjs ]] && force_solc_modules "${DIR}/solc/dist"
|
||||||
|
|
||||||
for preset in $SELECTED_PRESETS; do
|
for preset in $SELECTED_PRESETS; do
|
||||||
truffle_run_test "$config_file" "$BINARY_TYPE" "${DIR}/solc" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
|
truffle_run_test "$config_file" "$BINARY_TYPE" "${DIR}/solc/dist" "$preset" "${compile_only_presets[*]}" compile_fn test_fn
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ function test_fn { yarn test; }
|
|||||||
|
|
||||||
function perpetual_pools_test
|
function perpetual_pools_test
|
||||||
{
|
{
|
||||||
local repo="https://github.com/tracer-protocol/perpetual-pools-contracts"
|
local repo="https://github.com/solidity-external-tests/perpetual-pools-contracts"
|
||||||
local ref_type=branch
|
local ref_type=branch
|
||||||
local ref=pools-v2
|
local ref=pools-v2
|
||||||
local config_file="hardhat.config.ts"
|
local config_file="hardhat.config.ts"
|
||||||
|
@ -45,7 +45,7 @@ function pool_together_test
|
|||||||
"${compile_only_presets[@]}"
|
"${compile_only_presets[@]}"
|
||||||
#ir-no-optimize # Compilation fails with "YulException: Variable var_amount_205 is 9 slot(s) too deep inside the stack."
|
#ir-no-optimize # Compilation fails with "YulException: Variable var_amount_205 is 9 slot(s) too deep inside the stack."
|
||||||
#ir-optimize-evm-only # Compilation fails with "YulException: Variable var_amount_205 is 9 slot(s) too deep inside the stack."
|
#ir-optimize-evm-only # Compilation fails with "YulException: Variable var_amount_205 is 9 slot(s) too deep inside the stack."
|
||||||
#ir-optimize-evm+yul # FIXME: ICE due to https://github.com/ethereum/solidity/issues/12558
|
ir-optimize-evm+yul
|
||||||
legacy-no-optimize
|
legacy-no-optimize
|
||||||
legacy-optimize-evm-only
|
legacy-optimize-evm-only
|
||||||
legacy-optimize-evm+yul
|
legacy-optimize-evm+yul
|
||||||
|
@ -30,7 +30,7 @@ BINARY_PATH="$2"
|
|||||||
SELECTED_PRESETS="$3"
|
SELECTED_PRESETS="$3"
|
||||||
|
|
||||||
function compile_fn { yarn compile; }
|
function compile_fn { yarn compile; }
|
||||||
function test_fn { yarn test:contracts; }
|
function test_fn { yarn test; }
|
||||||
|
|
||||||
function prb_math_test
|
function prb_math_test
|
||||||
{
|
{
|
||||||
|
@ -27,6 +27,7 @@ source test/externalTests/common.sh
|
|||||||
verify_input "$@"
|
verify_input "$@"
|
||||||
BINARY_TYPE="$1"
|
BINARY_TYPE="$1"
|
||||||
BINARY_PATH="$2"
|
BINARY_PATH="$2"
|
||||||
|
SELECTED_PRESETS="$3"
|
||||||
|
|
||||||
function compile_fn { yarn build; }
|
function compile_fn { yarn build; }
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ source test/externalTests/common.sh
|
|||||||
verify_input "$@"
|
verify_input "$@"
|
||||||
BINARY_TYPE="$1"
|
BINARY_TYPE="$1"
|
||||||
BINARY_PATH="$2"
|
BINARY_PATH="$2"
|
||||||
|
SELECTED_PRESETS="$3"
|
||||||
|
|
||||||
function compile_fn { yarn compile; }
|
function compile_fn { yarn compile; }
|
||||||
function test_fn { UPDATE_SNAPSHOT=1 npx hardhat test; }
|
function test_fn { UPDATE_SNAPSHOT=1 npx hardhat test; }
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
// Example from https://github.com/ethereum/solidity/issues/12558
|
||||||
|
pragma abicoder v2;
|
||||||
|
contract C {
|
||||||
|
function f(uint[] calldata a) external returns (uint[][] memory) {
|
||||||
|
uint[][] memory m = new uint[][](2);
|
||||||
|
m[0] = a;
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contract Test {
|
||||||
|
C immutable c = new C();
|
||||||
|
|
||||||
|
function test() external returns (bool) {
|
||||||
|
uint[] memory arr = new uint[](4);
|
||||||
|
|
||||||
|
arr[0] = 13;
|
||||||
|
arr[1] = 14;
|
||||||
|
arr[2] = 15;
|
||||||
|
arr[3] = 16;
|
||||||
|
|
||||||
|
uint[][] memory ret = c.f(arr);
|
||||||
|
assert(ret.length == 2);
|
||||||
|
assert(ret[0].length == 4);
|
||||||
|
assert(ret[0][0] == 13);
|
||||||
|
assert(ret[0][1] == 14);
|
||||||
|
assert(ret[0][2] == 15);
|
||||||
|
assert(ret[0][3] == 16);
|
||||||
|
assert(ret[1].length == 0);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// EVMVersion: >homestead
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// test() -> true
|
24
test/libsolidity/semanticTests/structs/copy_from_storage.sol
Normal file
24
test/libsolidity/semanticTests/structs/copy_from_storage.sol
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
pragma abicoder v2;
|
||||||
|
// Example from https://github.com/ethereum/solidity/issues/12558
|
||||||
|
struct S {
|
||||||
|
uint x;
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
S sStorage;
|
||||||
|
constructor() {
|
||||||
|
sStorage.x = 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() external returns (S[] memory) {
|
||||||
|
S[] memory sMemory = new S[](1);
|
||||||
|
|
||||||
|
sMemory[0] = sStorage;
|
||||||
|
|
||||||
|
return sMemory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 0x20, 1, 13
|
@ -0,0 +1,96 @@
|
|||||||
|
pragma abicoder v2;
|
||||||
|
|
||||||
|
struct S { uint value; }
|
||||||
|
|
||||||
|
contract Test {
|
||||||
|
S[][] a;
|
||||||
|
S[] b;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
a.push();
|
||||||
|
a[0].push(S(1));
|
||||||
|
a[0].push(S(2));
|
||||||
|
a[0].push(S(3));
|
||||||
|
|
||||||
|
b.push(S(4));
|
||||||
|
b.push(S(5));
|
||||||
|
b.push(S(6));
|
||||||
|
b.push(S(7));
|
||||||
|
}
|
||||||
|
|
||||||
|
function test1() external returns (bool) {
|
||||||
|
a.push();
|
||||||
|
a[1] = b;
|
||||||
|
|
||||||
|
assert(a.length == 2);
|
||||||
|
assert(a[0].length == 3);
|
||||||
|
assert(a[1].length == 4);
|
||||||
|
assert(a[1][0].value == 4);
|
||||||
|
assert(a[1][1].value == 5);
|
||||||
|
assert(a[1][2].value == 6);
|
||||||
|
assert(a[1][3].value == 7);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test2() external returns (bool) {
|
||||||
|
S[][] memory temp = new S[][](2);
|
||||||
|
|
||||||
|
temp = a;
|
||||||
|
|
||||||
|
assert(temp.length == 2);
|
||||||
|
assert(temp[0].length == 3);
|
||||||
|
assert(temp[1].length == 4);
|
||||||
|
assert(temp[1][0].value == 4);
|
||||||
|
assert(temp[1][1].value == 5);
|
||||||
|
assert(temp[1][2].value == 6);
|
||||||
|
assert(temp[1][3].value == 7);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test3() external returns (bool) {
|
||||||
|
S[][] memory temp = new S[][](2);
|
||||||
|
|
||||||
|
temp[0] = a[0];
|
||||||
|
temp[1] = a[1];
|
||||||
|
|
||||||
|
assert(temp.length == 2);
|
||||||
|
assert(temp[0].length == 3);
|
||||||
|
assert(temp[1].length == 4);
|
||||||
|
assert(temp[1][0].value == 4);
|
||||||
|
assert(temp[1][1].value == 5);
|
||||||
|
assert(temp[1][2].value == 6);
|
||||||
|
assert(temp[1][3].value == 7);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test4() external returns (bool) {
|
||||||
|
S[][] memory temp = new S[][](2);
|
||||||
|
|
||||||
|
temp[0] = a[0];
|
||||||
|
temp[1] = b;
|
||||||
|
|
||||||
|
assert(temp.length == 2);
|
||||||
|
assert(temp[0].length == 3);
|
||||||
|
assert(temp[1].length == 4);
|
||||||
|
assert(temp[1][0].value == 4);
|
||||||
|
assert(temp[1][1].value == 5);
|
||||||
|
assert(temp[1][2].value == 6);
|
||||||
|
assert(temp[1][3].value == 7);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// EVMVersion: >homestead
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// test1() -> true
|
||||||
|
// gas irOptimized: 150618
|
||||||
|
// gas legacy: 150266
|
||||||
|
// gas legacyOptimized: 149875
|
||||||
|
// test2() -> true
|
||||||
|
// test3() -> true
|
||||||
|
// test4() -> true
|
@ -0,0 +1,44 @@
|
|||||||
|
pragma abicoder v2;
|
||||||
|
struct S {
|
||||||
|
function () external[] functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f(function () external[] calldata functions) external returns (S memory) {
|
||||||
|
S memory s;
|
||||||
|
s.functions = functions;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Test {
|
||||||
|
C immutable c = new C();
|
||||||
|
|
||||||
|
function test() external returns (bool) {
|
||||||
|
function() external[] memory functions = new function() external[](3);
|
||||||
|
|
||||||
|
functions[0] = this.random1;
|
||||||
|
functions[1] = this.random2;
|
||||||
|
functions[2] = this.random3;
|
||||||
|
|
||||||
|
S memory ret = c.f(functions);
|
||||||
|
|
||||||
|
assert(ret.functions.length == 3);
|
||||||
|
assert(ret.functions[0] == this.random1);
|
||||||
|
assert(ret.functions[1] == this.random2);
|
||||||
|
assert(ret.functions[2] == this.random3);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
function random1() external {
|
||||||
|
}
|
||||||
|
function random2() external {
|
||||||
|
}
|
||||||
|
function random3() external {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// EVMVersion: >homestead
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// test() -> true
|
@ -0,0 +1,45 @@
|
|||||||
|
pragma abicoder v2;
|
||||||
|
|
||||||
|
struct St0 {
|
||||||
|
bytes el0;
|
||||||
|
}
|
||||||
|
contract C {
|
||||||
|
function f() external returns (St0 memory) {
|
||||||
|
St0 memory x;
|
||||||
|
x.el0 = msg.data;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
function g() external returns (St0 memory) {
|
||||||
|
bytes memory temp = msg.data;
|
||||||
|
St0 memory x;
|
||||||
|
x.el0 = temp;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hashes() external returns (bytes4, bytes4) {
|
||||||
|
return (this.f.selector, this.g.selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
function large(uint256, uint256, uint256, uint256) external returns (St0 memory) {
|
||||||
|
St0 memory x;
|
||||||
|
x.el0 = msg.data;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
function another_large(uint256, uint256, uint256, uint256) external returns (St0 memory) {
|
||||||
|
bytes memory temp = msg.data;
|
||||||
|
St0 memory x;
|
||||||
|
x.el0 = temp;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 0x20, 0x20, 4, 0x26121ff000000000000000000000000000000000000000000000000000000000
|
||||||
|
// g() -> 0x20, 0x20, 4, 0xe2179b8e00000000000000000000000000000000000000000000000000000000
|
||||||
|
// hashes() -> 0x26121ff000000000000000000000000000000000000000000000000000000000, 0xe2179b8e00000000000000000000000000000000000000000000000000000000
|
||||||
|
// large(uint256,uint256,uint256,uint256): 1, 2, 3, 4 -> 0x20, 0x20, 0x84, 0xe02492f800000000000000000000000000000000000000000000000000000000, 0x100000000000000000000000000000000000000000000000000000000, 0x200000000000000000000000000000000000000000000000000000000, 0x300000000000000000000000000000000000000000000000000000000, 0x400000000000000000000000000000000000000000000000000000000
|
||||||
|
// another_large(uint256,uint256,uint256,uint256): 1, 2, 3, 4 -> 0x20, 0x20, 0x84, 0x2a46f85a00000000000000000000000000000000000000000000000000000000, 0x100000000000000000000000000000000000000000000000000000000, 0x200000000000000000000000000000000000000000000000000000000, 0x300000000000000000000000000000000000000000000000000000000, 0x400000000000000000000000000000000000000000000000000000000
|
@ -0,0 +1,9 @@
|
|||||||
|
// Example from https://github.com/ethereum/solidity/issues/12558
|
||||||
|
pragma abicoder v2;
|
||||||
|
contract C {
|
||||||
|
function() external[1][] s0;
|
||||||
|
constructor(function() external[1][] memory i0)
|
||||||
|
{
|
||||||
|
i0[0] = s0[1];
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user