Skip to content

Commit

Permalink
centralize abi cleanup logic
Browse files Browse the repository at this point in the history
  • Loading branch information
nedsalk committed Dec 15, 2024
1 parent 1b59dda commit d5df0f1
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 65 deletions.
53 changes: 53 additions & 0 deletions packages/abi/src/parser/specifications/v1/cleanup-abi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { AbiSpecificationV1 } from './specification';

/**
* Both RawVec and RawBytes are private sway std library types
* that can never be used directly in sway,
* and the only reason they're in the abi is because they're used internally by Vec and Bytes
* and not ignored when forc builds the outputs.
* We can safely ignore them and simplify the `Vec` and `Bytes` types.
* This makes it simpler for us to consume these types in typegen and coder,
* as well as for others consuming the parsed abi,
* who now don't have to worry about this unnecessary complexity.
* `raw untyped ptr` is also in the abi only because of RawVec and RawBytes,
* so we ignore that as well.
*/
const IGNORED_TYPES = ['struct std::vec::RawVec', 'struct std::bytes::RawBytes', 'raw untyped ptr'];

export function cleanupAbi(abi: AbiSpecificationV1): AbiSpecificationV1 {
return {
...abi,
metadataTypes: abi.metadataTypes
.filter((metadataType) => !IGNORED_TYPES.includes(metadataType.type))
.map((metadataType) => {
switch (metadataType.type) {
/**
* Vectors consist of multiple components,
* but we only care about the `buf`'s first type argument
* which defines the type of the vector data.
* Everything else is being ignored,
* as it's then easier to reason about the vector
* (you just treat is as a regular struct).
*/
case 'struct std::vec::Vec':
return {
...metadataType,
components: metadataType.components?.[0].typeArguments,
};

/**
* We treat Bytes as a special type
* that is handled only based on its type name ('struct std::bytes::Bytes')
* and not its components.
*/
case 'struct std::bytes::Bytes':
return {
type: metadataType.type,
metadataTypeId: metadataType.metadataTypeId,
};
default:
return metadataType;
}
}),
};
}
26 changes: 12 additions & 14 deletions packages/abi/src/parser/specifications/v1/parser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Abi, AbiConcreteType } from '../../abi';

import { cleanupAbi } from './cleanup-abi';
import { mapAttribute } from './map-attribute';
import { ResolvableType } from './resolvable-type';
import { ResolvedType } from './resolved-type';
Expand All @@ -14,15 +15,12 @@ import type {

export class AbiParserV1 {
static parse(abi: AbiSpecificationV1): Abi {
const resolvableTypes = abi.metadataTypes
.map((metadataType) => new ResolvableType(abi, metadataType.metadataTypeId, undefined))
.filter(
(resolveableType) =>
resolveableType.swayType !== 'struct std::vec::RawVec' &&
resolveableType.swayType !== 'struct std::bytes::RawBytes'
);
const cleanAbi = cleanupAbi(abi);
const resolvableTypes = cleanAbi.metadataTypes.map(
(metadataType) => new ResolvableType(cleanAbi, metadataType.metadataTypeId, undefined)
);

const concreteTypes = abi.concreteTypes.map((concreteType) => {
const concreteTypes = cleanAbi.concreteTypes.map((concreteType) => {
const resolvableType = resolvableTypes.find(
(resolvable) => resolvable.metadataTypeId === concreteType.metadataTypeId
);
Expand All @@ -41,9 +39,9 @@ export class AbiParserV1 {
return {
metadataTypes: resolvableTypes.map((rt) => rt.toAbiType()),
concreteTypes,
encodingVersion: abi.encodingVersion,
programType: abi.programType as Abi['programType'],
functions: abi.functions.map((fn: AbiFunctionV1) => ({
encodingVersion: cleanAbi.encodingVersion,
programType: cleanAbi.programType as Abi['programType'],
functions: cleanAbi.functions.map((fn: AbiFunctionV1) => ({
attributes: fn.attributes?.map(mapAttribute) ?? undefined,
name: fn.name,
output: getType(fn.output),
Expand All @@ -52,15 +50,15 @@ export class AbiParserV1 {
type: getType(input.concreteTypeId),
})),
})),
loggedTypes: abi.loggedTypes.map((loggedType: AbiLoggedTypeV1) => ({
loggedTypes: cleanAbi.loggedTypes.map((loggedType: AbiLoggedTypeV1) => ({
logId: loggedType.logId,
type: getType(loggedType.concreteTypeId),
})),
messageTypes: abi.messagesTypes.map((messageType: AbiMessageTypeV1) => ({
messageTypes: cleanAbi.messagesTypes.map((messageType: AbiMessageTypeV1) => ({
messageId: messageType.messageId,
type: getType(messageType.concreteTypeId),
})),
configurables: abi.configurables.map((configurable: AbiConfigurableV1) => ({
configurables: cleanAbi.configurables.map((configurable: AbiConfigurableV1) => ({
name: configurable.name,
offset: configurable.offset,
type: getType(configurable.concreteTypeId),
Expand Down
18 changes: 1 addition & 17 deletions packages/abi/src/parser/specifications/v1/resolvable-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,7 @@ export class ResolvableType {
new ResolvableType(this.abi, tp, undefined),
]);

let components = this.metadataType.components;

/**
* Vectors consist of multiple components,
* but we only care about the `buf`'s first type argument
* which defines the type of the vector data.
* Everything else is being ignored,
* as it's then easier to reason about the vector
* (you just treat is as a regular struct).
*/
if (swayTypeMatchers.vector(this.metadataType.type)) {
components = components
?.map((component) => (component.name === 'buf' ? component.typeArguments?.[0] : undefined))
.filter((x) => x !== undefined) as AbiComponentV1[];
}

this.components = components?.map((c) => this.handleComponent(this, c));
this.components = this.metadataType.components?.map((c) => this.handleComponent(this, c));
}

toComponentType(): AbiTypeComponent['type'] {
Expand Down
Loading

0 comments on commit d5df0f1

Please sign in to comment.