This repository has been archived by the owner on Jun 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
resolve.ts
240 lines (211 loc) · 6.69 KB
/
resolve.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
import debug from "debug";
import { CXTypeKind, Type } from "libclang-bindings";
import { StringBuilder } from "../string-builder";
import { ITypeNameResolver, LineEndings } from "../types";
import { resolveName, simpleDesugar } from "../util";
/**
* log for resolving ref types
*/
const resolveLog = debug("clangffi:tsgen:resolve");
/**
* Resolver that matches native types with their typescript types
*/
export class TSResolver implements ITypeNameResolver {
// TODO(bengreenier): ensure this list is exhaustive
lookup = {
void: "void",
uint8_t: "number",
int: "number",
"unsigned int": "number",
int8_t: "number",
uint16_t: "number",
int16_t: "number",
uint32_t: "number",
int32_t: "number",
uint64_t: "number",
float: "number",
double: "number",
bool: "boolean",
byte: "number",
char: "number",
"char*": "string",
short: "number",
long: "number",
time_t: "number",
size_t: "number",
"long long": "number",
"unsigned short": "number",
"unsigned long": "number",
"unsigned long long": "number",
};
createPointer(str: string): string {
return `TypedPointer<${str}>`;
}
createArray(str: string): string {
return `TypedArray<${str}>`;
}
createEnum(str: string): string {
return str;
}
createStruct(str: string): string {
return str;
}
createFunction(str: string): string {
// TODO(bengreenier): this is a dirty hack that we should probably rethink
// we are parsing out the ref function format (e.g. `ret, [param1, param2, etc]`)
// to create a typescript equivalent function type
const parts = str.split(",");
const ret = parts[0];
const remainder = parts.slice(1).join(",");
const params = remainder.substring(
remainder.indexOf("[") + 1,
remainder.lastIndexOf("]")
);
// ts needs names before types so generate that signature
const sig = params
.split(",")
.map((p, i) => `arg${i}: ${p}`.trim())
.join(", ");
return `(${sig}) => ${ret}`;
}
createUnknown(str: string): string {
return str;
}
}
/**
* Resolver that matches native types with their `ref` types
*/
export class RefResolver implements ITypeNameResolver {
// TODO(bengreenier): ensure this list is exhaustive
lookup = {
void: "ref.types.void",
uint8_t: "ref.types.uint8",
int: "ref.types.int",
"unsigned int": "ref.types.uint",
int8_t: "ref.types.int8",
uint16_t: "ref.types.uint16",
int16_t: "ref.types.int16",
uint32_t: "ref.types.uint32",
int32_t: "ref.types.int32",
uint64_t: "ref.types.int64",
float: "ref.types.float",
double: "ref.types.double",
bool: "ref.types.bool",
byte: "ref.types.byte",
char: "ref.types.char",
"char*": "ref.types.CString",
short: "ref.types.short",
long: "ref.types.long",
time_t: "ref.types.longlong",
size_t: "ref.types.ulonglong",
"long long": "ref.types.longlong",
"unsigned short": "ref.types.ushort",
"unsigned long": "ref.types.ulong",
"unsigned long long": "ref.types.ulonglong",
};
createPointer(str: string): string {
return `Pointer(${str})`;
}
createArray(str: string): string {
return `Array(${str})`;
}
createEnum(str: string): string {
return `${str}Def`;
}
createStruct(str: string): string {
return `${str}Def`;
}
createFunction(str: string): string {
return `ffi.Function(${str})`;
}
createUnknown(str: string): string {
return `${str}Def`;
}
}
/**
* Resolves a typescript ref type for a given clang type
* @param type clang type
* @param resolver the name resolver to use
*/
export function resolveType(type: Type, resolver: ITypeNameResolver): string {
const sb = new StringBuilder(LineEndings.LF);
// elaborated
if (!type.isCanonical && type.canonicalType) {
resolveLog(`${type.name} is elaborated, further resolving...`);
// just recurse
sb.append(resolveType(type.canonicalType, resolver));
}
// pointer
else if (type.isPointer && type.pointeeType) {
resolveLog(`${type.name} is pointer`);
const pointee = type.pointeeType;
if (pointee.isFunctionType) {
resolveLog(`${pointee.name} is function`);
// func protos are always pointers but we don't wrap them as such
sb.append(resolveType(pointee, resolver));
} else if (pointee.name.endsWith("char")) {
resolveLog(`${pointee.name} is char*`);
sb.append(resolver.lookup["char*"]);
} else {
resolveLog(`${pointee.name} is a pointer, further resolving..`);
sb.append(resolver.createPointer(`${resolveType(pointee, resolver)}`));
}
}
// const
else if (type.isLocalConstQualified && type.nonConstType) {
resolveLog(`${type.name} is const qualified, further resolving...`);
sb.append(resolveType(type.nonConstType, resolver));
}
// enum
else if (type.isEnumType) {
resolveLog(`${type.name} is enum`);
// sometimes the typename has "enum" sugar before it, we want to pull that off
var typeStr = type.name.startsWith("enum ")
? type.name.substring("enum ".length)
: type.name;
sb.append(resolver.createEnum(typeStr));
}
// struct (not just all records, only those that are structs)
else if (type.isRecordType && type.name.startsWith("struct ")) {
resolveLog(`${type.name} is struct`);
// here we pull off the struct sugar before creating the resolved type
sb.append(resolver.createStruct(type.name.substring("struct ".length)));
}
// array
else if (type.isArrayType && type.elementType) {
resolveLog(`${type.name} is array`);
sb.append(
resolver.createArray(
`${resolveType(type.elementType, resolver)}, ${type.arraySize}`
)
);
}
// function
else if (type.isFunctionType && type.paramTypes && type.returnType) {
resolveLog(`${type.name} is fn`);
// resolve all the params first
const params = type.paramTypes
.map((p) => resolveType(p, resolver))
.join(", ");
resolveLog(`${type.name} resolved params`);
sb.append(
resolver.createFunction(
`${resolveType(type.returnType, resolver)}, [${params}]`
)
);
}
// built in
else if (resolver.lookup[type.name]) {
resolveLog(`${type.name} is builtin`);
sb.append(resolver.lookup[type.name]);
}
// unknown
else {
resolveLog(`${type.name} is unknown`);
sb.append(resolver.createUnknown(simpleDesugar(type)));
}
const res = sb.toString();
resolveLog(`${type.name} resolved to '${res}'`);
// done!
return res;
}