Skip to content

Commit

Permalink
Add { $wordsized: <expression> } sugar
Browse files Browse the repository at this point in the history
... to mirror equivalent "$wordsize" expression
  • Loading branch information
gnidan committed Jun 25, 2024
1 parent fb7cce7 commit 2865e43
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 24 deletions.
6 changes: 6 additions & 0 deletions packages/pointers/src/evaluate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,5 +226,11 @@ describe("evaluate", () => {
expect(data).toHaveLength(1);
expect(data).toEqual(Data.fromNumber(0xcd));
}

{
const data = await evaluate({ $wordsized: "0xabcd" }, options);
expect(data).toHaveLength(32);
expect(data).toEqual(Data.fromNumber(0xabcd).resizeTo(32));
}
});
});
4 changes: 3 additions & 1 deletion packages/pointers/src/evaluate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,9 @@ async function evaluateResize(
): Promise<Data> {
const [[operation, subexpression]] = Object.entries(expression);

const newLength = Number(operation.match(/^\$sized([1-9]+[0-9]*)$/)![1]);
const newLength = Pointer.Expression.Resize.isToNumber(expression)
? Number(operation.match(/^\$sized([1-9]+[0-9]*)$/)![1])
: 32;

return (await evaluate(subexpression, options)).resizeTo(newLength);
}
Expand Down
50 changes: 38 additions & 12 deletions packages/pointers/src/pointer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,22 +381,48 @@ export namespace Pointer {
export const isKeccak256 =
makeIsOperation<"$keccak256", Keccak256>("$keccak256", isOperands);

export type Resize<N extends number = number> = {
[K in `$sized${N}`]: Expression;
}
export type Resize<N extends number = number> =
| Resize.ToNumber<N>
| Resize.ToWordsize;
export const isResize = <N extends number>(
value: unknown
): value is Resize<N> => {
if (
!value ||
typeof value !== "object" ||
Object.keys(value).length !== 1
) {
return false;
): value is Resize<N> =>
[
Resize.isToWordsize,
Resize.isToNumber,
].some(guard => guard(value));

export namespace Resize {
export type ToNumber<N extends number> = {
[K in `$sized${N}`]: Expression;
};
export const isToNumber = <N extends number>(
value: unknown
): value is ToNumber<N> => {
if (
!value ||
typeof value !== "object" ||
Object.keys(value).length !== 1
) {
return false;
}
const [key] = Object.keys(value);

return typeof key === "string" && /^\$sized([1-9]+[0-9]*)$/.test(key);
};

export type ToWordsize = {
$wordsized: Expression;
}
const [key] = Object.keys(value);
export const isToWordsize = (value: unknown): value is ToWordsize =>
!!value &&
typeof value === "object" &&
Object.keys(value).length === 1 &&
"$wordsized" in value &&
typeof value.$wordsized !== "undefined" &&
isExpression(value.$wordsized);


return typeof key === "string" && /^\$sized([1-9]+[0-9]*)$/.test(key);
}
}
}
2 changes: 1 addition & 1 deletion schemas/pointer.schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ examples:

"start-slot":
$keccak256:
- $sized32: "contract-variable-slot"
- $wordsized: "contract-variable-slot"

"total-slots":
# account for both zero and nonzero slot remainders by adding
Expand Down
33 changes: 23 additions & 10 deletions schemas/pointer/expression.schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -199,12 +199,15 @@ $defs:
Resize:
title: Resize data
description: |
An object of the form `{ "$sized<N>": <expression> }`, where `<N>` is the
smallest decimal representation of an unsigned integer and where
`<expression>` is another expression.
This object's value is evaluated as follows, based on the number
represented by `<N>` and the bytes width of `<expression>`:
A resize operation expression is either an object of the form
`{ "$sized<N>": <expression> }` or an object of the form
`{ "$wordsized": <expression> }`, where `<expression>` is an expression
whose value is to be resized, and, if applicable, where `<N>` is the
smallest decimal representation of an unsigned integer.
This object's value is evaluated as follows, based on the bytes width of
the value `<expression>` evaluates to and based on `<N>` (using the
value of `"$wordsize"` for `<N>` in the case of the latter form above):
- If the width equals `<N>`, this object evalutes to the same value as
`<expression>` (equivalent to the identity function or no-op).
- If the width is less than `<N>`, this object evalutes to the same value
Expand All @@ -218,15 +221,25 @@ $defs:
(These cases match the behavior that Solidity uses for resizing its
`bytesN`/`uintN` types.)
type: object
additionalProperties: false
patternProperties:
"^\\$sized([1-9]+[0-9]*)$":
$ref: "schema:ethdebug/format/pointer/expression"
oneOf:
- title: Resize to literal number of bytes
type: object
patternProperties:
"^\\$sized([1-9]+[0-9]*)$":
$ref: "schema:ethdebug/format/pointer/expression"
additionalProperties: false
- title: Resize to word-size
type: object
patternProperties:
"^\\$wordsized$":
$ref: "schema:ethdebug/format/pointer/expression"
additionalProperties: false
minProperties: 1
maxProperties: 1
examples:
- $sized2: "0x00" # 0x0000
- $sized2: "0xffffff" # 0xffff
- $wordsized: "0x00" # 0x0000000000000000000000000000000000000000000000000000000000000000

examples:
- 0
Expand Down

0 comments on commit 2865e43

Please sign in to comment.