gen-typescript-from-tolk-dev 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -29
- package/dist/abi-types.d.ts +169 -0
- package/dist/abi-types.js +8 -0
- package/dist/abi.d.ts +54 -0
- package/dist/abi.js +13 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +83 -0
- package/dist/codegen-ctx.d.ts +66 -0
- package/dist/codegen-ctx.js +108 -0
- package/dist/emit-field-defs.d.ts +5 -0
- package/dist/emit-field-defs.js +61 -0
- package/dist/emit-pack-unpack.d.ts +9 -0
- package/dist/emit-pack-unpack.js +296 -0
- package/dist/emit-stack-rw.d.ts +4 -0
- package/dist/emit-stack-rw.js +232 -0
- package/dist/emit-ts-types.d.ts +6 -0
- package/dist/emit-ts-types.js +74 -0
- package/dist/formatting.d.ts +15 -0
- package/{src/formatting.ts → dist/formatting.js} +26 -28
- package/dist/generate-ts-wrappers.d.ts +7 -0
- package/dist/generate-ts-wrappers.js +444 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +11 -0
- package/dist/out-template.generated.d.ts +1 -0
- package/dist/out-template.generated.js +6 -0
- package/dist/types-kernel.d.ts +10 -0
- package/dist/types-kernel.js +212 -0
- package/dist/unsupported-errors.d.ts +28 -0
- package/{src/unsupported-errors.ts → dist/unsupported-errors.js} +33 -39
- package/package.json +56 -30
- package/bin/generator.js +0 -15
- package/src/abi-types.ts +0 -157
- package/src/abi.ts +0 -132
- package/src/cli-generate-from-abi-json.ts +0 -21
- package/src/codegen-ctx.ts +0 -115
- package/src/dynamic-ctx.ts +0 -55
- package/src/dynamic-debug-print.ts +0 -191
- package/src/dynamic-get-methods.ts +0 -454
- package/src/dynamic-serialization.ts +0 -430
- package/src/dynamic-validation.ts +0 -55
- package/src/emit-field-defs.ts +0 -60
- package/src/emit-pack-unpack.ts +0 -280
- package/src/emit-stack-rw.ts +0 -239
- package/src/emit-ts-types.ts +0 -66
- package/src/generate-from-abi-json.ts +0 -22
- package/src/generate-ts-wrappers.ts +0 -477
- package/src/out-template.ts +0 -514
- package/src/tolk-to-abi.ts +0 -5
- package/src/types-kernel.ts +0 -215
- package/tsconfig.json +0 -13
package/src/abi-types.ts
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
ABI Types — describes the type system and symbols definitions in the Tolk language.
|
|
3
|
-
The goal is to have a full bidirectional mapping from any Tolk struct to ABI:
|
|
4
|
-
all tricky types like generics, aliases, inline unions, etc. should be supported.
|
|
5
|
-
Then, any input could be packed to a cell and unpacked back — exactly as it's done by the compiler.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
// Since ABI should be JSON-serialized, all 'bigint' numbers are stored as strings.
|
|
9
|
-
type bigint_as_string = string
|
|
10
|
-
|
|
11
|
-
// Ty is an "ABI Type" that fully reflects the type system in Tolk.
|
|
12
|
-
export type Ty =
|
|
13
|
-
| { kind: 'int' }
|
|
14
|
-
| { kind: 'intN'; n: number }
|
|
15
|
-
| { kind: 'uintN'; n: number }
|
|
16
|
-
| { kind: 'varintN', n: number }
|
|
17
|
-
| { kind: 'varuintN', n: number }
|
|
18
|
-
| { kind: 'coins' }
|
|
19
|
-
| { kind: 'bool' }
|
|
20
|
-
| { kind: 'cell' }
|
|
21
|
-
| { kind: 'builder' }
|
|
22
|
-
| { kind: 'slice' }
|
|
23
|
-
| { kind: 'string' }
|
|
24
|
-
| { kind: 'remaining' }
|
|
25
|
-
| { kind: 'address' }
|
|
26
|
-
| { kind: 'addressOpt' }
|
|
27
|
-
| { kind: 'addressExt' }
|
|
28
|
-
| { kind: 'addressAny' }
|
|
29
|
-
| { kind: 'bitsN'; n: number }
|
|
30
|
-
| { kind: 'nullLiteral' }
|
|
31
|
-
| { kind: 'callable' }
|
|
32
|
-
| { kind: 'void' }
|
|
33
|
-
| { kind: 'unknown' }
|
|
34
|
-
| { kind: 'nullable'; inner: Ty; stackTypeId?: number; stackWidth?: number }
|
|
35
|
-
| { kind: 'cellOf'; inner: Ty }
|
|
36
|
-
| { kind: 'arrayOf'; inner: Ty }
|
|
37
|
-
| { kind: 'lispListOf'; inner: Ty }
|
|
38
|
-
| { kind: 'tensor'; items: Ty[] }
|
|
39
|
-
| { kind: 'shapedTuple'; items: Ty[] }
|
|
40
|
-
| { kind: 'mapKV'; k: Ty; v: Ty }
|
|
41
|
-
| { kind: 'EnumRef'; enumName: string }
|
|
42
|
-
| { kind: 'StructRef'; structName: string; typeArgs?: Ty[] }
|
|
43
|
-
| { kind: 'AliasRef'; aliasName: string; typeArgs?: Ty[] }
|
|
44
|
-
| { kind: 'genericT'; nameT: string }
|
|
45
|
-
| { kind: 'union'; variants: UnionVariant[]; stackWidth: number }
|
|
46
|
-
|
|
47
|
-
// UnionVariant exists for every `T_i` in a union type `T1 | T2 | ...`.
|
|
48
|
-
// For binary serialization, a union should have a prefix tree,
|
|
49
|
-
// which is either defined explicitly with struct prefixes: `struct (0x12345678) CounterIncrement`,
|
|
50
|
-
// or auto-generated (implicit), e.g. `int8 | int16 | int32` is serialized as '00'+int8 / '01'+int16 / '10'+int32.
|
|
51
|
-
export interface UnionVariant {
|
|
52
|
-
variantTy: Ty
|
|
53
|
-
prefixStr: string
|
|
54
|
-
prefixLen: number
|
|
55
|
-
isPrefixImplicit?: boolean
|
|
56
|
-
stackTypeId: number
|
|
57
|
-
stackWidth: number
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// ABIConstExpression is "an expression" that occurs in fields defaults.
|
|
61
|
-
// Example:
|
|
62
|
-
// > struct Demo {
|
|
63
|
-
// > initial: int8 = 123
|
|
64
|
-
// > owner: address = address("UQ...")
|
|
65
|
-
// > }
|
|
66
|
-
// Then both fields have a default value:
|
|
67
|
-
// initial: { kind: 'int', v: '123' }
|
|
68
|
-
// owner: { kind: 'address', addr: 'UQ...' }
|
|
69
|
-
export type ABIConstExpression =
|
|
70
|
-
| { kind: 'int'; v: bigint_as_string }
|
|
71
|
-
| { kind: 'bool'; v: boolean }
|
|
72
|
-
| { kind: 'slice'; hex: string }
|
|
73
|
-
| { kind: 'string'; str: string }
|
|
74
|
-
| { kind: 'address'; addr: string }
|
|
75
|
-
| { kind: 'tensor'; items: ABIConstExpression[] }
|
|
76
|
-
| { kind: 'shapedTuple'; items: ABIConstExpression[] }
|
|
77
|
-
| { kind: 'object'; structName: string; fields: ABIConstExpression[] }
|
|
78
|
-
| { kind: 'castTo'; inner: ABIConstExpression; castTo: Ty }
|
|
79
|
-
| { kind: 'null' }
|
|
80
|
-
|
|
81
|
-
// ABICustomSerializers is present in a struct/alias if that type has custom serializers in Tolk code:
|
|
82
|
-
//
|
|
83
|
-
// fun SomeType.packToBuilder(self, mutate b: builder) { ... }
|
|
84
|
-
// fun SomeType.unpackFromSlice(mutate s: slice): SomeAlias { ... }
|
|
85
|
-
//
|
|
86
|
-
// Body of these functions is not a part of ABI (it's arbitrary Tolk code).
|
|
87
|
-
// To make serialization work (e.g., in TypeScript wrappers),
|
|
88
|
-
// one should provide equivalent implementations in a language ABI is applied to.
|
|
89
|
-
export interface ABICustomSerializers {
|
|
90
|
-
packToBuilder: boolean
|
|
91
|
-
unpackFromSlice: boolean
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// ABIStruct represents a Tolk struct.
|
|
95
|
-
// Examples:
|
|
96
|
-
//
|
|
97
|
-
// > struct Point { x: int, y: int }
|
|
98
|
-
// A simple struct, not serializable (because 'int', not 'int8' or similar)
|
|
99
|
-
//
|
|
100
|
-
// > struct Wrapper<TItem> { item: TItem }
|
|
101
|
-
// A generic struct (has typeParams), fields[0].ty = { kind: 'genericT', nameT: 'TItem' }
|
|
102
|
-
//
|
|
103
|
-
// > struct (0x12345678) Increment { ... }
|
|
104
|
-
// Has a serialization prefix: prefixStr = '0x12345678', prefixLen = 32
|
|
105
|
-
export interface ABIStruct {
|
|
106
|
-
kind: 'Struct'
|
|
107
|
-
name: string
|
|
108
|
-
typeParams?: string[]
|
|
109
|
-
prefix?: {
|
|
110
|
-
prefixStr: string
|
|
111
|
-
prefixLen: number
|
|
112
|
-
}
|
|
113
|
-
fields: {
|
|
114
|
-
name: string
|
|
115
|
-
ty: Ty
|
|
116
|
-
defaultValue?: ABIConstExpression
|
|
117
|
-
description?: string
|
|
118
|
-
}[]
|
|
119
|
-
customPackUnpack?: ABICustomSerializers
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// ABIAlias represents a Tolk type alias.
|
|
123
|
-
// Examples:
|
|
124
|
-
//
|
|
125
|
-
// > type UserId = int32
|
|
126
|
-
// A simple alias, targetTy = { kind: 'intN', n: 32 }
|
|
127
|
-
//
|
|
128
|
-
// > type Maybe<T> = MaybeNothing | MaybeJust<T>
|
|
129
|
-
// A generic alias (has typeParams), targetTy = { kind: 'union', variants: ... }
|
|
130
|
-
//
|
|
131
|
-
// An alias is serialized as its target, unless it has custom serializers in Tolk code.
|
|
132
|
-
export interface ABIAlias {
|
|
133
|
-
kind: 'Alias'
|
|
134
|
-
name: string
|
|
135
|
-
targetTy: Ty
|
|
136
|
-
typeParams?: string[]
|
|
137
|
-
customPackUnpack?: ABICustomSerializers
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// ABIEnum represents a Tolk enum.
|
|
141
|
-
// Examples:
|
|
142
|
-
//
|
|
143
|
-
// > enum Color { Red, Green, Blue }
|
|
144
|
-
// Has 3 members (values '0', '1', '2'), encoded as 'uint2' (auto-calculated by the compiler).
|
|
145
|
-
//
|
|
146
|
-
// > enum Mode: int8 { User = 0, Admin = 127 }
|
|
147
|
-
// Has 2 members, encoded as 'int8' (specified manually).
|
|
148
|
-
export interface ABIEnum {
|
|
149
|
-
kind: 'Enum'
|
|
150
|
-
name: string
|
|
151
|
-
encodedAs: Ty
|
|
152
|
-
members: {
|
|
153
|
-
name: string
|
|
154
|
-
value: bigint_as_string
|
|
155
|
-
}[]
|
|
156
|
-
customPackUnpack?: ABICustomSerializers
|
|
157
|
-
}
|
package/src/abi.ts
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
ABI of a contract describes how the contract is "seen" by external observers:
|
|
3
|
-
its get methods, incoming messages, etc.
|
|
4
|
-
Given an ABI, it becomes possible to
|
|
5
|
-
- post messages from a client, since ABI provides sufficient info for serialization
|
|
6
|
-
- generate wrappers for TypeScript and other languages
|
|
7
|
-
- render its storage in the explorer
|
|
8
|
-
- create a UI to interact with a contract from Web/IDE
|
|
9
|
-
- etc.
|
|
10
|
-
The ABI is emitted by the Tolk compiler, along with bytecode.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { Ty, ABIStruct, ABIAlias, ABIEnum, ABIConstExpression } from './abi-types'
|
|
14
|
-
|
|
15
|
-
// ABIGetMethod is a "get method" (aka "contract getter").
|
|
16
|
-
// In Tolk code, getters are created with `get fun`.
|
|
17
|
-
// Example:
|
|
18
|
-
// > get fun calcData(owner: address): SomeStruct { ... }
|
|
19
|
-
// It has one parameter with ty = { kind: 'address' },
|
|
20
|
-
// its returnTy is { kind: 'StructRef', structName: 'SomeStruct' }.
|
|
21
|
-
// Note, that getters are called off-chain — via the TVM stack, not via serialization.
|
|
22
|
-
// (For instance, they can return 'int' or 'slice', although they are not serializable)
|
|
23
|
-
export interface ABIGetMethod {
|
|
24
|
-
tvmMethodId: number
|
|
25
|
-
name: string
|
|
26
|
-
parameters: {
|
|
27
|
-
name: string
|
|
28
|
-
ty: Ty
|
|
29
|
-
description?: string
|
|
30
|
-
defaultValue?: ABIConstExpression
|
|
31
|
-
}[]
|
|
32
|
-
returnTy: Ty
|
|
33
|
-
description?: string
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// ABIInternalMessage is "an incoming internal message" (handled by `onInternalMessage`).
|
|
37
|
-
// In practice, a user describes each message as a struct:
|
|
38
|
-
// > struct (0x12345678) Increment { ... }
|
|
39
|
-
// > struct (0x23456789) Reset { ... }
|
|
40
|
-
// Then, ABI of a contract will contain those two messages:
|
|
41
|
-
// * bodyTy = { kind: 'StructRef', structName: 'Increment' }
|
|
42
|
-
// * bodyTy = { kind: 'StructRef', structName: 'Reset' }
|
|
43
|
-
// Theoretically, bodyTy can be something else: e.g., instantiation `Transfer<ForwardPayload>`.
|
|
44
|
-
// Description, minimalMsgValue, and other properties may be set with an `@abi` attribute:
|
|
45
|
-
// > @abi(...)
|
|
46
|
-
// > struct (0x12345678) Increment { ... }
|
|
47
|
-
export interface ABIInternalMessage {
|
|
48
|
-
bodyTy: Ty
|
|
49
|
-
description?: string
|
|
50
|
-
minimalMsgValue?: number
|
|
51
|
-
preferredSendMode?: number
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// ABIExternalMessage is "an incoming external message" (handled by `onExternalMessage`).
|
|
55
|
-
// It's either a 'slice' or some struct.
|
|
56
|
-
export interface ABIExternalMessage {
|
|
57
|
-
bodyTy: Ty
|
|
58
|
-
description?: string
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// ABIOutgoingMessage is "an outgoing internal/external message".
|
|
62
|
-
// In Tolk code, those are calls to `createMessage`.
|
|
63
|
-
export interface ABIOutgoingMessage {
|
|
64
|
-
bodyTy: Ty
|
|
65
|
-
description?: string
|
|
66
|
-
// todo need BounceMode? if yes, what if 2 messages of type T sent with different modes?
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// ABIStorage defines shape of a storage.
|
|
70
|
-
// Most often, it's a regular struct, serializable into a cell.
|
|
71
|
-
// In the case of NFT, when a storage changes its shape (several fields appear after deployment),
|
|
72
|
-
// the "initial storage" can also be expressed: it's called "storage at deployment".
|
|
73
|
-
// The storage is used to visualize current contract state and to calculate its address.
|
|
74
|
-
export interface ABIStorage {
|
|
75
|
-
storageTy?: Ty
|
|
76
|
-
storageAtDeploymentTy?: Ty
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// ABIThrownError is an errCode fired by `throw` or `assert` in Tolk code.
|
|
80
|
-
export interface ABIThrownError {
|
|
81
|
-
kind: 'plainInt' | 'constant' | 'enumMember'
|
|
82
|
-
name?: string
|
|
83
|
-
errCode: number
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// ContractABI is a final result — the ABI of a TON smart contract.
|
|
87
|
-
//
|
|
88
|
-
// Partially, its properties may be specified by a user manually:
|
|
89
|
-
// > contract MyName {
|
|
90
|
-
// > author: "Dima"
|
|
91
|
-
// > incomingMessages: SomeUnion
|
|
92
|
-
// > }
|
|
93
|
-
//
|
|
94
|
-
// Partially, its properties are automatically calculated by the compiler:
|
|
95
|
-
// - outgoingMessages, via the calls to `createMessage`
|
|
96
|
-
// - thrownErrors, via `throw` and `assert` statements
|
|
97
|
-
// - getMethods, essentially `get fun`
|
|
98
|
-
//
|
|
99
|
-
// Auto-calculated properties may be manually enriched via the `@abi` annotation:
|
|
100
|
-
// > @abi({
|
|
101
|
-
// > description: "...",
|
|
102
|
-
// > minimalMsgValue: ton("0.05"),
|
|
103
|
-
// > })
|
|
104
|
-
// > struct SomeOutgoingMessage { ... }
|
|
105
|
-
//
|
|
106
|
-
// todo opened questions:
|
|
107
|
-
// - clientTy for struct fields
|
|
108
|
-
// - potentially missing struct fields, especially for describing FunC
|
|
109
|
-
// - union types are not supported in get methods now, they have complicated stack layout
|
|
110
|
-
// - TEPs and namespaces?
|
|
111
|
-
export interface ContractABI {
|
|
112
|
-
contractName: string
|
|
113
|
-
author?: string
|
|
114
|
-
version?: string
|
|
115
|
-
description?: string
|
|
116
|
-
|
|
117
|
-
declarations: (ABIStruct | ABIAlias | ABIEnum)[]
|
|
118
|
-
|
|
119
|
-
storage: ABIStorage
|
|
120
|
-
incomingMessages: ABIInternalMessage[]
|
|
121
|
-
incomingExternal: ABIExternalMessage[]
|
|
122
|
-
outgoingMessages: ABIOutgoingMessage[]
|
|
123
|
-
emittedEvents: ABIOutgoingMessage[]
|
|
124
|
-
getMethods: ABIGetMethod[]
|
|
125
|
-
thrownErrors: ABIThrownError[]
|
|
126
|
-
|
|
127
|
-
compilerName: string
|
|
128
|
-
compilerVersion: string
|
|
129
|
-
|
|
130
|
-
// todo is it a part of ABI? the same question for source maps in the future
|
|
131
|
-
codeBoc64: string
|
|
132
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { generateTypeScriptFromAbiJson } from './generate-from-abi-json';
|
|
2
|
-
|
|
3
|
-
const HELP_TEXT = `Usage:
|
|
4
|
-
generator '<contract-abi-json>'
|
|
5
|
-
|
|
6
|
-
Prints the generated TypeScript wrapper to stdout.`;
|
|
7
|
-
|
|
8
|
-
function parseCliArgs(argv: string[]): string {
|
|
9
|
-
if (argv.length !== 1) {
|
|
10
|
-
throw new Error(`Expected a single positional ABI JSON argument.\n\n${HELP_TEXT}`);
|
|
11
|
-
}
|
|
12
|
-
const abiJson = argv[0];
|
|
13
|
-
if (abiJson.length === 0) {
|
|
14
|
-
throw new Error(`ABI JSON argument is required.\n\n${HELP_TEXT}`);
|
|
15
|
-
}
|
|
16
|
-
return abiJson;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export async function runGeneratorFromAbiCli(argv: string[]): Promise<void> {
|
|
20
|
-
process.stdout.write(generateTypeScriptFromAbiJson(parseCliArgs(argv)));
|
|
21
|
-
}
|
package/src/codegen-ctx.ts
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import type { Ty, ABIStruct, ABIAlias, ABIEnum } from './abi-types';
|
|
2
|
-
import { SymbolNotFound } from './unsupported-errors';
|
|
3
|
-
|
|
4
|
-
// Functions implemented in an output template, used to detect name shadowing.
|
|
5
|
-
export const RUNTIME = {
|
|
6
|
-
beginCell: 'beginCell',
|
|
7
|
-
lookupPrefix: 'lookupPrefix',
|
|
8
|
-
lookupPrefixAndEat: 'lookupPrefixAndEat',
|
|
9
|
-
throwNonePrefixMatch: 'throwNonePrefixMatch',
|
|
10
|
-
loadAndCheckPrefix32: 'loadAndCheckPrefix32',
|
|
11
|
-
loadAndCheckPrefix: 'loadAndCheckPrefix',
|
|
12
|
-
loadTolkBitsN: 'loadTolkBitsN',
|
|
13
|
-
storeTolkBitsN: 'storeTolkBitsN',
|
|
14
|
-
loadTolkAddressAny: 'loadTolkAddressAny',
|
|
15
|
-
storeTolkAddressAny: 'storeTolkAddressAny',
|
|
16
|
-
loadTolkRemaining: 'loadTolkRemaining',
|
|
17
|
-
storeTolkRemaining: 'storeTolkRemaining',
|
|
18
|
-
loadCellRef: 'loadCellRef',
|
|
19
|
-
storeCellRef: 'storeCellRef',
|
|
20
|
-
loadArrayOf: 'loadArrayOf',
|
|
21
|
-
storeArrayOf: 'storeArrayOf',
|
|
22
|
-
loadLispListOf: 'loadLispListOf',
|
|
23
|
-
storeLispListOf: 'storeLispListOf',
|
|
24
|
-
storeTolkNullable: 'storeTolkNullable',
|
|
25
|
-
makeCellFrom: 'makeCellFrom',
|
|
26
|
-
createDictionaryValue: 'createDictionaryValue',
|
|
27
|
-
registerCustomPackUnpack: 'registerCustomPackUnpack',
|
|
28
|
-
ExtraSendOptions: 'ExtraSendOptions',
|
|
29
|
-
StackReader: 'StackReader',
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
// SymTable is an index of top-level declarations.
|
|
33
|
-
export class SymTable {
|
|
34
|
-
private structs = new Map<string, ABIStruct>()
|
|
35
|
-
private aliases = new Map<string, ABIAlias>()
|
|
36
|
-
private enums = new Map<string, ABIEnum>()
|
|
37
|
-
|
|
38
|
-
constructor(declarations: (ABIStruct | ABIAlias | ABIEnum)[]) {
|
|
39
|
-
for (const n of declarations) {
|
|
40
|
-
if (n.kind === 'Struct') this.structs.set(n.name, n);
|
|
41
|
-
else if (n.kind === 'Alias') this.aliases.set(n.name, n);
|
|
42
|
-
else if (n.kind === 'Enum') this.enums.set(n.name, n);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
getStruct(structName: string): ABIStruct {
|
|
47
|
-
const s = this.structs.get(structName);
|
|
48
|
-
if (s === undefined)
|
|
49
|
-
throw new SymbolNotFound('struct', structName);
|
|
50
|
-
return s;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
getAlias(aliasName: string): ABIAlias {
|
|
54
|
-
const a = this.aliases.get(aliasName);
|
|
55
|
-
if (a === undefined)
|
|
56
|
-
throw new SymbolNotFound('alias', aliasName);
|
|
57
|
-
return a;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
getEnum(enumName: string): ABIEnum {
|
|
61
|
-
const e = this.enums.get(enumName);
|
|
62
|
-
if (e === undefined)
|
|
63
|
-
throw new SymbolNotFound('enum', enumName);
|
|
64
|
-
return e;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
getAliasTarget(aliasName: string): Ty {
|
|
68
|
-
return this.getAlias(aliasName).targetTy;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// CodegenCtx represents current state of TypeScript code generation.
|
|
73
|
-
// It contains properties that affect final output and help resolve symbols.
|
|
74
|
-
// For example, we want Tolk `int64` not be just TS `bigint`, but
|
|
75
|
-
// to export `type int64 = bigint` and use `int64` directly,
|
|
76
|
-
// that's why we track all occurred intN.
|
|
77
|
-
// Same for other public properties, they are modified while iterating through declarations.
|
|
78
|
-
export class CodegenCtx {
|
|
79
|
-
readonly symbols: SymTable
|
|
80
|
-
|
|
81
|
-
intNOccurred = new Set<string>(['int8', 'int16', 'int32', 'int256'])
|
|
82
|
-
uintNOccurred = new Set<string>(['uint8', 'uint16', 'uint32', 'uint256'])
|
|
83
|
-
varIntNOccurred = new Set<string>()
|
|
84
|
-
bitsNOccurred = new Set<string>()
|
|
85
|
-
|
|
86
|
-
has_RemainingBitsAndRefs = false
|
|
87
|
-
has_customPackUnpack = false
|
|
88
|
-
has_customDictV = false
|
|
89
|
-
has_implicitUnionPrefix = false
|
|
90
|
-
has_non32Prefixes = false
|
|
91
|
-
has_addressAny = false
|
|
92
|
-
has_arrayOf = false
|
|
93
|
-
has_lispListOf = false
|
|
94
|
-
|
|
95
|
-
stackReadsUnknown = false
|
|
96
|
-
stackReadsArrayOf = false
|
|
97
|
-
stackReadsLispListOf = false
|
|
98
|
-
stackReadsSnakeString = false
|
|
99
|
-
stackReadsTuple = false
|
|
100
|
-
stackReadsMapKV = false
|
|
101
|
-
stackReadsBuilder = false
|
|
102
|
-
stackReadsNullable = false
|
|
103
|
-
stackReadsWideNullable = false
|
|
104
|
-
stackReadsUnionType = false
|
|
105
|
-
stackReadsCellRef = false
|
|
106
|
-
stackReadsNullLiteral = false
|
|
107
|
-
|
|
108
|
-
constructor(declarations: (ABIStruct | ABIAlias | ABIEnum)[]) {
|
|
109
|
-
this.symbols = new SymTable(declarations);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
sortOccurred(occurred: Set<string>): string[] {
|
|
113
|
-
return [...occurred].sort((a, b) => a.length - b.length || a.localeCompare(b));
|
|
114
|
-
}
|
|
115
|
-
}
|
package/src/dynamic-ctx.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import * as c from '@ton/core'
|
|
2
|
-
import { SymTable } from './codegen-ctx'
|
|
3
|
-
import { ABIGetMethod, ContractABI } from './abi'
|
|
4
|
-
import { CantPackDynamic, CantUnpackDynamic } from './unsupported-errors'
|
|
5
|
-
|
|
6
|
-
type CustomPackToBuilderFn<T> = (self: T, b: c.Builder) => void
|
|
7
|
-
type CustomUnpackFromSliceFn<T> = (s: c.Slice) => T
|
|
8
|
-
|
|
9
|
-
export class DynamicCtx {
|
|
10
|
-
readonly contractName: string;
|
|
11
|
-
readonly getMethods: ABIGetMethod[];
|
|
12
|
-
readonly symbols: SymTable;
|
|
13
|
-
|
|
14
|
-
private customSerializersRegistry: Map<string, [CustomPackToBuilderFn<any> | null, CustomUnpackFromSliceFn<any> | null]> = new Map;
|
|
15
|
-
|
|
16
|
-
constructor(contract: ContractABI) {
|
|
17
|
-
this.contractName = contract.contractName;
|
|
18
|
-
this.getMethods = contract.getMethods;
|
|
19
|
-
this.symbols = new SymTable(contract.declarations);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
registerCustomPackUnpack<T>(
|
|
23
|
-
typeName: string,
|
|
24
|
-
packToBuilderFn: CustomPackToBuilderFn<T> | null,
|
|
25
|
-
unpackFromSliceFn: CustomUnpackFromSliceFn<T> | null,
|
|
26
|
-
) {
|
|
27
|
-
if (this.customSerializersRegistry.has(typeName)) {
|
|
28
|
-
throw new Error(`Custom pack/unpack for '${typeName}' already registered`);
|
|
29
|
-
}
|
|
30
|
-
this.customSerializersRegistry.set(typeName, [packToBuilderFn, unpackFromSliceFn]);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
getCustomPackFnOrThrow(typeName: string, fieldPath: string): CustomPackToBuilderFn<any> {
|
|
34
|
-
let packUnpack = this.customSerializersRegistry.get(typeName);
|
|
35
|
-
let packFn = packUnpack ? packUnpack[0] : null;
|
|
36
|
-
if (!packFn) {
|
|
37
|
-
throw new CantPackDynamic(fieldPath, `custom packToBuilder was not registered for '${typeName}'`);
|
|
38
|
-
}
|
|
39
|
-
return packFn;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
getCustomUnpackFnOrThrow(typeName: string, fieldPath: string): CustomUnpackFromSliceFn<any> {
|
|
43
|
-
let packUnpack = this.customSerializersRegistry.get(typeName);
|
|
44
|
-
let unpackFn = packUnpack ? packUnpack[1] : null;
|
|
45
|
-
if (!unpackFn) {
|
|
46
|
-
throw new CantUnpackDynamic(fieldPath, `custom unpackFromSlice was not registered for '${typeName}'`);
|
|
47
|
-
}
|
|
48
|
-
return unpackFn;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
findGetMethod(getMethodName: string): ABIGetMethod | undefined {
|
|
52
|
-
return this.getMethods.find(m => m.name === getMethodName);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import * as c from '@ton/core';
|
|
2
|
-
import { DynamicCtx } from './dynamic-ctx';
|
|
3
|
-
import { Ty } from './abi-types';
|
|
4
|
-
import { makeTvmTupleDynamic, StackReader } from './dynamic-get-methods';
|
|
5
|
-
import { createTonCoreDictionaryKey, createTonCoreDictionaryValue, dynamicUnpack } from './dynamic-serialization';
|
|
6
|
-
import { calcWidthOnStack, createLabelsForUnion, instantiateGenerics, renderTy } from './types-kernel';
|
|
7
|
-
|
|
8
|
-
/*
|
|
9
|
-
Debug printing from the TVM stack.
|
|
10
|
-
Given TupleItem[] of a type Ty, and an ABI (via DynamicCtx),
|
|
11
|
-
produce a human-readable string representation.
|
|
12
|
-
This is used for debugging / println / explorers —
|
|
13
|
-
to show the value of a variable or a get method result in a readable format.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
function printRawCellContents(c: c.Cell): string {
|
|
17
|
-
return c.toString();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Read `ty` from a stack and return formatted human-readable representation.
|
|
21
|
-
// Throws if a stack is too small or failed to deserialize a value (inside `Cell<T>` or a map, for example).
|
|
22
|
-
function debugFormat(ctx: DynamicCtx, r: StackReader, fieldPath: string, ty: Ty, unTupleIfW = false): string {
|
|
23
|
-
if (unTupleIfW) {
|
|
24
|
-
let wOnStack = calcWidthOnStack(ctx.symbols, ty);
|
|
25
|
-
if (wOnStack !== 1) {
|
|
26
|
-
return r.readTuple(wOnStack, (r) => debugFormat(ctx, r, fieldPath, ty, false));
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
switch (ty.kind) {
|
|
31
|
-
case 'int':
|
|
32
|
-
case 'intN':
|
|
33
|
-
case 'uintN':
|
|
34
|
-
case 'varintN':
|
|
35
|
-
case 'varuintN':
|
|
36
|
-
case 'coins': return r.readBigInt().toString();
|
|
37
|
-
case 'bool': return r.readBoolean() ? 'true' : 'false';
|
|
38
|
-
case 'cell': return `cell{${printRawCellContents(r.readCell())}}`;
|
|
39
|
-
case 'builder': return `builder{${printRawCellContents(r.readBuilder().endCell())}}`;
|
|
40
|
-
case 'slice':
|
|
41
|
-
case 'bitsN':
|
|
42
|
-
case 'remaining': return `slice{${printRawCellContents(r.readSlice().asCell())}}`;
|
|
43
|
-
case 'string': return `"${r.readSnakeString()}"`;
|
|
44
|
-
case 'address': return r.readSlice().loadAddress().toString();
|
|
45
|
-
case 'addressOpt': return debugFormat(ctx, r, fieldPath, { kind: 'nullable', inner: { kind: 'address' } });
|
|
46
|
-
case 'addressExt': return r.readSlice().loadExternalAddress().toString();
|
|
47
|
-
case 'addressAny': {
|
|
48
|
-
let addr = dynamicUnpack(ctx, fieldPath, ty, r.readSlice());
|
|
49
|
-
return addr === 'none' ? 'addr_none' : addr.toString();
|
|
50
|
-
}
|
|
51
|
-
case 'nullLiteral': return r.readNullLiteral() ?? 'null';
|
|
52
|
-
case 'void': return '(void)';
|
|
53
|
-
case 'unknown': return debugPrintUnknown(r.readUnknown());
|
|
54
|
-
case 'nullable': {
|
|
55
|
-
if (ty.stackTypeId) {
|
|
56
|
-
return r.readWideNullable(ty.stackWidth!, (r) => debugFormat(ctx, r, fieldPath, ty.inner)) ?? 'null';
|
|
57
|
-
}
|
|
58
|
-
return r.readNullable((r) => debugFormat(ctx, r, fieldPath, ty.inner)) ?? 'null';
|
|
59
|
-
}
|
|
60
|
-
case 'cellOf': {
|
|
61
|
-
let innerStr = r.readCellRef((s) => {
|
|
62
|
-
const deserialized = dynamicUnpack(ctx, fieldPath, ty.inner, s);
|
|
63
|
-
return debugFormatValue(ctx, fieldPath, ty.inner, deserialized);
|
|
64
|
-
});
|
|
65
|
-
return `ref{${innerStr.ref}}`;
|
|
66
|
-
}
|
|
67
|
-
case 'arrayOf': {
|
|
68
|
-
let items = r.readArrayOf((r) => debugFormat(ctx, r, fieldPath, ty.inner, true));
|
|
69
|
-
return `[${items.join(', ')}]`;
|
|
70
|
-
}
|
|
71
|
-
case 'lispListOf': {
|
|
72
|
-
let items = r.readLispListOf((r) => debugFormat(ctx, r, fieldPath, ty.inner, true));
|
|
73
|
-
return `[${items.join(', ')}]`;
|
|
74
|
-
}
|
|
75
|
-
case 'tensor': {
|
|
76
|
-
let parts: string[] = [];
|
|
77
|
-
for (let i = 0; i < ty.items.length; ++i) {
|
|
78
|
-
parts.push(debugFormat(ctx, r, `${fieldPath}[${i}]`, ty.items[i]));
|
|
79
|
-
}
|
|
80
|
-
return `(${parts.join(', ')})`;
|
|
81
|
-
}
|
|
82
|
-
case 'shapedTuple': {
|
|
83
|
-
return r.readTuple(ty.items.length, (r) => {
|
|
84
|
-
let parts: string[] = [];
|
|
85
|
-
for (let i = 0; i < ty.items.length; ++i) {
|
|
86
|
-
parts.push(debugFormat(ctx, r, `${fieldPath}[${i}]`, ty.items[i], true));
|
|
87
|
-
}
|
|
88
|
-
return `[${parts.join(', ')}]`;
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
case 'mapKV': {
|
|
92
|
-
let dictKey = createTonCoreDictionaryKey(fieldPath, ty.k);
|
|
93
|
-
let dictValue = createTonCoreDictionaryValue(ctx, fieldPath, ty.v);
|
|
94
|
-
let entries: string[] = [];
|
|
95
|
-
for (let [k, v] of r.readDictionary(dictKey, dictValue)) {
|
|
96
|
-
let kStr = debugFormatValue(ctx, fieldPath, ty.k, k);
|
|
97
|
-
let vStr = debugFormatValue(ctx, `${fieldPath}[${kStr}]`, ty.v, v);
|
|
98
|
-
entries.push(`${kStr}: ${vStr}`);
|
|
99
|
-
}
|
|
100
|
-
if (entries.length === 0) {
|
|
101
|
-
return 'map{}';
|
|
102
|
-
}
|
|
103
|
-
return `map{${entries.join(', ')}}`;
|
|
104
|
-
}
|
|
105
|
-
case 'EnumRef': {
|
|
106
|
-
let value = r.readBigInt();
|
|
107
|
-
let enumRef = ctx.symbols.getEnum(ty.enumName);
|
|
108
|
-
let member = enumRef.members.find(m => BigInt(m.value) === value);
|
|
109
|
-
if (member) {
|
|
110
|
-
return `${ty.enumName}.${member.name}`;
|
|
111
|
-
}
|
|
112
|
-
return `${ty.enumName}(${value})`;
|
|
113
|
-
}
|
|
114
|
-
case 'StructRef': {
|
|
115
|
-
const structRef = ctx.symbols.getStruct(ty.structName);
|
|
116
|
-
if (structRef.fields.length === 0) {
|
|
117
|
-
return `${ty.structName} {}`;
|
|
118
|
-
}
|
|
119
|
-
const fieldStrs: string[] = [];
|
|
120
|
-
for (const f of structRef.fields) {
|
|
121
|
-
const fTy = ty.typeArgs ? instantiateGenerics(f.ty, structRef.typeParams, ty.typeArgs) : f.ty;
|
|
122
|
-
fieldStrs.push(`${f.name}: ${debugFormat(ctx, r, `${fieldPath}.${f.name}`, fTy)}`);
|
|
123
|
-
}
|
|
124
|
-
return `${ty.structName} { ${fieldStrs.join(', ')} }`;
|
|
125
|
-
}
|
|
126
|
-
case 'AliasRef': {
|
|
127
|
-
const aliasRef = ctx.symbols.getAlias(ty.aliasName);
|
|
128
|
-
const targetTy = ty.typeArgs ? instantiateGenerics(aliasRef.targetTy, aliasRef.typeParams, ty.typeArgs) : aliasRef.targetTy;
|
|
129
|
-
return debugFormat(ctx, r, fieldPath, targetTy);
|
|
130
|
-
}
|
|
131
|
-
case 'union': {
|
|
132
|
-
const variants = createLabelsForUnion(ctx.symbols, ty.variants);
|
|
133
|
-
const infoForTypeId = {} as Record<number, [number, string | null, (r: StackReader) => string]>;
|
|
134
|
-
for (let v of variants) {
|
|
135
|
-
infoForTypeId[v.stackTypeId] = [v.stackWidth, v.hasValueField ? v.labelStr : null,
|
|
136
|
-
(r: StackReader) => debugFormat(ctx, r, `${fieldPath}#${v.labelStr}`, v.variantTy)
|
|
137
|
-
];
|
|
138
|
-
}
|
|
139
|
-
// readUnionType returns `valueT` (if no label) or `{ $: "label", value: valueT }`
|
|
140
|
-
// In our case, valueT is already a string.
|
|
141
|
-
const result = r.readUnionType<string | { $: string, value: string }>(ty.stackWidth, infoForTypeId);
|
|
142
|
-
if (typeof result === 'string') {
|
|
143
|
-
// label is null, then it's a struct with its own $, like "Point { ... }", or it's "null"
|
|
144
|
-
return result;
|
|
145
|
-
}
|
|
146
|
-
// render "#label value": notation "#label" means "active variant of a union"
|
|
147
|
-
return `#${result.$} ${result.value}`;
|
|
148
|
-
}
|
|
149
|
-
case 'callable': {
|
|
150
|
-
r.readUnknown();
|
|
151
|
-
return 'continuation';
|
|
152
|
-
}
|
|
153
|
-
case 'genericT': throw new Error(`unexpected genericT=${ty.nameT}`);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Format a value that was already parsed by dynamicUnpack into a JS representation.
|
|
158
|
-
// This is used for inner values of `Cell<T>` and map entries, where we parse via `dynamicUnpack`
|
|
159
|
-
// and then need to show the result as a string.
|
|
160
|
-
function debugFormatValue(ctx: DynamicCtx, fieldPath: string, ty: Ty, value: any): string {
|
|
161
|
-
// unlike `debugFormat()`, here we don't read `ty` from a stack: we already have `value` of type `ty`;
|
|
162
|
-
// to visualize it, instead of making a giant switch-case for all types, do the following trick:
|
|
163
|
-
// pack value back to a tuple, and render this tuple as if it's a stack value
|
|
164
|
-
const backToTuple = makeTvmTupleDynamic(ctx, ty, value);
|
|
165
|
-
return debugFormat(ctx, new StackReader(backToTuple), fieldPath, ty);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// `unknown` is a raw TVM tuple item; `tuple`, by definition, is `array<unknown>`,
|
|
169
|
-
// it has no compile-time type information, so visualize it anyhow
|
|
170
|
-
function debugPrintUnknown(item: c.TupleItem): string {
|
|
171
|
-
switch (item.type) {
|
|
172
|
-
case 'int': return `${item.value}`;
|
|
173
|
-
case 'cell': return `cell{${printRawCellContents(item.cell)}}`;
|
|
174
|
-
case 'slice': return `slice{${printRawCellContents(item.cell)}}`;
|
|
175
|
-
case 'builder': return `builder{${printRawCellContents(item.cell)}}`;
|
|
176
|
-
case 'null': return 'null';
|
|
177
|
-
case 'tuple': return `(${item.items.map(debugPrintUnknown).join(', ')})`;
|
|
178
|
-
case 'nan': return 'NaN';
|
|
179
|
-
default: return `<unknown>`;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Read a value of type `ty` from the TVM stack `tuple` and return a human-readable string.
|
|
186
|
-
* Consumes `calcWidthOnStack(ty)` elements from the beginning of the array.
|
|
187
|
-
* Example: given `Point { x: int, y: int }` and stack `[10n, 20n]`, returns "Point { x: 10, y: 20 }".
|
|
188
|
-
*/
|
|
189
|
-
export function debugPrintFromStack(ctx: DynamicCtx, r: StackReader, ty: Ty): string {
|
|
190
|
-
return debugFormat(ctx, r, 'self', ty);
|
|
191
|
-
}
|