gen-typescript-from-tolk-dev 0.2.3 → 0.3.0

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.
@@ -1,16 +1,113 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SymTable = void 0;
3
4
  exports.renderTy = renderTy;
4
5
  exports.createLabelsForUnion = createLabelsForUnion;
5
- exports.instantiateGenerics = instantiateGenerics;
6
6
  exports.calcWidthOnStack = calcWidthOnStack;
7
+ const unsupported_errors_1 = require("./unsupported-errors");
7
8
  /*
8
9
  Contains basic functions working with ABI types,
9
10
  which are used both for generating wrappers and dynamic serialization.
10
- They do not emit TypeScript expressions and do not depend on CodegenCtx and DynamicCtx.
11
11
  */
12
+ // SymTable is an index of ABI symbols: top-level declarations by name and unique types by index.
13
+ class SymTable {
14
+ constructor(declarations, uniqueTypes, structInstantiations, aliasInstantiations) {
15
+ this.structs = new Map();
16
+ this.aliases = new Map();
17
+ this.enums = new Map();
18
+ this.uniqueTypes = uniqueTypes;
19
+ this.structInstantiations = structInstantiations;
20
+ this.aliasInstantiations = aliasInstantiations;
21
+ for (const n of declarations) {
22
+ if (n.kind === 'struct')
23
+ this.structs.set(n.name, n);
24
+ else if (n.kind === 'alias')
25
+ this.aliases.set(n.name, n);
26
+ else if (n.kind === 'enum')
27
+ this.enums.set(n.name, n);
28
+ }
29
+ }
30
+ getStruct(structName) {
31
+ const s = this.structs.get(structName);
32
+ if (s === undefined)
33
+ throw new unsupported_errors_1.SymbolNotFound('struct', structName);
34
+ return s;
35
+ }
36
+ getAlias(aliasName) {
37
+ const a = this.aliases.get(aliasName);
38
+ if (a === undefined)
39
+ throw new unsupported_errors_1.SymbolNotFound('alias', aliasName);
40
+ return a;
41
+ }
42
+ getEnum(enumName) {
43
+ const e = this.enums.get(enumName);
44
+ if (e === undefined)
45
+ throw new unsupported_errors_1.SymbolNotFound('enum', enumName);
46
+ return e;
47
+ }
48
+ tyByIdx(tyIdx) {
49
+ const ty = this.uniqueTypes[tyIdx];
50
+ if (ty === undefined)
51
+ throw new unsupported_errors_1.SymbolNotFound('ty_idx', tyIdx.toString());
52
+ return ty;
53
+ }
54
+ // Get fields of a struct, `tyIdx` points to "StructRef".
55
+ // Simple (non-generic) structs, e.g. `Storage`:
56
+ // - just return its fields, they all are also non-generic
57
+ // Generic instantiations, e.g. `Wrapper<int32>`:
58
+ // - override original ty_idx (potentially generic) from monomorphic instantiations (with Ts substitutes)
59
+ // - save original ty_idx as uLabelTyIdx in case of generic unions later
60
+ structFieldsOf(tyIdx) {
61
+ const ty = this.tyByIdx(tyIdx);
62
+ if (ty.kind !== 'StructRef') {
63
+ throw new Error(`expected StructRef at ty_idx=${tyIdx}`);
64
+ }
65
+ let fields = this.getStruct(ty.struct_name).fields;
66
+ let instantiation = this.structInstantiations.find(s => s.ty_idx === tyIdx);
67
+ if (instantiation) {
68
+ if (fields.length !== instantiation.monomorphic_fields_ty_idx.length) {
69
+ throw new Error(`mismatch monomorphic fields size for '${instantiation.struct_name}'`);
70
+ }
71
+ fields = fields.map((f, i) => ({
72
+ ...f,
73
+ ty_idx: instantiation.monomorphic_fields_ty_idx[i],
74
+ uLabelTyIdx: f.ty_idx,
75
+ }));
76
+ }
77
+ return fields;
78
+ }
79
+ // Get target of an alias, `tyIdx` points to "AliasRef".
80
+ // Simple (non-generic) aliases, e.g. `AllowedMessages`:
81
+ // - just return its target
82
+ // Generic instantiations, e.g. `Either<int8, int16>`:
83
+ // - override target_ty_idx (potentially generic) from monomorphic instantiations (with Ts substitutes)
84
+ // - save original target_ty_idx as uLabelTyIdx in case of generic unions later
85
+ aliasTargetOf(tyIdx) {
86
+ const ty = this.tyByIdx(tyIdx);
87
+ if (ty.kind !== 'AliasRef') {
88
+ throw new Error(`expected AliasRef at ty_idx=${tyIdx}`);
89
+ }
90
+ let target = {
91
+ ty_idx: this.getAlias(ty.alias_name).target_ty_idx,
92
+ };
93
+ let instantiation = this.aliasInstantiations.find(a => a.ty_idx === tyIdx);
94
+ if (instantiation) {
95
+ target = {
96
+ ty_idx: instantiation.monomorphic_target_ty_idx,
97
+ uLabelTyIdx: target.ty_idx,
98
+ };
99
+ }
100
+ return target;
101
+ }
102
+ }
103
+ exports.SymTable = SymTable;
104
+ // Render `<T>` if a struct/alias is generic.
105
+ function renderTypeArgs(symbols, typeArgsTyIdx) {
106
+ return typeArgsTyIdx ? `<${typeArgsTyIdx.map(ty_idx => renderTy(symbols, ty_idx)).join(', ')}>` : '';
107
+ }
12
108
  // Render intermediate representation back to a valid Tolk type, exactly as it was in original sources.
