Generate already lint code from procedural generation (#5060)
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const cp = require('child_process');
|
||||
// const cp = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const format = require('./format-lines');
|
||||
@ -23,11 +23,11 @@ function generateFromTemplate(file, template, outputPrefix = '') {
|
||||
...(version ? [version + ` (${file})`] : []),
|
||||
`// This file was procedurally generated from ${input}.`,
|
||||
'',
|
||||
require(template),
|
||||
require(template).trimEnd(),
|
||||
);
|
||||
|
||||
fs.writeFileSync(output, content);
|
||||
cp.execFileSync('prettier', ['--write', output]);
|
||||
// cp.execFileSync('prettier', ['--write', output]);
|
||||
}
|
||||
|
||||
// Contracts
|
||||
|
||||
@ -47,7 +47,7 @@ const sort = type => `\
|
||||
}
|
||||
`;
|
||||
|
||||
const quickSort = `
|
||||
const quickSort = `\
|
||||
/**
|
||||
* @dev Performs a quick sort of a segment of memory. The segment sorted starts at \`begin\` (inclusive), and stops
|
||||
* at end (exclusive). Sorting follows the \`comp\` comparator.
|
||||
@ -123,7 +123,7 @@ function _swap(uint256 ptr1, uint256 ptr2) private pure {
|
||||
}
|
||||
`;
|
||||
|
||||
const defaultComparator = `
|
||||
const defaultComparator = `\
|
||||
/// @dev Comparator for sorting arrays in increasing order.
|
||||
function _defaultComp(bytes32 a, bytes32 b) private pure returns (bool) {
|
||||
return a < b;
|
||||
@ -150,7 +150,7 @@ const castComparator = type => `\
|
||||
}
|
||||
`;
|
||||
|
||||
const search = `
|
||||
const search = `\
|
||||
/**
|
||||
* @dev Searches a sorted \`array\` and returns the first index that contains
|
||||
* a value greater or equal to \`element\`. If no such index exists (i.e. all
|
||||
@ -319,7 +319,7 @@ function upperBoundMemory(uint256[] memory array, uint256 element) internal pure
|
||||
}
|
||||
`;
|
||||
|
||||
const unsafeAccessStorage = type => `
|
||||
const unsafeAccessStorage = type => `\
|
||||
/**
|
||||
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
|
||||
*
|
||||
@ -334,9 +334,10 @@ function unsafeAccess(${type}[] storage arr, uint256 pos) internal pure returns
|
||||
slot := arr.slot
|
||||
}
|
||||
return slot.deriveArray().offset(pos).get${capitalize(type)}Slot();
|
||||
}`;
|
||||
}
|
||||
`;
|
||||
|
||||
const unsafeAccessMemory = type => `
|
||||
const unsafeAccessMemory = type => `\
|
||||
/**
|
||||
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
|
||||
*
|
||||
@ -349,7 +350,7 @@ function unsafeMemoryAccess(${type}[] memory arr, uint256 pos) internal pure ret
|
||||
}
|
||||
`;
|
||||
|
||||
const unsafeSetLength = type => `
|
||||
const unsafeSetLength = type => `\
|
||||
/**
|
||||
* @dev Helper to set the length of an dynamic array. Directly writing to \`.length\` is forbidden.
|
||||
*
|
||||
@ -360,14 +361,18 @@ function unsafeSetLength(${type}[] storage array, uint256 len) internal {
|
||||
assembly {
|
||||
sstore(array.slot, len)
|
||||
}
|
||||
}`;
|
||||
}
|
||||
`;
|
||||
|
||||
// GENERATE
|
||||
module.exports = format(
|
||||
header.trimEnd(),
|
||||
'library Arrays {',
|
||||
format(
|
||||
[].concat(
|
||||
'using SlotDerivation for bytes32;',
|
||||
'using StorageSlot for bytes32;',
|
||||
'',
|
||||
// sorting, comparator, helpers and internal
|
||||
sort('bytes32'),
|
||||
TYPES.filter(type => type !== 'bytes32').map(sort),
|
||||
@ -381,5 +386,7 @@ module.exports = format(
|
||||
TYPES.map(unsafeAccessStorage),
|
||||
TYPES.map(unsafeAccessMemory),
|
||||
TYPES.map(unsafeSetLength),
|
||||
),
|
||||
).trimEnd(),
|
||||
'}',
|
||||
);
|
||||
|
||||
@ -41,11 +41,7 @@ struct ${opts.checkpointTypeName} {
|
||||
* IMPORTANT: Never accept \`key\` as a user input, since an arbitrary \`type(${opts.keyTypeName}).max\` key set will disable the
|
||||
* library.
|
||||
*/
|
||||
function push(
|
||||
${opts.historyTypeName} storage self,
|
||||
${opts.keyTypeName} key,
|
||||
${opts.valueTypeName} value
|
||||
) internal returns (${opts.valueTypeName}, ${opts.valueTypeName}) {
|
||||
function push(${opts.historyTypeName} storage self, ${opts.keyTypeName} key, ${opts.valueTypeName} value) internal returns (${opts.valueTypeName}, ${opts.valueTypeName}) {
|
||||
return _insert(self.${opts.checkpointFieldName}, key, value);
|
||||
}
|
||||
|
||||
@ -108,15 +104,7 @@ function latest(${opts.historyTypeName} storage self) internal view returns (${o
|
||||
* @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
|
||||
* in the most recent checkpoint.
|
||||
*/
|
||||
function latestCheckpoint(${opts.historyTypeName} storage self)
|
||||
internal
|
||||
view
|
||||
returns (
|
||||
bool exists,
|
||||
${opts.keyTypeName} ${opts.keyFieldName},
|
||||
${opts.valueTypeName} ${opts.valueFieldName}
|
||||
)
|
||||
{
|
||||
function latestCheckpoint(${opts.historyTypeName} storage self) internal view returns (bool exists, ${opts.keyTypeName} ${opts.keyFieldName}, ${opts.valueTypeName} ${opts.valueFieldName}) {
|
||||
uint256 pos = self.${opts.checkpointFieldName}.length;
|
||||
if (pos == 0) {
|
||||
return (false, 0, 0);
|
||||
@ -144,11 +132,7 @@ function at(${opts.historyTypeName} storage self, uint32 pos) internal view retu
|
||||
* @dev Pushes a (\`key\`, \`value\`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
|
||||
* or by updating the last one.
|
||||
*/
|
||||
function _insert(
|
||||
${opts.checkpointTypeName}[] storage self,
|
||||
${opts.keyTypeName} key,
|
||||
${opts.valueTypeName} value
|
||||
) private returns (${opts.valueTypeName}, ${opts.valueTypeName}) {
|
||||
function _insert(${opts.checkpointTypeName}[] storage self, ${opts.keyTypeName} key, ${opts.valueTypeName} value) private returns (${opts.valueTypeName}, ${opts.valueTypeName}) {
|
||||
uint256 pos = self.length;
|
||||
|
||||
if (pos > 0) {
|
||||
@ -225,11 +209,10 @@ function _lowerBinaryLookup(
|
||||
/**
|
||||
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
|
||||
*/
|
||||
function _unsafeAccess(${opts.checkpointTypeName}[] storage self, uint256 pos)
|
||||
private
|
||||
pure
|
||||
returns (${opts.checkpointTypeName} storage result)
|
||||
{
|
||||
function _unsafeAccess(
|
||||
${opts.checkpointTypeName}[] storage self,
|
||||
uint256 pos
|
||||
) private pure returns (${opts.checkpointTypeName} storage result) {
|
||||
assembly {
|
||||
mstore(0, self.slot)
|
||||
result.slot := add(keccak256(0, 0x20), pos)
|
||||
@ -242,7 +225,11 @@ function _unsafeAccess(${opts.checkpointTypeName}[] storage self, uint256 pos)
|
||||
module.exports = format(
|
||||
header.trimEnd(),
|
||||
'library Checkpoints {',
|
||||
format(
|
||||
[].concat(
|
||||
errors,
|
||||
OPTS.flatMap(opts => template(opts)),
|
||||
OPTS.map(opts => template(opts)),
|
||||
),
|
||||
).trimEnd(),
|
||||
'}',
|
||||
);
|
||||
|
||||
@ -22,18 +22,13 @@ uint8 internal constant _KEY_MAX_GAP = 64;
|
||||
Checkpoints.${opts.historyTypeName} internal _ckpts;
|
||||
|
||||
// helpers
|
||||
function _bound${capitalize(opts.keyTypeName)}(
|
||||
${opts.keyTypeName} x,
|
||||
${opts.keyTypeName} min,
|
||||
${opts.keyTypeName} max
|
||||
) internal pure returns (${opts.keyTypeName}) {
|
||||
function _bound${capitalize(opts.keyTypeName)}(${opts.keyTypeName} x, ${opts.keyTypeName} min, ${
|
||||
opts.keyTypeName
|
||||
} max) internal pure returns (${opts.keyTypeName}) {
|
||||
return SafeCast.to${capitalize(opts.keyTypeName)}(bound(uint256(x), uint256(min), uint256(max)));
|
||||
}
|
||||
|
||||
function _prepareKeys(
|
||||
${opts.keyTypeName}[] memory keys,
|
||||
${opts.keyTypeName} maxSpread
|
||||
) internal pure {
|
||||
function _prepareKeys(${opts.keyTypeName}[] memory keys, ${opts.keyTypeName} maxSpread) internal pure {
|
||||
${opts.keyTypeName} lastKey = 0;
|
||||
for (uint256 i = 0; i < keys.length; ++i) {
|
||||
${opts.keyTypeName} key = _bound${capitalize(opts.keyTypeName)}(keys[i], lastKey, lastKey + maxSpread);
|
||||
@ -42,11 +37,7 @@ function _prepareKeys(
|
||||
}
|
||||
}
|
||||
|
||||
function _assertLatestCheckpoint(
|
||||
bool exist,
|
||||
${opts.keyTypeName} key,
|
||||
${opts.valueTypeName} value
|
||||
) internal {
|
||||
function _assertLatestCheckpoint(bool exist, ${opts.keyTypeName} key, ${opts.valueTypeName} value) internal {
|
||||
(bool _exist, ${opts.keyTypeName} _key, ${opts.valueTypeName} _value) = _ckpts.latestCheckpoint();
|
||||
assertEq(_exist, exist);
|
||||
assertEq(_key, key);
|
||||
@ -54,11 +45,9 @@ function _assertLatestCheckpoint(
|
||||
}
|
||||
|
||||
// tests
|
||||
function testPush(
|
||||
${opts.keyTypeName}[] memory keys,
|
||||
${opts.valueTypeName}[] memory values,
|
||||
${opts.keyTypeName} pastKey
|
||||
) public {
|
||||
function testPush(${opts.keyTypeName}[] memory keys, ${opts.valueTypeName}[] memory values, ${
|
||||
opts.keyTypeName
|
||||
} pastKey) public {
|
||||
vm.assume(values.length > 0 && values.length <= keys.length);
|
||||
_prepareKeys(keys, _KEY_MAX_GAP);
|
||||
|
||||
@ -98,11 +87,9 @@ function push(${opts.keyTypeName} key, ${opts.valueTypeName} value) external {
|
||||
_ckpts.push(key, value);
|
||||
}
|
||||
|
||||
function testLookup(
|
||||
${opts.keyTypeName}[] memory keys,
|
||||
${opts.valueTypeName}[] memory values,
|
||||
${opts.keyTypeName} lookup
|
||||
) public {
|
||||
function testLookup(${opts.keyTypeName}[] memory keys, ${opts.valueTypeName}[] memory values, ${
|
||||
opts.keyTypeName
|
||||
} lookup) public {
|
||||
vm.assume(values.length > 0 && values.length <= keys.length);
|
||||
_prepareKeys(keys, _KEY_MAX_GAP);
|
||||
|
||||
@ -142,5 +129,10 @@ function testLookup(
|
||||
// GENERATE
|
||||
module.exports = format(
|
||||
header,
|
||||
...OPTS.flatMap(opts => [`contract Checkpoints${opts.historyTypeName}Test is Test {`, [template(opts)], '}']),
|
||||
...OPTS.flatMap(opts => [
|
||||
`contract Checkpoints${opts.historyTypeName}Test is Test {`,
|
||||
[template(opts).trimEnd()],
|
||||
'}',
|
||||
'',
|
||||
]),
|
||||
);
|
||||
|
||||
@ -54,7 +54,7 @@ import {EnumerableSet} from "./EnumerableSet.sol";
|
||||
`;
|
||||
/* eslint-enable max-len */
|
||||
|
||||
const defaultMap = () => `\
|
||||
const defaultMap = `\
|
||||
// To implement this library for multiple types with as little code repetition as possible, we write it in
|
||||
// terms of a generic Map type with bytes32 keys and values. The Map implementation uses private functions,
|
||||
// and user-facing implementations such as \`UintToAddressMap\` are just wrappers around the underlying Map.
|
||||
@ -78,11 +78,7 @@ struct Bytes32ToBytes32Map {
|
||||
* Returns true if the key was added to the map, that is if it was not
|
||||
* already present.
|
||||
*/
|
||||
function set(
|
||||
Bytes32ToBytes32Map storage map,
|
||||
bytes32 key,
|
||||
bytes32 value
|
||||
) internal returns (bool) {
|
||||
function set(Bytes32ToBytes32Map storage map, bytes32 key, bytes32 value) internal returns (bool) {
|
||||
map._values[key] = value;
|
||||
return map._keys.add(key);
|
||||
}
|
||||
@ -181,11 +177,7 @@ struct ${name} {
|
||||
* Returns true if the key was added to the map, that is if it was not
|
||||
* already present.
|
||||
*/
|
||||
function set(
|
||||
${name} storage map,
|
||||
${keyType} key,
|
||||
${valueType} value
|
||||
) internal returns (bool) {
|
||||
function set(${name} storage map, ${keyType} key, ${valueType} value) internal returns (bool) {
|
||||
return set(map._inner, ${toBytes32(keyType, 'key')}, ${toBytes32(valueType, 'value')});
|
||||
}
|
||||
|
||||
@ -271,11 +263,13 @@ function keys(${name} storage map) internal view returns (${keyType}[] memory) {
|
||||
module.exports = format(
|
||||
header.trimEnd(),
|
||||
'library EnumerableMap {',
|
||||
[
|
||||
format(
|
||||
[].concat(
|
||||
'using EnumerableSet for EnumerableSet.Bytes32Set;',
|
||||
'',
|
||||
defaultMap(),
|
||||
TYPES.map(details => customMap(details).trimEnd()).join('\n\n'),
|
||||
],
|
||||
defaultMap,
|
||||
TYPES.map(details => customMap(details)),
|
||||
),
|
||||
).trimEnd(),
|
||||
'}',
|
||||
);
|
||||
|
||||
@ -43,7 +43,7 @@ pragma solidity ^0.8.20;
|
||||
`;
|
||||
/* eslint-enable max-len */
|
||||
|
||||
const defaultSet = () => `\
|
||||
const defaultSet = `\
|
||||
// To implement this library for multiple types with as little code
|
||||
// repetition as possible, we write it in terms of a generic Set type with
|
||||
// bytes32 values.
|
||||
@ -240,6 +240,11 @@ function values(${name} storage set) internal view returns (${type}[] memory) {
|
||||
module.exports = format(
|
||||
header.trimEnd(),
|
||||
'library EnumerableSet {',
|
||||
[defaultSet(), TYPES.map(details => customSet(details).trimEnd()).join('\n\n')],
|
||||
format(
|
||||
[].concat(
|
||||
defaultSet,
|
||||
TYPES.map(details => customSet(details)),
|
||||
),
|
||||
).trimEnd(),
|
||||
'}',
|
||||
);
|
||||
|
||||
@ -116,7 +116,7 @@ function toUint${length}(int${length} value) internal pure returns (uint${length
|
||||
}
|
||||
`;
|
||||
|
||||
const boolToUint = `
|
||||
const boolToUint = `\
|
||||
/**
|
||||
* @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
|
||||
*/
|
||||
@ -132,7 +132,8 @@ const boolToUint = `
|
||||
module.exports = format(
|
||||
header.trimEnd(),
|
||||
'library SafeCast {',
|
||||
errors,
|
||||
[...LENGTHS.map(toUintDownCast), toUint(256), ...LENGTHS.map(toIntDownCast), toInt(256), boolToUint],
|
||||
format(
|
||||
[].concat(errors, LENGTHS.map(toUintDownCast), toUint(256), LENGTHS.map(toIntDownCast), toInt(256), boolToUint),
|
||||
).trimEnd(),
|
||||
'}',
|
||||
);
|
||||
|
||||
@ -109,8 +109,12 @@ function deriveMapping(bytes32 slot, ${type} memory key) internal pure returns (
|
||||
module.exports = format(
|
||||
header.trimEnd(),
|
||||
'library SlotDerivation {',
|
||||
format(
|
||||
[].concat(
|
||||
namespace,
|
||||
array,
|
||||
TYPES.map(type => (type.isValueType ? mapping(type) : mapping2(type))),
|
||||
),
|
||||
).trimEnd(),
|
||||
'}',
|
||||
);
|
||||
|
||||
@ -90,8 +90,10 @@ function _assertDeriveMapping${name}(${type} memory key) internal {
|
||||
|
||||
// GENERATE
|
||||
module.exports = format(
|
||||
header.trimEnd(),
|
||||
header,
|
||||
'contract SlotDerivationTest is Test, SymTest {',
|
||||
format(
|
||||
[].concat(
|
||||
'using SlotDerivation for bytes32;',
|
||||
'',
|
||||
array,
|
||||
@ -105,5 +107,7 @@ module.exports = format(
|
||||
})),
|
||||
),
|
||||
).map(type => (type.isValueType ? mapping(type) : boundedMapping(type))),
|
||||
),
|
||||
).trimEnd(),
|
||||
'}',
|
||||
);
|
||||
|
||||
@ -86,6 +86,7 @@ const udvt = ({ type, name }) => `\
|
||||
* @dev UDVT that represent a slot holding a ${type}.
|
||||
*/
|
||||
type ${name}SlotType is bytes32;
|
||||
|
||||
/**
|
||||
* @dev Cast an arbitrary slot to a ${name}SlotType.
|
||||
*/
|
||||
@ -104,6 +105,7 @@ function tload(${name}SlotType slot) internal view returns (${type} value) {
|
||||
value := tload(slot)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Store \`value\` at location \`slot\` in transient storage.
|
||||
*/
|
||||
@ -119,9 +121,13 @@ function tstore(${name}SlotType slot, ${type} value) internal {
|
||||
module.exports = format(
|
||||
header.trimEnd(),
|
||||
'library StorageSlot {',
|
||||
format(
|
||||
[].concat(
|
||||
TYPES.map(type => struct(type)),
|
||||
TYPES.flatMap(type => [get(type), type.isValueType ? '' : getStorage(type)]),
|
||||
TYPES.flatMap(type => [get(type), !type.isValueType && getStorage(type)].filter(Boolean)),
|
||||
TYPES.filter(type => type.isValueType).map(type => udvt(type)),
|
||||
TYPES.filter(type => type.isValueType).map(type => transient(type)),
|
||||
),
|
||||
).trimEnd(),
|
||||
'}',
|
||||
);
|
||||
|
||||
@ -54,12 +54,17 @@ function tstore(bytes32 slot, ${type} value) public {
|
||||
|
||||
// GENERATE
|
||||
module.exports = format(
|
||||
header.trimEnd(),
|
||||
header,
|
||||
'contract StorageSlotMock is Multicall {',
|
||||
format(
|
||||
[].concat(
|
||||
'using StorageSlot for *;',
|
||||
'',
|
||||
TYPES.filter(type => type.isValueType).map(type => storageSetValueType(type)),
|
||||
TYPES.filter(type => type.isValueType).map(type => storageGetValueType(type)),
|
||||
TYPES.filter(type => !type.isValueType).map(type => storageSetNonValueType(type)),
|
||||
TYPES.filter(type => type.isValueType).map(type => transient(type)),
|
||||
),
|
||||
).trimEnd(),
|
||||
'}',
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user