13
- function renderTy(ty) {
109
+ function renderTy(symbols, tyIdx) {
110
+ const ty = symbols.tyByIdx(tyIdx);
14
111
  switch (ty.kind) {
15
112
  case 'int': return `int`;
16
113
  case 'intN': return `int${ty.n}`;
@@ -33,24 +130,25 @@ function renderTy(ty) {
33
130
  case 'callable': return `continuation`;
34
131
  case 'void': return `void`;
35
132
  case 'unknown': return `unknown`;
36
- case 'nullable': return `${renderTy(ty.inner)}?`;
37
- case 'cellOf': return `Cell<${renderTy(ty.inner)}>`;
38
- case 'arrayOf': return `array<${renderTy(ty.inner)}>`;
39
- case 'lispListOf': return `lisp_list<${renderTy(ty.inner)}>`;
40
- case 'tensor': return `(${ty.items.map(renderTy).join(', ')})`;
41
- case 'shapedTuple': return `[${ty.items.map(renderTy).join(', ')}]`;
42
- case 'mapKV': return `map<${renderTy(ty.k)}, ${renderTy(ty.v)}>`;
133
+ case 'nullable': return `${renderTy(symbols, ty.inner_ty_idx)}?`;
134
+ case 'cellOf': return `Cell<${renderTy(symbols, ty.inner_ty_idx)}>`;
135
+ case 'arrayOf': return `array<${renderTy(symbols, ty.inner_ty_idx)}>`;
136
+ case 'lispListOf': return `lisp_list<${renderTy(symbols, ty.inner_ty_idx)}>`;
137
+ case 'tensor': return `(${ty.items_ty_idx.map(ty_idx => renderTy(symbols, ty_idx)).join(', ')})`;
138
+ case 'shapedTuple': return `[${ty.items_ty_idx.map(ty_idx => renderTy(symbols, ty_idx)).join(', ')}]`;
139
+ case 'mapKV': return `map<${renderTy(symbols, ty.key_ty_idx)}, ${renderTy(symbols, ty.value_ty_idx)}>`;
43
140
  case 'EnumRef': return ty.enum_name;
44
- case 'StructRef': return ty.struct_name + (ty.type_args ? `<${ty.type_args.map(renderTy).join(', ')}>` : '');
45
- case 'AliasRef': return ty.alias_name + (ty.type_args ? `<${ty.type_args.map(renderTy).join(', ')}>` : '');
141
+ case 'StructRef': return ty.struct_name + renderTypeArgs(symbols, ty.type_args_ty_idx);
142
+ case 'AliasRef': return ty.alias_name + renderTypeArgs(symbols, ty.type_args_ty_idx);
46
143
  case 'genericT': return ty.name_t;
47
- case 'union': return ty.variants.map(v => renderTy(v.variant_ty)).join(' | ');
144
+ case 'union': return ty.variants.map(v => renderTy(symbols, v.variant_ty_idx)).join(' | ');
48
145
  }
49
146
  }
50
147
  // A "label" is the `$` field in TypeScript output, used to differentiate structs and union variants.
51
148
  // Tolk `int32 | int64` -> TS `{ $: 'int32', value: bigint } | { $: 'int64', value: bigint }`.
52
149
  // This function creates a string for `$`.
53
- function createLabel(symbols, ty) {
150
+ function createLabel(symbols, tyIdx) {
151
+ const ty = symbols.tyByIdx(tyIdx);
54
152
  switch (ty.kind) {
55
153
  case 'int': return `int`;
56
154
  case 'intN': return `int${ty.n}`;
@@ -73,7 +171,7 @@ function createLabel(symbols, ty) {
73
171
  case 'callable': return `callable`;
74
172
  case 'void': return `void`;
75
173
  case 'unknown': return `unknown`;
76
- case 'nullable': return `${createLabel(symbols, ty.inner)}?`;
174
+ case 'nullable': return `${createLabel(symbols, ty.inner_ty_idx)}?`;
77
175
  case 'cellOf': return `Cell`;
78
176
  case 'arrayOf': return `array`;
79
177
  case 'lispListOf': return `lisp_list`;
@@ -82,85 +180,70 @@ function createLabel(symbols, ty) {
82
180
  case 'mapKV': return `map`;
83
181
  case 'EnumRef': return ty.enum_name;
84
182
  case 'StructRef': return ty.struct_name;
85
- case 'AliasRef': return createLabel(symbols, symbols.getAliasTarget(ty.alias_name));
183
+ case 'AliasRef': return createLabel(symbols, symbols.aliasTargetOf(tyIdx).ty_idx);
86
184
  case 'genericT': return ty.name_t;
87
- case 'union': return ty.variants.map(v => createLabel(symbols, v.variant_ty)).join('|');
185
+ case 'union': return ty.variants.map(v => createLabel(symbols, v.variant_ty_idx)).join('|');
88
186
  }
89
187
  }
90
188
  // Every Tolk struct is generated as TS `interface` with `$` field.
91
189
  // Tolk `struct A { ... }` -> TS `interface A { $: 'A', ... }`.
92
190
  // So, Tolk `A | B` is represented as TS `A | B` (unlike primitives),
93
191
  // because every struct has its own label.
94
- function isStructWithItsOwnLabel(symbols, ty) {
192
+ function isStructWithItsOwnLabel(symbols, tyIdx) {
193
+ const ty = symbols.tyByIdx(tyIdx);
95
194
  if (ty.kind === 'StructRef')
96
195
  return true;
97
196
  if (ty.kind === 'AliasRef')
98
- return isStructWithItsOwnLabel(symbols, symbols.getAliasTarget(ty.alias_name));
197
+ return isStructWithItsOwnLabel(symbols, symbols.aliasTargetOf(tyIdx).ty_idx);
99
198
  return false;
100
199
  }
101
200
  // Given a union `T1 | T2 | ...`, calculate a TypeScript representation:
102
201
  // whether each variant should be `$ + value` or not, and what string does `$` hold.
103
- function createLabelsForUnion(symbols, variants) {
104
- // in all practical cases, `T_i` is a structure or a primitive;
105
- // we try to shorten a label (`createLabel()` is a "simple string of a type"):
202
+ // Parameter `uLabelTyIdx` is set in case of generic instantiations, see a comment inside.
203
+ function createLabelsForUnion(symbols, variants, uLabelTyIdx) {
204
+ // About uLabelTyIdx:
205
+ // For example, original (generic) is `struct Or<T1, T2> { uni: T1 | T2 }`.
206
+ // We have `Or<int32, int64>` with instantiated variants = [ { variant_ty_idx: int32 }, { variant_ty_idx: int64 } ].
207
+ // But for labels we want not { $: 'int32' } and { $: 'int64' }, but original { $: 'T1' } and { $: 'T2' }.
208
+ // In this case, uLabelTyIdx points to original (generic) union `T1 | T2`, we have labels from there.
209
+ let genericVariantsTyIdx;
210
+ if (uLabelTyIdx !== undefined) {
211
+ const labelTy = symbols.tyByIdx(uLabelTyIdx);
212
+ if (labelTy.kind === 'union' && labelTy.variants.length === variants.length) {
213
+ genericVariantsTyIdx = labelTy.variants.map(v => v.variant_ty_idx);
214
+ }
215
+ }
216
+ // In all practical cases, `T_i` is a structure or a primitive.
217
+ // We try to shorten a label (`createLabel()` is a "simple string of a type"):
106
218
  // - for a generic struct, use `Wrapper`, not `Wrapper<xxx>`
107
219
  // - for `Cell<...>`, use a shorthand `Cell`
108
220
  // - etc.
109
- // but if it results in a duplicate (e.g. `Wrapper<int32> | Wrapper<int64>` => ['Wrapper','Wrapper']),
221
+ // But if it results in a duplicate (e.g. `Wrapper<int32> | Wrapper<int64>` => ['Wrapper','Wrapper']),
110
222
  // then full-string Tolk types are used, and `value` is always required:
111
223
  // `type U = { $: 'Wrapper<int32>', value: Wrapper<int32> } | ...`
112
224
  let unique = new Set();
113
225
  let hasDuplicates = false;
114
- variants.forEach(variant => {
115
- let labelStr = createLabel(symbols, variant.variant_ty);
226
+ variants.forEach((variant, i) => {
227
+ let labelStr = createLabel(symbols, genericVariantsTyIdx?.[i] ?? variant.variant_ty_idx);
116
228
  hasDuplicates || (hasDuplicates = unique.has(labelStr));
117
229
  unique.add(labelStr);
118
230
  });
119
- return variants.map(variant => {
120
- let variantTy = variant.variant_ty;
121
- if (variantTy.kind === 'nullLiteral') {
231
+ return variants.map((variant, i) => {
232
+ const labelTyIdx = genericVariantsTyIdx?.[i] ?? variant.variant_ty_idx;
233
+ const labelTy = symbols.tyByIdx(labelTyIdx);
234
+ if (labelTy.kind === 'nullLiteral') {
122
235
  return { ...variant, labelStr: '', hasValueField: false };
123
236
  }
124
237
  return {
125
238
  ...variant,
126
- labelStr: hasDuplicates ? renderTy(variantTy) : createLabel(symbols, variantTy),
127
- hasValueField: hasDuplicates ? true : !isStructWithItsOwnLabel(symbols, variantTy)
239
+ labelStr: hasDuplicates ? renderTy(symbols, labelTyIdx) : createLabel(symbols, labelTyIdx),
240
+ hasValueField: hasDuplicates ? true : !isStructWithItsOwnLabel(symbols, labelTyIdx)
128
241
  };
129
242
  });
130
243
  }
131
- // Replace all generic Ts (typeParams) with instantiation (typeArgs) recursively.
132
- // Example: `(int, T, Wrapper<T?>)` and T=coins => `(int, coins, Wrapper<coins?>)`
133
- function instantiateGenerics(ty, typeParams, typeArgs) {
134
- if (ty.kind === 'nullable')
135
- return { ...ty, inner: instantiateGenerics(ty.inner, typeParams, typeArgs) };
136
- if (ty.kind === 'cellOf')
137
- return { ...ty, inner: instantiateGenerics(ty.inner, typeParams, typeArgs) };
138
- if (ty.kind === 'arrayOf')
139
- return { ...ty, inner: instantiateGenerics(ty.inner, typeParams, typeArgs) };
140
- if (ty.kind === 'lispListOf')
141
- return { ...ty, inner: instantiateGenerics(ty.inner, typeParams, typeArgs) };
142
- if (ty.kind === 'tensor')
143
- return { ...ty, items: ty.items.map(item => instantiateGenerics(item, typeParams, typeArgs)) };
144
- if (ty.kind === 'shapedTuple')
145
- return { ...ty, items: ty.items.map(item => instantiateGenerics(item, typeParams, typeArgs)) };
146
- if (ty.kind === 'mapKV')
147
- return { ...ty, k: instantiateGenerics(ty.k, typeParams, typeArgs), v: instantiateGenerics(ty.v, typeParams, typeArgs) };
148
- if (ty.kind === 'StructRef')
149
- return { ...ty, type_args: ty.type_args?.map(ta => instantiateGenerics(ta, typeParams, typeArgs)) };
150
- if (ty.kind === 'AliasRef')
151
- return { ...ty, type_args: ty.type_args?.map(ta => instantiateGenerics(ta, typeParams, typeArgs)) };
152
- if (ty.kind === 'union')
153
- return { ...ty, variants: ty.variants.map(v => ({ ...v, variant_ty: instantiateGenerics(v.variant_ty, typeParams, typeArgs) })) };
154
- if (ty.kind === 'genericT') {
155
- const idx = typeParams?.indexOf(ty.name_t);
156
- if (idx === undefined || idx === -1 || idx >= typeArgs.length)
157
- throw new Error(`inconsistent generics: could not find type argument for ${ty.name_t}`);
158
- return typeArgs[idx];
159
- }
160
- return ty;
161
- }
162
244
  // Calculate, how many stack slots a type occupies.
163
- function calcWidthOnStack(symbols, ty) {
245
+ function calcWidthOnStack(symbols, tyIdx) {
246
+ const ty = symbols.tyByIdx(tyIdx);
164
247
  switch (ty.kind) {
165
248
  case 'void': {
166
249
  // void is like "unit", equal to an empty tensor
@@ -168,24 +251,17 @@ function calcWidthOnStack(symbols, ty) {
168
251
  }
169
252
  case 'tensor': {
170
253
  // a tensor is a sum of its elements
171
- return ty.items.map(item => calcWidthOnStack(symbols, item)).reduce((p, c) => p + c, 0);
254
+ return ty.items_ty_idx.map(ty_idx => calcWidthOnStack(symbols, ty_idx)).reduce((p, c) => p + c, 0);
172
255
  }
173
256
  case 'StructRef': {
174
257
  // a struct is a named tensor: fields one by one;
175
- // if a struct is generic `Wrapper<T>`, we have typeArgs T=xxx, and replace T in each field
176
- // (it works unless `T` is used in unions)
177
- const structRef = symbols.getStruct(ty.struct_name);
178
- return structRef.fields.map(f => {
179
- const fTy = ty.type_args ? instantiateGenerics(f.ty, structRef.type_params, ty.type_args) : f.ty;
180
- return calcWidthOnStack(symbols, fTy);
181
- }).reduce((p, c) => p + c, 0);
258
+ const fields = symbols.structFieldsOf(tyIdx);
259
+ return fields.map(f => calcWidthOnStack(symbols, f.ty_idx)).reduce((p, c) => p + c, 0);
182
260
  }
183
261
  case 'AliasRef': {
184
262
  // an alias is the same as its underlying (target) type;
185
- // if an alias is generic `Maybe<T>`, we have typeArgs T=xxx, and replace T in its target
186
- const aliasRef = symbols.getAlias(ty.alias_name);
187
- const targetTy = ty.type_args ? instantiateGenerics(aliasRef.target_ty, aliasRef.type_params, ty.type_args) : aliasRef.target_ty;
188
- return calcWidthOnStack(symbols, targetTy);
263
+ const target = symbols.aliasTargetOf(tyIdx);
264
+ return calcWidthOnStack(symbols, target.ty_idx);
189
265
  }
190
266
  case 'nullable': {
191
267
  // for primitive nullables (common case), like `int?` and `address?`, it's 1 (TVM value or NULL);
@@ -193,7 +269,10 @@ function calcWidthOnStack(symbols, ty) {
193
269
  return ty.stack_width ?? 1;
194
270
  }
195
271
  case 'union': {
196
- // for union types, the compiler always inserts stackWidth for simplicity (and stackTypeId for each variant)
272
+ // for union types, the compiler always inserts stackWidth for non-generic concrete forms
273
+ if (ty.stack_width === undefined) {
274
+ throw new Error(`unexpected stack_width=undefined at ty_idx=${tyIdx}`);
275
+ }
197
276
  return ty.stack_width;
198
277
  }
199
278
  case 'genericT': {
@@ -1,4 +1,3 @@
1
- import type { Ty } from './abi-types';
2
1
  export declare class SymbolNotFound extends Error {
3
2
  constructor(symbolKind: string, notFoundName: string);
4
3
  }
@@ -9,7 +8,7 @@ export declare class NonStandardDictKey extends Error {
9
8
  constructor(message: string);
10
9
  }
11
10
  export declare class NotSupportedTypeOnStack extends Error {
12
- constructor(ty: Ty, fieldPath: string);
11
+ constructor(tyRendered: string, fieldPath: string);
13
12
  }
14
13
  export declare class CantGenerateWrappersAtAll extends Error {
15
14
  constructor(where: string, prevEx: any);
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CantCallGetMethodDynamic = exports.CantUnpackDynamic = exports.CantPackDynamic = exports.InvalidDynamicInput = exports.CantGenerateWrappersAtAll = exports.NotSupportedTypeOnStack = exports.NonStandardDictKey = exports.CantGeneratePackUnpack = exports.SymbolNotFound = void 0;
4
- const types_kernel_1 = require("./types-kernel");
5
4
  // Fired when TyRef contains unresolved symbols,
6
5
  // e.g. { kind: 'StructRef', struct_name: 'non-existing' }
7
6
  class SymbolNotFound extends Error {
@@ -30,8 +29,8 @@ class NonStandardDictKey extends Error {
30
29
  exports.NonStandardDictKey = NonStandardDictKey;
31
30
  // Some types, like callables/lambdas, can not be used in get methods, we can't generate wrappers then.
32
31
  class NotSupportedTypeOnStack extends Error {
33
- constructor(ty, fieldPath) {
34
- super(`'${fieldPath}' can not be used in get methods, because it contains '${(0, types_kernel_1.renderTy)(ty)}'`);
32
+ constructor(tyRendered, fieldPath) {
33
+ super(`'${fieldPath}' can not be used in get methods, because it contains '${tyRendered}'`);
35
34
  }
36
35
  }
37
36
  exports.NotSupportedTypeOnStack = NotSupportedTypeOnStack;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gen-typescript-from-tolk-dev",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "Generate TypeScript wrappers from Tolk ABI",
5
5
  "author": "TON Core",
6
6
  "license": "MIT",